tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pkcs11uri.c (24727B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "pkcs11.h"
      6 #include "pkcs11uri.h"
      7 #include "plarena.h"
      8 #include "prprf.h"
      9 #include "secport.h"
     10 
     11 /* Character sets used in the ABNF rules in RFC7512. */
     12 #define PK11URI_DIGIT "0123456789"
     13 #define PK11URI_ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
     14 #define PK11URI_HEXDIG PK11URI_DIGIT "abcdefABCDEF"
     15 #define PK11URI_UNRESERVED PK11URI_ALPHA PK11URI_DIGIT "-._~"
     16 #define PK11URI_RES_AVAIL ":[]@!$'()*+,="
     17 #define PK11URI_PATH_RES_AVAIL PK11URI_RES_AVAIL "&"
     18 #define PK11URI_QUERY_RES_AVAIL PK11URI_RES_AVAIL "/?|"
     19 #define PK11URI_ATTR_NM_CHAR PK11URI_ALPHA PK11URI_DIGIT "-_"
     20 #define PK11URI_PCHAR PK11URI_UNRESERVED PK11URI_PATH_RES_AVAIL
     21 #define PK11URI_QCHAR PK11URI_UNRESERVED PK11URI_QUERY_RES_AVAIL
     22 
     23 /* Path attributes defined in RFC7512. */
     24 static const char *pattr_names[] = {
     25    PK11URI_PATTR_TOKEN,
     26    PK11URI_PATTR_MANUFACTURER,
     27    PK11URI_PATTR_SERIAL,
     28    PK11URI_PATTR_MODEL,
     29    PK11URI_PATTR_LIBRARY_MANUFACTURER,
     30    PK11URI_PATTR_LIBRARY_DESCRIPTION,
     31    PK11URI_PATTR_LIBRARY_VERSION,
     32    PK11URI_PATTR_OBJECT,
     33    PK11URI_PATTR_TYPE,
     34    PK11URI_PATTR_ID,
     35    PK11URI_PATTR_SLOT_MANUFACTURER,
     36    PK11URI_PATTR_SLOT_DESCRIPTION,
     37    PK11URI_PATTR_SLOT_ID
     38 };
     39 
     40 /* Query attributes defined in RFC7512. */
     41 static const char *qattr_names[] = {
     42    PK11URI_QATTR_PIN_SOURCE,
     43    PK11URI_QATTR_PIN_VALUE,
     44    PK11URI_QATTR_MODULE_NAME,
     45    PK11URI_QATTR_MODULE_PATH
     46 };
     47 
     48 struct PK11URIBufferStr {
     49    PLArenaPool *arena;
     50    unsigned char *data;
     51    size_t size;
     52    size_t allocated;
     53 };
     54 typedef struct PK11URIBufferStr PK11URIBuffer;
     55 
     56 struct PK11URIAttributeListEntryStr {
     57    char *name;
     58    SECItem value;
     59 };
     60 typedef struct PK11URIAttributeListEntryStr PK11URIAttributeListEntry;
     61 
     62 struct PK11URIAttributeListStr {
     63    PLArenaPool *arena;
     64    PK11URIAttributeListEntry *attrs;
     65    size_t num_attrs;
     66 };
     67 typedef struct PK11URIAttributeListStr PK11URIAttributeList;
     68 
     69 struct PK11URIStr {
     70    PLArenaPool *arena;
     71 
     72    PK11URIAttributeList pattrs;
     73    PK11URIAttributeList vpattrs;
     74 
     75    PK11URIAttributeList qattrs;
     76    PK11URIAttributeList vqattrs;
     77 };
     78 
     79 #define PK11URI_ARENA_SIZE 1024
     80 
     81 typedef int (*PK11URIAttributeCompareNameFunc)(const char *a, const char *b);
     82 
     83 /* This belongs in secport.h */
     84 #define PORT_ArenaGrowArray(poolp, oldptr, type, oldnum, newnum) \
     85    (type *)PORT_ArenaGrow((poolp), (oldptr),                    \
     86                           (oldnum) * sizeof(type), (newnum) * sizeof(type))
     87 #define PORT_ReallocArray(oldptr, type, newnum) \
     88    (type *)PORT_Realloc((oldptr), (newnum) * sizeof(type))
     89 
     90 /* Functions for resizable buffer. */
     91 static SECStatus
     92 pk11uri_AppendBuffer(PK11URIBuffer *buffer, const unsigned char *data,
     93                     size_t size)
     94 {
     95    /* Check overflow. */
     96    if (buffer->size + size < buffer->size)
     97        return SECFailure;
     98 
     99    if (buffer->size + size > buffer->allocated) {
    100        size_t allocated = buffer->allocated * 2 + size;
    101        if (allocated < buffer->allocated)
    102            return SECFailure;
    103        if (buffer->arena)
    104            buffer->data = PORT_ArenaGrow(buffer->arena, buffer->data,
    105                                          buffer->allocated, allocated);
    106        else
    107            buffer->data = PORT_Realloc(buffer->data, allocated);
    108        if (buffer->data == NULL)
    109            return SECFailure;
    110        buffer->allocated = allocated;
    111    }
    112 
    113    memcpy(&buffer->data[buffer->size], data, size);
    114    buffer->size += size;
    115 
    116    return SECSuccess;
    117 }
    118 
    119 static void
    120 pk11uri_InitBuffer(PK11URIBuffer *buffer, PLArenaPool *arena)
    121 {
    122    memset(buffer, 0, sizeof(PK11URIBuffer));
    123    buffer->arena = arena;
    124 }
    125 
    126 static void
    127 pk11uri_DestroyBuffer(PK11URIBuffer *buffer)
    128 {
    129    if (buffer->arena == NULL) {
    130        PORT_Free(buffer->data);
    131    }
    132 }
    133 
    134 /* URI encoding functions. */
    135 static char *
    136 pk11uri_Escape(PLArenaPool *arena, const unsigned char *value, size_t length,
    137               const char *available)
    138 {
    139    PK11URIBuffer buffer;
    140    const unsigned char *p;
    141    unsigned char buf[4];
    142    char *result = NULL;
    143    SECStatus ret;
    144 
    145    pk11uri_InitBuffer(&buffer, arena);
    146 
    147    for (p = value; p < value + length; p++) {
    148        if (strchr(available, *p) == NULL) {
    149            if (PR_snprintf((char *)buf, sizeof(buf), "%%%02X", *p) == (PRUint32)-1) {
    150                goto fail;
    151            }
    152            ret = pk11uri_AppendBuffer(&buffer, buf, 3);
    153            if (ret != SECSuccess) {
    154                goto fail;
    155            }
    156        } else {
    157            ret = pk11uri_AppendBuffer(&buffer, p, 1);
    158            if (ret != SECSuccess) {
    159                goto fail;
    160            }
    161        }
    162    }
    163    buf[0] = '\0';
    164    ret = pk11uri_AppendBuffer(&buffer, buf, 1);
    165    if (ret != SECSuccess) {
    166        goto fail;
    167    }
    168 
    169    /* Steal the memory allocated in buffer. */
    170    result = (char *)buffer.data;
    171    buffer.data = NULL;
    172 
    173 fail:
    174    pk11uri_DestroyBuffer(&buffer);
    175 
    176    return result;
    177 }
    178 
    179 static unsigned char *
    180 pk11uri_Unescape(PLArenaPool *arena, const char *value, size_t *length)
    181 {
    182    PK11URIBuffer buffer;
    183    const char *p;
    184    unsigned char buf[1];
    185    unsigned char *result = NULL;
    186    SECStatus ret;
    187 
    188    pk11uri_InitBuffer(&buffer, arena);
    189 
    190    for (p = value; p < value + *length; p++) {
    191        if (*p == '%') {
    192            int c;
    193            size_t i;
    194 
    195            p++;
    196            for (c = 0, i = 0; i < 2; i++) {
    197                int h = *(p + i);
    198                if ('0' <= h && h <= '9') {
    199                    c = (c << 4) | (h - '0');
    200                } else if ('a' <= h && h <= 'f') {
    201                    c = (c << 4) | (h - 'a' + 10);
    202                } else if ('A' <= h && h <= 'F') {
    203                    c = (c << 4) | (h - 'A' + 10);
    204                } else {
    205                    break;
    206                }
    207            }
    208            if (i != 2) {
    209                goto fail;
    210            }
    211            p++;
    212            buf[0] = c;
    213        } else {
    214            buf[0] = *p;
    215        }
    216        ret = pk11uri_AppendBuffer(&buffer, buf, 1);
    217        if (ret != SECSuccess) {
    218            goto fail;
    219        }
    220    }
    221    *length = buffer.size;
    222    buf[0] = '\0';
    223    ret = pk11uri_AppendBuffer(&buffer, buf, 1);
    224    if (ret != SECSuccess) {
    225        goto fail;
    226    }
    227 
    228    result = buffer.data;
    229    buffer.data = NULL;
    230 
    231 fail:
    232    pk11uri_DestroyBuffer(&buffer);
    233 
    234    return result;
    235 }
    236 
    237 /* Functions for manipulating attributes array. */
    238 
    239 /* Compare two attribute names by the array index in attr_names.  Both
    240 * attribute names must be present in attr_names, otherwise it is a
    241 * programming error. */
    242 static int
    243 pk11uri_CompareByPosition(const char *a, const char *b,
    244                          const char **attr_names, size_t num_attr_names)
    245 {
    246    size_t i, j;
    247 
    248    for (i = 0; i < num_attr_names; i++) {
    249        if (strcmp(a, attr_names[i]) == 0) {
    250            break;
    251        }
    252    }
    253    PR_ASSERT(i < num_attr_names);
    254 
    255    for (j = 0; j < num_attr_names; j++) {
    256        if (strcmp(b, attr_names[j]) == 0) {
    257            break;
    258        }
    259    }
    260    PR_ASSERT(j < num_attr_names);
    261 
    262    return i - j;
    263 }
    264 
    265 /* Those pk11uri_Compare{Path,Query}AttributeName functions are used
    266 * to reorder attributes when inserting. */
    267 static int
    268 pk11uri_ComparePathAttributeName(const char *a, const char *b)
    269 {
    270    return pk11uri_CompareByPosition(a, b, pattr_names, PR_ARRAY_SIZE(pattr_names));
    271 }
    272 
    273 static int
    274 pk11uri_CompareQueryAttributeName(const char *a, const char *b)
    275 {
    276    return pk11uri_CompareByPosition(a, b, qattr_names, PR_ARRAY_SIZE(qattr_names));
    277 }
    278 
    279 static SECStatus
    280 pk11uri_InsertToAttributeList(PK11URIAttributeList *attrs,
    281                              char *name, unsigned char *value, size_t size,
    282                              PK11URIAttributeCompareNameFunc compare_name,
    283                              PRBool allow_duplicate)
    284 {
    285    size_t i;
    286 
    287    if (attrs->arena) {
    288        attrs->attrs = PORT_ArenaGrowArray(attrs->arena, attrs->attrs,
    289                                           PK11URIAttributeListEntry,
    290                                           attrs->num_attrs,
    291                                           attrs->num_attrs + 1);
    292    } else {
    293        attrs->attrs = PORT_ReallocArray(attrs->attrs,
    294                                         PK11URIAttributeListEntry,
    295                                         attrs->num_attrs + 1);
    296    }
    297    if (attrs->attrs == NULL) {
    298        return SECFailure;
    299    }
    300 
    301    for (i = 0; i < attrs->num_attrs; i++) {
    302        if (!allow_duplicate && strcmp(name, attrs->attrs[i].name) == 0) {
    303            return SECFailure;
    304        }
    305        if (compare_name(name, attrs->attrs[i].name) < 0) {
    306            memmove(&attrs->attrs[i + 1], &attrs->attrs[i],
    307                    sizeof(PK11URIAttributeListEntry) * (attrs->num_attrs - i));
    308            break;
    309        }
    310    }
    311 
    312    attrs->attrs[i].name = name;
    313    attrs->attrs[i].value.type = siBuffer;
    314    attrs->attrs[i].value.data = value;
    315    attrs->attrs[i].value.len = size;
    316 
    317    attrs->num_attrs++;
    318 
    319    return SECSuccess;
    320 }
    321 
    322 static SECStatus
    323 pk11uri_InsertToAttributeListEscaped(PK11URIAttributeList *attrs,
    324                                     const char *name, size_t name_size,
    325                                     const char *value, size_t value_size,
    326                                     PK11URIAttributeCompareNameFunc compare_name,
    327                                     PRBool allow_duplicate)
    328 {
    329    char *name_copy = NULL;
    330    unsigned char *value_copy = NULL;
    331    SECStatus ret;
    332 
    333    if (attrs->arena) {
    334        name_copy = PORT_ArenaNewArray(attrs->arena, char, name_size + 1);
    335    } else {
    336        name_copy = PORT_Alloc(name_size + 1);
    337    }
    338    if (name_copy == NULL) {
    339        goto fail;
    340    }
    341    memcpy(name_copy, name, name_size);
    342    name_copy[name_size] = '\0';
    343 
    344    value_copy = pk11uri_Unescape(attrs->arena, value, &value_size);
    345    if (value_copy == NULL) {
    346        goto fail;
    347    }
    348 
    349    ret = pk11uri_InsertToAttributeList(attrs, name_copy, value_copy, value_size,
    350                                        compare_name, allow_duplicate);
    351    if (ret != SECSuccess) {
    352        goto fail;
    353    }
    354 
    355    return ret;
    356 
    357 fail:
    358    if (attrs->arena == NULL) {
    359        PORT_Free(name_copy);
    360        PORT_Free(value_copy);
    361    }
    362 
    363    return SECFailure;
    364 }
    365 
    366 static void
    367 pk11uri_InitAttributeList(PK11URIAttributeList *attrs, PLArenaPool *arena)
    368 {
    369    memset(attrs, 0, sizeof(PK11URIAttributeList));
    370    attrs->arena = arena;
    371 }
    372 
    373 static void
    374 pk11uri_DestroyAttributeList(PK11URIAttributeList *attrs)
    375 {
    376    if (attrs->arena == NULL) {
    377        size_t i;
    378 
    379        for (i = 0; i < attrs->num_attrs; i++) {
    380            PORT_Free(attrs->attrs[i].name);
    381            PORT_Free(attrs->attrs[i].value.data);
    382        }
    383        PORT_Free(attrs->attrs);
    384    }
    385 }
    386 
    387 static SECStatus
    388 pk11uri_AppendAttributeListToBuffer(PK11URIBuffer *buffer,
    389                                    PK11URIAttributeList *attrs,
    390                                    int separator,
    391                                    const char *unescaped)
    392 {
    393    size_t i;
    394    SECStatus ret;
    395 
    396    for (i = 0; i < attrs->num_attrs; i++) {
    397        unsigned char sep[1];
    398        char *escaped;
    399        PK11URIAttributeListEntry *attr = &attrs->attrs[i];
    400 
    401        if (i > 0) {
    402            sep[0] = separator;
    403            ret = pk11uri_AppendBuffer(buffer, sep, 1);
    404            if (ret != SECSuccess) {
    405                return ret;
    406            }
    407        }
    408 
    409        ret = pk11uri_AppendBuffer(buffer, (unsigned char *)attr->name,
    410                                   strlen(attr->name));
    411        if (ret != SECSuccess) {
    412            return ret;
    413        }
    414 
    415        sep[0] = '=';
    416        ret = pk11uri_AppendBuffer(buffer, sep, 1);
    417        if (ret != SECSuccess) {
    418            return ret;
    419        }
    420 
    421        escaped = pk11uri_Escape(buffer->arena, attr->value.data, attr->value.len,
    422                                 unescaped);
    423        if (escaped == NULL) {
    424            return ret;
    425        }
    426        ret = pk11uri_AppendBuffer(buffer, (unsigned char *)escaped,
    427                                   strlen(escaped));
    428        if (buffer->arena == NULL) {
    429            PORT_Free(escaped);
    430        }
    431        if (ret != SECSuccess) {
    432            return ret;
    433        }
    434    }
    435 
    436    return SECSuccess;
    437 }
    438 
    439 /* Creation of PK11URI object. */
    440 static PK11URI *
    441 pk11uri_AllocURI(void)
    442 {
    443    PLArenaPool *arena;
    444    PK11URI *result;
    445 
    446    arena = PORT_NewArena(PK11URI_ARENA_SIZE);
    447    if (arena == NULL) {
    448        return NULL;
    449    }
    450 
    451    result = PORT_ArenaZAlloc(arena, sizeof(PK11URI));
    452    if (result == NULL) {
    453        PORT_FreeArena(arena, PR_FALSE);
    454        return NULL;
    455    }
    456 
    457    result->arena = arena;
    458    pk11uri_InitAttributeList(&result->pattrs, arena);
    459    pk11uri_InitAttributeList(&result->vpattrs, arena);
    460    pk11uri_InitAttributeList(&result->qattrs, arena);
    461    pk11uri_InitAttributeList(&result->vqattrs, arena);
    462 
    463    return result;
    464 }
    465 
    466 static SECStatus
    467 pk11uri_InsertAttributes(PK11URIAttributeList *dest_attrs,
    468                         PK11URIAttributeList *dest_vattrs,
    469                         const PK11URIAttribute *attrs,
    470                         size_t num_attrs,
    471                         const char **attr_names,
    472                         size_t num_attr_names,
    473                         PK11URIAttributeCompareNameFunc compare_name,
    474                         PRBool allow_duplicate,
    475                         PRBool vendor_allow_duplicate)
    476 {
    477    SECStatus ret;
    478    size_t i;
    479 
    480    for (i = 0; i < num_attrs; i++) {
    481        char *name, *value;
    482        const char *p;
    483        size_t j;
    484 
    485        p = attrs[i].name;
    486 
    487        /* The attribute must not be empty. */
    488        if (*p == '\0') {
    489            return SECFailure;
    490        }
    491 
    492        /* Check that the name doesn't contain invalid character. */
    493        for (; *p != '\0'; p++) {
    494            if (strchr(PK11URI_ATTR_NM_CHAR, *p) == NULL) {
    495                return SECFailure;
    496            }
    497        }
    498 
    499        name = PORT_ArenaStrdup(dest_attrs->arena, attrs[i].name);
    500        if (name == NULL) {
    501            return SECFailure;
    502        }
    503 
    504        value = PORT_ArenaStrdup(dest_attrs->arena, attrs[i].value);
    505        if (value == NULL) {
    506            return SECFailure;
    507        }
    508 
    509        for (j = 0; j < num_attr_names; j++) {
    510            if (strcmp(name, attr_names[j]) == 0) {
    511                break;
    512            }
    513        }
    514        if (j < num_attr_names) {
    515            /* Named attribute. */
    516            ret = pk11uri_InsertToAttributeList(dest_attrs,
    517                                                name,
    518                                                (unsigned char *)value,
    519                                                strlen(value),
    520                                                compare_name,
    521                                                allow_duplicate);
    522            if (ret != SECSuccess) {
    523                return ret;
    524            }
    525        } else {
    526            /* Vendor attribute. */
    527            ret = pk11uri_InsertToAttributeList(dest_vattrs,
    528                                                name,
    529                                                (unsigned char *)value,
    530                                                strlen(value),
    531                                                strcmp,
    532                                                vendor_allow_duplicate);
    533            if (ret != SECSuccess) {
    534                return ret;
    535            }
    536        }
    537    }
    538 
    539    return SECSuccess;
    540 }
    541 
    542 PK11URI *
    543 PK11URI_CreateURI(const PK11URIAttribute *pattrs,
    544                  size_t num_pattrs,
    545                  const PK11URIAttribute *qattrs,
    546                  size_t num_qattrs)
    547 {
    548    PK11URI *result;
    549    SECStatus ret;
    550 
    551    result = pk11uri_AllocURI();
    552 
    553    ret = pk11uri_InsertAttributes(&result->pattrs, &result->vpattrs,
    554                                   pattrs, num_pattrs,
    555                                   pattr_names, PR_ARRAY_SIZE(pattr_names),
    556                                   pk11uri_ComparePathAttributeName,
    557                                   PR_FALSE, PR_FALSE);
    558    if (ret != SECSuccess) {
    559        goto fail;
    560    }
    561 
    562    ret = pk11uri_InsertAttributes(&result->qattrs, &result->vqattrs,
    563                                   qattrs, num_qattrs,
    564                                   qattr_names, PR_ARRAY_SIZE(qattr_names),
    565                                   pk11uri_CompareQueryAttributeName,
    566                                   PR_FALSE, PR_TRUE);
    567    if (ret != SECSuccess) {
    568        goto fail;
    569    }
    570 
    571    return result;
    572 
    573 fail:
    574    PK11URI_DestroyURI(result);
    575 
    576    return NULL;
    577 }
    578 
    579 /* Parsing. */
    580 static SECStatus
    581 pk11uri_ParseAttributes(const char **string,
    582                        const char *stop_chars,
    583                        int separator,
    584                        const char *accept_chars,
    585                        const char **attr_names, size_t num_attr_names,
    586                        PK11URIAttributeList *attrs,
    587                        PK11URIAttributeList *vattrs,
    588                        PK11URIAttributeCompareNameFunc compare_name,
    589                        PRBool allow_duplicate,
    590                        PRBool vendor_allow_duplicate)
    591 {
    592    const char *p = *string;
    593 
    594    for (; *p != '\0'; p++) {
    595        const char *name_start, *name_end, *value_start, *value_end;
    596        size_t name_length, value_length, i;
    597        SECStatus ret;
    598 
    599        if (strchr(stop_chars, *p) != NULL) {
    600            break;
    601        }
    602        for (name_start = p; *p != '=' && *p != '\0'; p++) {
    603            if (strchr(PK11URI_ATTR_NM_CHAR, *p) != NULL)
    604                continue;
    605 
    606            return SECFailure;
    607        }
    608        if (*p == '\0') {
    609            return SECFailure;
    610        }
    611        name_end = p++;
    612 
    613        /* The attribute name must not be empty. */
    614        if (name_end == name_start) {
    615            return SECFailure;
    616        }
    617 
    618        for (value_start = p; *p != separator && *p != '\0'; p++) {
    619            if (strchr(stop_chars, *p) != NULL) {
    620                break;
    621            }
    622            if (strchr(accept_chars, *p) != NULL) {
    623                continue;
    624            }
    625            if (*p == '%') {
    626                const char ch2 = *++p;
    627                if (strchr(PK11URI_HEXDIG, ch2) != NULL) {
    628                    const char ch3 = *++p;
    629                    if (strchr(PK11URI_HEXDIG, ch3) != NULL)
    630                        continue;
    631                }
    632            }
    633 
    634            return SECFailure;
    635        }
    636        value_end = p;
    637 
    638        name_length = name_end - name_start;
    639        value_length = value_end - value_start;
    640 
    641        for (i = 0; i < num_attr_names; i++) {
    642            if (name_length == strlen(attr_names[i]) &&
    643                memcmp(name_start, attr_names[i], name_length) == 0) {
    644                break;
    645            }
    646        }
    647        if (i < num_attr_names) {
    648            /* Named attribute. */
    649            ret = pk11uri_InsertToAttributeListEscaped(attrs,
    650                                                       name_start, name_length,
    651                                                       value_start, value_length,
    652                                                       compare_name,
    653                                                       allow_duplicate);
    654            if (ret != SECSuccess) {
    655                return ret;
    656            }
    657        } else {
    658            /* Vendor attribute. */
    659            ret = pk11uri_InsertToAttributeListEscaped(vattrs,
    660                                                       name_start, name_length,
    661                                                       value_start, value_length,
    662                                                       strcmp,
    663                                                       vendor_allow_duplicate);
    664            if (ret != SECSuccess) {
    665                return ret;
    666            }
    667        }
    668 
    669        if (*p == '?' || *p == '\0') {
    670            break;
    671        }
    672    }
    673 
    674    *string = p;
    675    return SECSuccess;
    676 }
    677 
    678 PK11URI *
    679 PK11URI_ParseURI(const char *string)
    680 {
    681    PK11URI *result;
    682    const char *p = string;
    683    SECStatus ret;
    684 
    685    if (PORT_Strncasecmp("pkcs11:", p, 7) != 0) {
    686        return NULL;
    687    }
    688    p += 7;
    689 
    690    result = pk11uri_AllocURI();
    691    if (result == NULL) {
    692        return NULL;
    693    }
    694 
    695    /* Parse the path component and its attributes. */
    696    ret = pk11uri_ParseAttributes(&p, "?", ';', PK11URI_PCHAR,
    697                                  pattr_names, PR_ARRAY_SIZE(pattr_names),
    698                                  &result->pattrs, &result->vpattrs,
    699                                  pk11uri_ComparePathAttributeName,
    700                                  PR_FALSE, PR_FALSE);
    701    if (ret != SECSuccess) {
    702        goto fail;
    703    }
    704 
    705    /* Parse the query component and its attributes. */
    706    if (*p == '?') {
    707        p++;
    708        ret = pk11uri_ParseAttributes(&p, "", '&', PK11URI_QCHAR,
    709                                      qattr_names, PR_ARRAY_SIZE(qattr_names),
    710                                      &result->qattrs, &result->vqattrs,
    711                                      pk11uri_CompareQueryAttributeName,
    712                                      PR_FALSE, PR_TRUE);
    713        if (ret != SECSuccess) {
    714            goto fail;
    715        }
    716    }
    717 
    718    return result;
    719 
    720 fail:
    721    PK11URI_DestroyURI(result);
    722 
    723    return NULL;
    724 }
    725 
    726 /* Formatting. */
    727 char *
    728 PK11URI_FormatURI(PLArenaPool *arena, PK11URI *uri)
    729 {
    730    PK11URIBuffer buffer;
    731    SECStatus ret;
    732    char *result = NULL;
    733 
    734    pk11uri_InitBuffer(&buffer, arena);
    735 
    736    ret = pk11uri_AppendBuffer(&buffer, (unsigned char *)"pkcs11:", 7);
    737    if (ret != SECSuccess)
    738        goto fail;
    739 
    740    ret = pk11uri_AppendAttributeListToBuffer(&buffer, &uri->pattrs, ';', PK11URI_PCHAR);
    741    if (ret != SECSuccess) {
    742        goto fail;
    743    }
    744 
    745    if (uri->pattrs.num_attrs > 0 && uri->vpattrs.num_attrs > 0) {
    746        ret = pk11uri_AppendBuffer(&buffer, (unsigned char *)";", 1);
    747        if (ret != SECSuccess) {
    748            goto fail;
    749        }
    750    }
    751 
    752    ret = pk11uri_AppendAttributeListToBuffer(&buffer, &uri->vpattrs, ';',
    753                                              PK11URI_PCHAR);
    754    if (ret != SECSuccess) {
    755        goto fail;
    756    }
    757 
    758    if (uri->qattrs.num_attrs > 0 || uri->vqattrs.num_attrs > 0) {
    759        ret = pk11uri_AppendBuffer(&buffer, (unsigned char *)"?", 1);
    760        if (ret != SECSuccess) {
    761            goto fail;
    762        }
    763    }
    764 
    765    ret = pk11uri_AppendAttributeListToBuffer(&buffer, &uri->qattrs, '&', PK11URI_QCHAR);
    766    if (ret != SECSuccess) {
    767        goto fail;
    768    }
    769 
    770    if (uri->qattrs.num_attrs > 0 && uri->vqattrs.num_attrs > 0) {
    771        ret = pk11uri_AppendBuffer(&buffer, (unsigned char *)"&", 1);
    772        if (ret != SECSuccess) {
    773            goto fail;
    774        }
    775    }
    776 
    777    ret = pk11uri_AppendAttributeListToBuffer(&buffer, &uri->vqattrs, '&',
    778                                              PK11URI_QCHAR);
    779    if (ret != SECSuccess) {
    780        goto fail;
    781    }
    782 
    783    ret = pk11uri_AppendBuffer(&buffer, (unsigned char *)"\0", 1);
    784    if (ret != SECSuccess) {
    785        goto fail;
    786    }
    787 
    788    result = (char *)buffer.data;
    789    buffer.data = NULL;
    790 
    791 fail:
    792    pk11uri_DestroyBuffer(&buffer);
    793 
    794    return result;
    795 }
    796 
    797 /* Deallocating. */
    798 void
    799 PK11URI_DestroyURI(PK11URI *uri)
    800 {
    801    pk11uri_DestroyAttributeList(&uri->pattrs);
    802    pk11uri_DestroyAttributeList(&uri->vpattrs);
    803    pk11uri_DestroyAttributeList(&uri->qattrs);
    804    pk11uri_DestroyAttributeList(&uri->vqattrs);
    805    PORT_FreeArena(uri->arena, PR_FALSE);
    806 }
    807 
    808 /* Accessors. */
    809 static const SECItem *
    810 pk11uri_GetAttribute(PK11URIAttributeList *attrs,
    811                     PK11URIAttributeList *vattrs,
    812                     const char *name)
    813 {
    814    size_t i;
    815 
    816    for (i = 0; i < attrs->num_attrs; i++) {
    817        if (strcmp(name, attrs->attrs[i].name) == 0) {
    818            return &attrs->attrs[i].value;
    819        }
    820    }
    821 
    822    for (i = 0; i < vattrs->num_attrs; i++) {
    823        if (strcmp(name, vattrs->attrs[i].name) == 0) {
    824            return &vattrs->attrs[i].value;
    825        }
    826    }
    827 
    828    return NULL;
    829 }
    830 
    831 const SECItem *
    832 PK11URI_GetPathAttributeItem(PK11URI *uri, const char *name)
    833 {
    834    return pk11uri_GetAttribute(&uri->pattrs, &uri->vpattrs, name);
    835 }
    836 
    837 const char *
    838 PK11URI_GetPathAttribute(PK11URI *uri, const char *name)
    839 {
    840    const SECItem *value;
    841 
    842    value = PK11URI_GetPathAttributeItem(uri, name);
    843    if (!value) {
    844        return NULL;
    845    }
    846 
    847    return (const char *)value->data;
    848 }
    849 
    850 const SECItem *
    851 PK11URI_GetQueryAttributeItem(PK11URI *uri, const char *name)
    852 {
    853    return pk11uri_GetAttribute(&uri->qattrs, &uri->vqattrs, name);
    854 }
    855 
    856 const char *
    857 PK11URI_GetQueryAttribute(PK11URI *uri, const char *name)
    858 {
    859    const SECItem *value;
    860 
    861    value = PK11URI_GetQueryAttributeItem(uri, name);
    862    if (!value) {
    863        return NULL;
    864    }
    865 
    866    return (const char *)value->data;
    867 }