2017-05-16 18:12:24 +03:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "pkcs11.h"
|
|
|
|
#include "pkcs11uri.h"
|
|
|
|
#include "plarena.h"
|
|
|
|
#include "prprf.h"
|
|
|
|
#include "secport.h"
|
|
|
|
|
|
|
|
/* Character sets used in the ABNF rules in RFC7512. */
|
|
|
|
#define PK11URI_DIGIT "0123456789"
|
|
|
|
#define PK11URI_ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
#define PK11URI_HEXDIG PK11URI_DIGIT "abcdefABCDEF"
|
|
|
|
#define PK11URI_UNRESERVED PK11URI_ALPHA PK11URI_DIGIT "-._~"
|
|
|
|
#define PK11URI_RES_AVAIL ":[]@!$'()*+,="
|
|
|
|
#define PK11URI_PATH_RES_AVAIL PK11URI_RES_AVAIL "&"
|
|
|
|
#define PK11URI_QUERY_RES_AVAIL PK11URI_RES_AVAIL "/?|"
|
|
|
|
#define PK11URI_ATTR_NM_CHAR PK11URI_ALPHA PK11URI_DIGIT "-_"
|
|
|
|
#define PK11URI_PCHAR PK11URI_UNRESERVED PK11URI_PATH_RES_AVAIL
|
|
|
|
#define PK11URI_QCHAR PK11URI_UNRESERVED PK11URI_QUERY_RES_AVAIL
|
|
|
|
|
|
|
|
/* Path attributes defined in RFC7512. */
|
|
|
|
static const char *pattr_names[] = {
|
|
|
|
PK11URI_PATTR_TOKEN,
|
|
|
|
PK11URI_PATTR_MANUFACTURER,
|
|
|
|
PK11URI_PATTR_SERIAL,
|
|
|
|
PK11URI_PATTR_MODEL,
|
|
|
|
PK11URI_PATTR_LIBRARY_MANUFACTURER,
|
|
|
|
PK11URI_PATTR_LIBRARY_DESCRIPTION,
|
|
|
|
PK11URI_PATTR_LIBRARY_VERSION,
|
|
|
|
PK11URI_PATTR_OBJECT,
|
|
|
|
PK11URI_PATTR_TYPE,
|
|
|
|
PK11URI_PATTR_ID,
|
|
|
|
PK11URI_PATTR_SLOT_MANUFACTURER,
|
|
|
|
PK11URI_PATTR_SLOT_DESCRIPTION,
|
|
|
|
PK11URI_PATTR_SLOT_ID
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Query attributes defined in RFC7512. */
|
|
|
|
static const char *qattr_names[] = {
|
|
|
|
PK11URI_QATTR_PIN_SOURCE,
|
|
|
|
PK11URI_QATTR_PIN_VALUE,
|
|
|
|
PK11URI_QATTR_MODULE_NAME,
|
|
|
|
PK11URI_QATTR_MODULE_PATH
|
|
|
|
};
|
|
|
|
|
|
|
|
struct PK11URIBufferStr {
|
|
|
|
PLArenaPool *arena;
|
|
|
|
char *data;
|
|
|
|
size_t size;
|
|
|
|
size_t allocated;
|
|
|
|
};
|
|
|
|
typedef struct PK11URIBufferStr PK11URIBuffer;
|
|
|
|
|
|
|
|
struct PK11URIAttributeListEntryStr {
|
|
|
|
char *name;
|
|
|
|
char *value;
|
|
|
|
};
|
|
|
|
typedef struct PK11URIAttributeListEntryStr PK11URIAttributeListEntry;
|
|
|
|
|
|
|
|
struct PK11URIAttributeListStr {
|
|
|
|
PLArenaPool *arena;
|
|
|
|
PK11URIAttributeListEntry *attrs;
|
|
|
|
size_t num_attrs;
|
|
|
|
};
|
|
|
|
typedef struct PK11URIAttributeListStr PK11URIAttributeList;
|
|
|
|
|
|
|
|
struct PK11URIStr {
|
|
|
|
PLArenaPool *arena;
|
|
|
|
|
|
|
|
PK11URIAttributeList pattrs;
|
|
|
|
PK11URIAttributeList vpattrs;
|
|
|
|
|
|
|
|
PK11URIAttributeList qattrs;
|
|
|
|
PK11URIAttributeList vqattrs;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define PK11URI_ARENA_SIZE 1024
|
|
|
|
|
|
|
|
typedef int (*PK11URIAttributeCompareNameFunc)(const char *a, const char *b);
|
|
|
|
|
|
|
|
/* This belongs in secport.h */
|
|
|
|
#define PORT_ArenaGrowArray(poolp, oldptr, type, oldnum, newnum) \
|
|
|
|
(type *)PORT_ArenaGrow((poolp), (oldptr), \
|
|
|
|
(oldnum) * sizeof(type), (newnum) * sizeof(type))
|
|
|
|
#define PORT_ReallocArray(oldptr, type, newnum) \
|
|
|
|
(type *)PORT_Realloc((oldptr), (newnum) * sizeof(type))
|
|
|
|
|
|
|
|
/* Functions for resizable buffer. */
|
|
|
|
static SECStatus
|
|
|
|
pk11uri_AppendBuffer(PK11URIBuffer *buffer, const unsigned char *data,
|
|
|
|
size_t size)
|
|
|
|
{
|
|
|
|
/* Check overflow. */
|
|
|
|
if (buffer->size + size < buffer->size)
|
|
|
|
return SECFailure;
|
|
|
|
|
|
|
|
if (buffer->size + size > buffer->allocated) {
|
|
|
|
size_t allocated = buffer->allocated * 2 + size;
|
|
|
|
if (allocated < buffer->allocated)
|
|
|
|
return SECFailure;
|
|
|
|
if (buffer->arena)
|
|
|
|
buffer->data = PORT_ArenaGrow(buffer->arena, buffer->data,
|
|
|
|
buffer->allocated, allocated);
|
|
|
|
else
|
|
|
|
buffer->data = PORT_Realloc(buffer->data, allocated);
|
|
|
|
if (buffer->data == NULL)
|
|
|
|
return SECFailure;
|
|
|
|
buffer->allocated = allocated;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&buffer->data[buffer->size], data, size);
|
|
|
|
buffer->size += size;
|
|
|
|
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pk11uri_InitBuffer(PK11URIBuffer *buffer, PLArenaPool *arena)
|
|
|
|
{
|
|
|
|
memset(buffer, 0, sizeof(PK11URIBuffer));
|
|
|
|
buffer->arena = arena;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pk11uri_DestroyBuffer(PK11URIBuffer *buffer)
|
|
|
|
{
|
|
|
|
if (buffer->arena == NULL) {
|
|
|
|
PORT_Free(buffer->data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* URI encoding functions. */
|
|
|
|
static char *
|
|
|
|
pk11uri_Escape(PLArenaPool *arena, const char *value, size_t length,
|
|
|
|
const char *available)
|
|
|
|
{
|
|
|
|
PK11URIBuffer buffer;
|
|
|
|
const char *p;
|
|
|
|
unsigned char buf[4];
|
|
|
|
char *result = NULL;
|
|
|
|
SECStatus ret;
|
|
|
|
|
|
|
|
pk11uri_InitBuffer(&buffer, arena);
|
|
|
|
|
|
|
|
for (p = value; p < value + length; p++) {
|
|
|
|
if (strchr(available, *p) == NULL) {
|
|
|
|
if (PR_snprintf((char *)buf, sizeof(buf), "%%%02X", *p) == (PRUint32)-1) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
ret = pk11uri_AppendBuffer(&buffer, buf, 3);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret = pk11uri_AppendBuffer(&buffer, (const unsigned char *)p, 1);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buf[0] = '\0';
|
|
|
|
ret = pk11uri_AppendBuffer(&buffer, buf, 1);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Steal the memory allocated in buffer. */
|
|
|
|
result = buffer.data;
|
|
|
|
buffer.data = NULL;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
pk11uri_DestroyBuffer(&buffer);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
pk11uri_Unescape(PLArenaPool *arena, const char *value, size_t length)
|
|
|
|
{
|
|
|
|
PK11URIBuffer buffer;
|
|
|
|
const char *p;
|
|
|
|
unsigned char buf[1];
|
|
|
|
char *result = NULL;
|
|
|
|
SECStatus ret;
|
|
|
|
|
|
|
|
pk11uri_InitBuffer(&buffer, arena);
|
|
|
|
|
|
|
|
for (p = value; p < value + length; p++) {
|
|
|
|
if (*p == '%') {
|
|
|
|
int c;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
p++;
|
|
|
|
for (c = 0, i = 0; i < 2; i++) {
|
|
|
|
int h = *(p + i);
|
|
|
|
if ('0' <= h && h <= '9') {
|
|
|
|
c = (c << 4) | (h - '0');
|
|
|
|
} else if ('a' <= h && h <= 'f') {
|
|
|
|
c = (c << 4) | (h - 'a' + 10);
|
|
|
|
} else if ('A' <= h && h <= 'F') {
|
|
|
|
c = (c << 4) | (h - 'A' + 10);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i != 2) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
buf[0] = c;
|
|
|
|
} else {
|
|
|
|
buf[0] = *p;
|
|
|
|
}
|
|
|
|
ret = pk11uri_AppendBuffer(&buffer, buf, 1);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buf[0] = '\0';
|
|
|
|
ret = pk11uri_AppendBuffer(&buffer, buf, 1);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = buffer.data;
|
|
|
|
buffer.data = NULL;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
pk11uri_DestroyBuffer(&buffer);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Functions for manipulating attributes array. */
|
|
|
|
|
|
|
|
/* Compare two attribute names by the array index in attr_names. Both
|
|
|
|
* attribute names must be present in attr_names, otherwise it is a
|
|
|
|
* programming error. */
|
|
|
|
static int
|
|
|
|
pk11uri_CompareByPosition(const char *a, const char *b,
|
|
|
|
const char **attr_names, size_t num_attr_names)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < num_attr_names; i++) {
|
|
|
|
if (strcmp(a, attr_names[i]) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PR_ASSERT(i < num_attr_names);
|
|
|
|
|
|
|
|
for (j = 0; j < num_attr_names; j++) {
|
|
|
|
if (strcmp(b, attr_names[j]) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PR_ASSERT(j < num_attr_names);
|
|
|
|
|
|
|
|
return i - j;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Those pk11uri_Compare{Path,Query}AttributeName functions are used
|
|
|
|
* to reorder attributes when inserting. */
|
|
|
|
static int
|
|
|
|
pk11uri_ComparePathAttributeName(const char *a, const char *b)
|
|
|
|
{
|
|
|
|
return pk11uri_CompareByPosition(a, b, pattr_names, PR_ARRAY_SIZE(pattr_names));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pk11uri_CompareQueryAttributeName(const char *a, const char *b)
|
|
|
|
{
|
|
|
|
return pk11uri_CompareByPosition(a, b, qattr_names, PR_ARRAY_SIZE(qattr_names));
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
pk11uri_InsertToAttributeList(PK11URIAttributeList *attrs,
|
|
|
|
char *name, char *value,
|
|
|
|
PK11URIAttributeCompareNameFunc compare_name,
|
|
|
|
PRBool allow_duplicate)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (attrs->arena) {
|
|
|
|
attrs->attrs = PORT_ArenaGrowArray(attrs->arena, attrs->attrs,
|
|
|
|
PK11URIAttributeListEntry,
|
|
|
|
attrs->num_attrs,
|
|
|
|
attrs->num_attrs + 1);
|
|
|
|
} else {
|
|
|
|
attrs->attrs = PORT_ReallocArray(attrs->attrs,
|
|
|
|
PK11URIAttributeListEntry,
|
|
|
|
attrs->num_attrs + 1);
|
|
|
|
}
|
|
|
|
if (attrs->attrs == NULL) {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < attrs->num_attrs; i++) {
|
|
|
|
if (!allow_duplicate && strcmp(name, attrs->attrs[i].name) == 0) {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
if (compare_name(name, attrs->attrs[i].name) < 0) {
|
|
|
|
memmove(&attrs->attrs[i + 1], &attrs->attrs[i],
|
|
|
|
sizeof(PK11URIAttributeListEntry) * (attrs->num_attrs - i));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
attrs->attrs[i].name = name;
|
|
|
|
attrs->attrs[i].value = value;
|
|
|
|
|
|
|
|
attrs->num_attrs++;
|
|
|
|
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
pk11uri_InsertToAttributeListEscaped(PK11URIAttributeList *attrs,
|
|
|
|
const char *name, size_t name_size,
|
|
|
|
const char *value, size_t value_size,
|
|
|
|
PK11URIAttributeCompareNameFunc compare_name,
|
|
|
|
PRBool allow_duplicate)
|
|
|
|
{
|
|
|
|
char *name_copy = NULL, *value_copy = NULL;
|
|
|
|
SECStatus ret;
|
|
|
|
|
|
|
|
if (attrs->arena) {
|
|
|
|
name_copy = PORT_ArenaNewArray(attrs->arena, char, name_size + 1);
|
|
|
|
} else {
|
|
|
|
name_copy = PORT_Alloc(name_size + 1);
|
|
|
|
}
|
|
|
|
if (name_copy == NULL) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
memcpy(name_copy, name, name_size);
|
|
|
|
name_copy[name_size] = '\0';
|
|
|
|
|
|
|
|
value_copy = pk11uri_Unescape(attrs->arena, value, value_size);
|
|
|
|
if (value_copy == NULL) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pk11uri_InsertToAttributeList(attrs, name_copy, value_copy, compare_name,
|
|
|
|
allow_duplicate);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (attrs->arena == NULL) {
|
|
|
|
PORT_Free(name_copy);
|
|
|
|
PORT_Free(value_copy);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pk11uri_InitAttributeList(PK11URIAttributeList *attrs, PLArenaPool *arena)
|
|
|
|
{
|
|
|
|
memset(attrs, 0, sizeof(PK11URIAttributeList));
|
|
|
|
attrs->arena = arena;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pk11uri_DestroyAttributeList(PK11URIAttributeList *attrs)
|
|
|
|
{
|
|
|
|
if (attrs->arena == NULL) {
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < attrs->num_attrs; i++) {
|
|
|
|
PORT_Free(attrs->attrs[i].name);
|
|
|
|
PORT_Free(attrs->attrs[i].value);
|
|
|
|
}
|
|
|
|
PORT_Free(attrs->attrs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
pk11uri_AppendAttributeListToBuffer(PK11URIBuffer *buffer,
|
|
|
|
PK11URIAttributeList *attrs,
|
|
|
|
int separator,
|
|
|
|
const char *unescaped)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
SECStatus ret;
|
|
|
|
|
|
|
|
for (i = 0; i < attrs->num_attrs; i++) {
|
|
|
|
unsigned char sep[1];
|
|
|
|
char *escaped;
|
|
|
|
PK11URIAttributeListEntry *attr = &attrs->attrs[i];
|
|
|
|
|
|
|
|
if (i > 0) {
|
|
|
|
sep[0] = separator;
|
|
|
|
ret = pk11uri_AppendBuffer(buffer, sep, 1);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pk11uri_AppendBuffer(buffer, (unsigned char *)attr->name,
|
|
|
|
strlen(attr->name));
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
sep[0] = '=';
|
|
|
|
ret = pk11uri_AppendBuffer(buffer, sep, 1);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
escaped = pk11uri_Escape(buffer->arena, attr->value, strlen(attr->value),
|
|
|
|
unescaped);
|
|
|
|
if (escaped == NULL) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
ret = pk11uri_AppendBuffer(buffer, (unsigned char *)escaped,
|
|
|
|
strlen(escaped));
|
|
|
|
if (buffer->arena == NULL) {
|
|
|
|
PORT_Free(escaped);
|
|
|
|
}
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Creation of PK11URI object. */
|
|
|
|
static PK11URI *
|
|
|
|
pk11uri_AllocURI(void)
|
|
|
|
{
|
|
|
|
PLArenaPool *arena;
|
|
|
|
PK11URI *result;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(PK11URI_ARENA_SIZE);
|
|
|
|
if (arena == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = PORT_ArenaZAlloc(arena, sizeof(PK11URI));
|
|
|
|
if (result == NULL) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
result->arena = arena;
|
|
|
|
pk11uri_InitAttributeList(&result->pattrs, arena);
|
|
|
|
pk11uri_InitAttributeList(&result->vpattrs, arena);
|
|
|
|
pk11uri_InitAttributeList(&result->qattrs, arena);
|
|
|
|
pk11uri_InitAttributeList(&result->vqattrs, arena);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
pk11uri_InsertAttributes(PK11URIAttributeList *dest_attrs,
|
|
|
|
PK11URIAttributeList *dest_vattrs,
|
|
|
|
const PK11URIAttribute *attrs,
|
|
|
|
size_t num_attrs,
|
|
|
|
const char **attr_names,
|
|
|
|
size_t num_attr_names,
|
|
|
|
PK11URIAttributeCompareNameFunc compare_name,
|
|
|
|
PRBool allow_duplicate,
|
|
|
|
PRBool vendor_allow_duplicate)
|
|
|
|
{
|
|
|
|
SECStatus ret;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < num_attrs; i++) {
|
|
|
|
char *name, *value;
|
|
|
|
const char *p;
|
|
|
|
size_t j;
|
|
|
|
|
|
|
|
p = attrs[i].name;
|
|
|
|
|
|
|
|
/* The attribute must not be empty. */
|
|
|
|
if (*p == '\0') {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that the name doesn't contain invalid character. */
|
|
|
|
for (; *p != '\0'; p++) {
|
|
|
|
if (strchr(PK11URI_ATTR_NM_CHAR, *p) == NULL) {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
name = PORT_ArenaStrdup(dest_attrs->arena, attrs[i].name);
|
|
|
|
if (name == NULL) {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = PORT_ArenaStrdup(dest_attrs->arena, attrs[i].value);
|
|
|
|
if (value == NULL) {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < num_attr_names; j++) {
|
|
|
|
if (strcmp(name, attr_names[j]) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (j < num_attr_names) {
|
|
|
|
/* Named attribute. */
|
|
|
|
ret = pk11uri_InsertToAttributeList(dest_attrs,
|
|
|
|
name, value,
|
|
|
|
compare_name,
|
|
|
|
allow_duplicate);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Vendor attribute. */
|
|
|
|
ret = pk11uri_InsertToAttributeList(dest_vattrs,
|
|
|
|
name, value,
|
|
|
|
strcmp,
|
|
|
|
vendor_allow_duplicate);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
PK11URI *
|
|
|
|
PK11URI_CreateURI(const PK11URIAttribute *pattrs,
|
|
|
|
size_t num_pattrs,
|
|
|
|
const PK11URIAttribute *qattrs,
|
|
|
|
size_t num_qattrs)
|
|
|
|
{
|
|
|
|
PK11URI *result;
|
|
|
|
SECStatus ret;
|
|
|
|
|
|
|
|
result = pk11uri_AllocURI();
|
|
|
|
|
|
|
|
ret = pk11uri_InsertAttributes(&result->pattrs, &result->vpattrs,
|
|
|
|
pattrs, num_pattrs,
|
|
|
|
pattr_names, PR_ARRAY_SIZE(pattr_names),
|
|
|
|
pk11uri_ComparePathAttributeName,
|
|
|
|
PR_FALSE, PR_FALSE);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pk11uri_InsertAttributes(&result->qattrs, &result->vqattrs,
|
|
|
|
qattrs, num_qattrs,
|
|
|
|
qattr_names, PR_ARRAY_SIZE(qattr_names),
|
|
|
|
pk11uri_CompareQueryAttributeName,
|
|
|
|
PR_FALSE, PR_TRUE);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
PK11URI_DestroyURI(result);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parsing. */
|
|
|
|
static SECStatus
|
|
|
|
pk11uri_ParseAttributes(const char **string,
|
|
|
|
const char *stop_chars,
|
|
|
|
int separator,
|
|
|
|
const char *accept_chars,
|
|
|
|
const char **attr_names, size_t num_attr_names,
|
|
|
|
PK11URIAttributeList *attrs,
|
|
|
|
PK11URIAttributeList *vattrs,
|
|
|
|
PK11URIAttributeCompareNameFunc compare_name,
|
|
|
|
PRBool allow_duplicate,
|
|
|
|
PRBool vendor_allow_duplicate)
|
|
|
|
{
|
|
|
|
const char *p = *string;
|
|
|
|
|
|
|
|
for (; *p != '\0'; p++) {
|
|
|
|
const char *name_start, *name_end, *value_start, *value_end;
|
|
|
|
size_t name_length, value_length, i;
|
|
|
|
SECStatus ret;
|
|
|
|
|
|
|
|
if (strchr(stop_chars, *p) != NULL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (name_start = p; *p != '=' && *p != '\0'; p++) {
|
|
|
|
if (strchr(PK11URI_ATTR_NM_CHAR, *p) != NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
if (*p == '\0') {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
name_end = p++;
|
|
|
|
|
|
|
|
/* The attribute name must not be empty. */
|
|
|
|
if (name_end == name_start) {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (value_start = p; *p != separator && *p != '\0'; p++) {
|
|
|
|
if (strchr(stop_chars, *p) != NULL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (strchr(accept_chars, *p) != NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*p == '%') {
|
|
|
|
const char ch2 = *++p;
|
|
|
|
if (strchr(PK11URI_HEXDIG, ch2) != NULL) {
|
|
|
|
const char ch3 = *++p;
|
|
|
|
if (strchr(PK11URI_HEXDIG, ch3) != NULL)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
value_end = p;
|
|
|
|
|
|
|
|
name_length = name_end - name_start;
|
|
|
|
value_length = value_end - value_start;
|
|
|
|
|
|
|
|
for (i = 0; i < num_attr_names; i++) {
|
|
|
|
if (name_length == strlen(attr_names[i]) &&
|
|
|
|
memcmp(name_start, attr_names[i], name_length) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i < num_attr_names) {
|
|
|
|
/* Named attribute. */
|
|
|
|
ret = pk11uri_InsertToAttributeListEscaped(attrs,
|
|
|
|
name_start, name_length,
|
|
|
|
value_start, value_length,
|
|
|
|
compare_name,
|
|
|
|
allow_duplicate);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Vendor attribute. */
|
|
|
|
ret = pk11uri_InsertToAttributeListEscaped(vattrs,
|
|
|
|
name_start, name_length,
|
|
|
|
value_start, value_length,
|
|
|
|
strcmp,
|
|
|
|
vendor_allow_duplicate);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*p == '?' || *p == '\0') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*string = p;
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
PK11URI *
|
|
|
|
PK11URI_ParseURI(const char *string)
|
|
|
|
{
|
|
|
|
PK11URI *result;
|
|
|
|
const char *p = string;
|
|
|
|
SECStatus ret;
|
|
|
|
|
|
|
|
if (strncmp("pkcs11:", p, 7) != 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p += 7;
|
|
|
|
|
|
|
|
result = pk11uri_AllocURI();
|
|
|
|
if (result == NULL) {
|
2017-05-29 10:27:40 +03:00
|
|
|
return NULL;
|
2017-05-16 18:12:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse the path component and its attributes. */
|
|
|
|
ret = pk11uri_ParseAttributes(&p, "?", ';', PK11URI_PCHAR,
|
|
|
|
pattr_names, PR_ARRAY_SIZE(pattr_names),
|
|
|
|
&result->pattrs, &result->vpattrs,
|
|
|
|
pk11uri_ComparePathAttributeName,
|
|
|
|
PR_FALSE, PR_FALSE);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse the query component and its attributes. */
|
|
|
|
if (*p == '?') {
|
|
|
|
p++;
|
|
|
|
ret = pk11uri_ParseAttributes(&p, "", '&', PK11URI_QCHAR,
|
|
|
|
qattr_names, PR_ARRAY_SIZE(qattr_names),
|
|
|
|
&result->qattrs, &result->vqattrs,
|
|
|
|
pk11uri_CompareQueryAttributeName,
|
|
|
|
PR_FALSE, PR_TRUE);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
PK11URI_DestroyURI(result);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Formatting. */
|
|
|
|
char *
|
|
|
|
PK11URI_FormatURI(PLArenaPool *arena, PK11URI *uri)
|
|
|
|
{
|
|
|
|
PK11URIBuffer buffer;
|
|
|
|
SECStatus ret;
|
|
|
|
char *result = NULL;
|
|
|
|
|
|
|
|
pk11uri_InitBuffer(&buffer, arena);
|
|
|
|
|
|
|
|
ret = pk11uri_AppendBuffer(&buffer, (unsigned char *)"pkcs11:", 7);
|
|
|
|
if (ret != SECSuccess)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
ret = pk11uri_AppendAttributeListToBuffer(&buffer, &uri->pattrs, ';', PK11URI_PCHAR);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uri->pattrs.num_attrs > 0 && uri->vpattrs.num_attrs > 0) {
|
|
|
|
ret = pk11uri_AppendBuffer(&buffer, (unsigned char *)";", 1);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pk11uri_AppendAttributeListToBuffer(&buffer, &uri->vpattrs, ';',
|
|
|
|
PK11URI_PCHAR);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uri->qattrs.num_attrs > 0 || uri->vqattrs.num_attrs > 0) {
|
|
|
|
ret = pk11uri_AppendBuffer(&buffer, (unsigned char *)"?", 1);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pk11uri_AppendAttributeListToBuffer(&buffer, &uri->qattrs, '&', PK11URI_QCHAR);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uri->qattrs.num_attrs > 0 && uri->vqattrs.num_attrs > 0) {
|
|
|
|
ret = pk11uri_AppendBuffer(&buffer, (unsigned char *)"&", 1);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pk11uri_AppendAttributeListToBuffer(&buffer, &uri->vqattrs, '&',
|
|
|
|
PK11URI_QCHAR);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pk11uri_AppendBuffer(&buffer, (unsigned char *)"\0", 1);
|
|
|
|
if (ret != SECSuccess) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = buffer.data;
|
|
|
|
buffer.data = NULL;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
pk11uri_DestroyBuffer(&buffer);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deallocating. */
|
|
|
|
void
|
|
|
|
PK11URI_DestroyURI(PK11URI *uri)
|
|
|
|
{
|
|
|
|
pk11uri_DestroyAttributeList(&uri->pattrs);
|
|
|
|
pk11uri_DestroyAttributeList(&uri->vpattrs);
|
|
|
|
pk11uri_DestroyAttributeList(&uri->qattrs);
|
|
|
|
pk11uri_DestroyAttributeList(&uri->vqattrs);
|
|
|
|
PORT_FreeArena(uri->arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Accessors. */
|
|
|
|
static const char *
|
|
|
|
pk11uri_GetAttribute(PK11URIAttributeList *attrs,
|
|
|
|
PK11URIAttributeList *vattrs,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < attrs->num_attrs; i++) {
|
|
|
|
if (strcmp(name, attrs->attrs[i].name) == 0) {
|
|
|
|
return attrs->attrs[i].value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < vattrs->num_attrs; i++) {
|
|
|
|
if (strcmp(name, vattrs->attrs[i].name) == 0) {
|
|
|
|
return vattrs->attrs[i].value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
PK11URI_GetPathAttribute(PK11URI *uri, const char *name)
|
|
|
|
{
|
|
|
|
return pk11uri_GetAttribute(&uri->pattrs, &uri->vpattrs, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
PK11URI_GetQueryAttribute(PK11URI *uri, const char *name)
|
|
|
|
{
|
|
|
|
return pk11uri_GetAttribute(&uri->qattrs, &uri->vqattrs, name);
|
|
|
|
}
|