| OLD | NEW | 
 | (Empty) | 
|    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 /* |  | 
|    6  * Certificate Extensions handling code |  | 
|    7  * |  | 
|    8  */ |  | 
|    9  |  | 
|   10 #include "cert.h" |  | 
|   11 #include "secitem.h" |  | 
|   12 #include "secoid.h" |  | 
|   13 #include "secder.h" |  | 
|   14 #include "secasn1.h" |  | 
|   15 #include "certxutl.h" |  | 
|   16 #include "secerr.h" |  | 
|   17  |  | 
|   18 #ifdef OLD |  | 
|   19 #include "ocspti.h" /* XXX a better extensions interface would not |  | 
|   20                          * require knowledge of data structures of callers */ |  | 
|   21 #endif |  | 
|   22  |  | 
|   23 static CERTCertExtension * |  | 
|   24 GetExtension(CERTCertExtension **extensions, SECItem *oid) |  | 
|   25 { |  | 
|   26     CERTCertExtension **exts; |  | 
|   27     CERTCertExtension *ext = NULL; |  | 
|   28     SECComparison comp; |  | 
|   29  |  | 
|   30     exts = extensions; |  | 
|   31  |  | 
|   32     if (exts) { |  | 
|   33         while (*exts) { |  | 
|   34             ext = *exts; |  | 
|   35             comp = SECITEM_CompareItem(oid, &ext->id); |  | 
|   36             if (comp == SECEqual) |  | 
|   37                 break; |  | 
|   38  |  | 
|   39             exts++; |  | 
|   40         } |  | 
|   41         return (*exts ? ext : NULL); |  | 
|   42     } |  | 
|   43     return (NULL); |  | 
|   44 } |  | 
|   45  |  | 
|   46 SECStatus |  | 
|   47 cert_FindExtensionByOID(CERTCertExtension **extensions, SECItem *oid, |  | 
|   48                         SECItem *value) |  | 
|   49 { |  | 
|   50     CERTCertExtension *ext; |  | 
|   51     SECStatus rv = SECSuccess; |  | 
|   52  |  | 
|   53     ext = GetExtension(extensions, oid); |  | 
|   54     if (ext == NULL) { |  | 
|   55         PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); |  | 
|   56         return (SECFailure); |  | 
|   57     } |  | 
|   58     if (value) |  | 
|   59         rv = SECITEM_CopyItem(NULL, value, &ext->value); |  | 
|   60     return (rv); |  | 
|   61 } |  | 
|   62  |  | 
|   63 SECStatus |  | 
|   64 CERT_GetExtenCriticality(CERTCertExtension **extensions, int tag, |  | 
|   65                          PRBool *isCritical) |  | 
|   66 { |  | 
|   67     CERTCertExtension *ext; |  | 
|   68     SECOidData *oid; |  | 
|   69  |  | 
|   70     if (!isCritical) |  | 
|   71         return (SECSuccess); |  | 
|   72  |  | 
|   73     /* find the extension in the extensions list */ |  | 
|   74     oid = SECOID_FindOIDByTag((SECOidTag)tag); |  | 
|   75     if (!oid) { |  | 
|   76         return (SECFailure); |  | 
|   77     } |  | 
|   78     ext = GetExtension(extensions, &oid->oid); |  | 
|   79     if (ext == NULL) { |  | 
|   80         PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); |  | 
|   81         return (SECFailure); |  | 
|   82     } |  | 
|   83  |  | 
|   84     /* If the criticality is omitted, then it is false by default. |  | 
|   85        ex->critical.data is NULL */ |  | 
|   86     if (ext->critical.data == NULL) |  | 
|   87         *isCritical = PR_FALSE; |  | 
|   88     else |  | 
|   89         *isCritical = (ext->critical.data[0] == 0xff) ? PR_TRUE : PR_FALSE; |  | 
|   90     return (SECSuccess); |  | 
|   91 } |  | 
|   92  |  | 
|   93 SECStatus |  | 
|   94 cert_FindExtension(CERTCertExtension **extensions, int tag, SECItem *value) |  | 
|   95 { |  | 
|   96     SECOidData *oid; |  | 
|   97  |  | 
|   98     oid = SECOID_FindOIDByTag((SECOidTag)tag); |  | 
|   99     if (!oid) { |  | 
|  100         return (SECFailure); |  | 
|  101     } |  | 
|  102  |  | 
|  103     return (cert_FindExtensionByOID(extensions, &oid->oid, value)); |  | 
|  104 } |  | 
|  105  |  | 
|  106 typedef struct _extNode { |  | 
|  107     struct _extNode *next; |  | 
|  108     CERTCertExtension *ext; |  | 
|  109 } extNode; |  | 
|  110  |  | 
|  111 typedef struct { |  | 
|  112     void (*setExts)(void *object, CERTCertExtension **exts); |  | 
|  113     void *object; |  | 
|  114     PLArenaPool *ownerArena; |  | 
|  115     PLArenaPool *arena; |  | 
|  116     extNode *head; |  | 
|  117     int count; |  | 
|  118 } extRec; |  | 
|  119  |  | 
|  120 /* |  | 
|  121  * cert_StartExtensions |  | 
|  122  * |  | 
|  123  * NOTE: This interface changed significantly to remove knowledge |  | 
|  124  *   about callers data structures (owner objects) |  | 
|  125  */ |  | 
|  126 void * |  | 
|  127 cert_StartExtensions(void *owner, PLArenaPool *ownerArena, |  | 
|  128                      void (*setExts)(void *object, CERTCertExtension **exts)) |  | 
|  129 { |  | 
|  130     PLArenaPool *arena; |  | 
|  131     extRec *handle; |  | 
|  132  |  | 
|  133     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |  | 
|  134     if (!arena) { |  | 
|  135         return (0); |  | 
|  136     } |  | 
|  137  |  | 
|  138     handle = (extRec *)PORT_ArenaAlloc(arena, sizeof(extRec)); |  | 
|  139     if (!handle) { |  | 
|  140         PORT_FreeArena(arena, PR_FALSE); |  | 
|  141         return (0); |  | 
|  142     } |  | 
|  143  |  | 
|  144     handle->object = owner; |  | 
|  145     handle->ownerArena = ownerArena; |  | 
|  146     handle->setExts = setExts; |  | 
|  147  |  | 
|  148     handle->arena = arena; |  | 
|  149     handle->head = 0; |  | 
|  150     handle->count = 0; |  | 
|  151  |  | 
|  152     return (handle); |  | 
|  153 } |  | 
|  154  |  | 
|  155 static unsigned char hextrue = 0xff; |  | 
|  156  |  | 
|  157 /* |  | 
|  158  * Note - assumes that data pointed to by oid->data will not move |  | 
|  159  */ |  | 
|  160 SECStatus |  | 
|  161 CERT_AddExtensionByOID(void *exthandle, SECItem *oid, SECItem *value, |  | 
|  162                        PRBool critical, PRBool copyData) |  | 
|  163 { |  | 
|  164     CERTCertExtension *ext; |  | 
|  165     SECStatus rv; |  | 
|  166     extNode *node; |  | 
|  167     extRec *handle; |  | 
|  168  |  | 
|  169     handle = (extRec *)exthandle; |  | 
|  170  |  | 
|  171     /* allocate space for extension and list node */ |  | 
|  172     ext = (CERTCertExtension *)PORT_ArenaZAlloc(handle->ownerArena, |  | 
|  173                                                 sizeof(CERTCertExtension)); |  | 
|  174     if (!ext) { |  | 
|  175         return (SECFailure); |  | 
|  176     } |  | 
|  177  |  | 
|  178     node = (extNode *)PORT_ArenaAlloc(handle->arena, sizeof(extNode)); |  | 
|  179     if (!node) { |  | 
|  180         return (SECFailure); |  | 
|  181     } |  | 
|  182  |  | 
|  183     /* add to list */ |  | 
|  184     node->next = handle->head; |  | 
|  185     handle->head = node; |  | 
|  186  |  | 
|  187     /* point to ext struct */ |  | 
|  188     node->ext = ext; |  | 
|  189  |  | 
|  190     /* the object ID of the extension */ |  | 
|  191     ext->id = *oid; |  | 
|  192  |  | 
|  193     /* set critical field */ |  | 
|  194     if (critical) { |  | 
|  195         ext->critical.data = (unsigned char *)&hextrue; |  | 
|  196         ext->critical.len = 1; |  | 
|  197     } |  | 
|  198  |  | 
|  199     /* set the value */ |  | 
|  200     if (copyData) { |  | 
|  201         rv = SECITEM_CopyItem(handle->ownerArena, &ext->value, value); |  | 
|  202         if (rv) { |  | 
|  203             return (SECFailure); |  | 
|  204         } |  | 
|  205     } else { |  | 
|  206         ext->value = *value; |  | 
|  207     } |  | 
|  208  |  | 
|  209     handle->count++; |  | 
|  210  |  | 
|  211     return (SECSuccess); |  | 
|  212 } |  | 
|  213  |  | 
|  214 SECStatus |  | 
|  215 CERT_AddExtension(void *exthandle, int idtag, SECItem *value, PRBool critical, |  | 
|  216                   PRBool copyData) |  | 
|  217 { |  | 
|  218     SECOidData *oid; |  | 
|  219  |  | 
|  220     oid = SECOID_FindOIDByTag((SECOidTag)idtag); |  | 
|  221     if (!oid) { |  | 
|  222         return (SECFailure); |  | 
|  223     } |  | 
|  224  |  | 
|  225     return (CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical, |  | 
|  226                                    copyData)); |  | 
|  227 } |  | 
|  228  |  | 
|  229 SECStatus |  | 
|  230 CERT_EncodeAndAddExtension(void *exthandle, int idtag, void *value, |  | 
|  231                            PRBool critical, const SEC_ASN1Template *atemplate) |  | 
|  232 { |  | 
|  233     extRec *handle; |  | 
|  234     SECItem *encitem; |  | 
|  235  |  | 
|  236     handle = (extRec *)exthandle; |  | 
|  237  |  | 
|  238     encitem = SEC_ASN1EncodeItem(handle->ownerArena, NULL, value, atemplate); |  | 
|  239     if (encitem == NULL) { |  | 
|  240         return (SECFailure); |  | 
|  241     } |  | 
|  242  |  | 
|  243     return CERT_AddExtension(exthandle, idtag, encitem, critical, PR_FALSE); |  | 
|  244 } |  | 
|  245  |  | 
|  246 void |  | 
|  247 PrepareBitStringForEncoding(SECItem *bitsmap, SECItem *value) |  | 
|  248 { |  | 
|  249     unsigned char onebyte; |  | 
|  250     unsigned int i, len = 0; |  | 
|  251  |  | 
|  252     /* to prevent warning on some platform at compile time */ |  | 
|  253     onebyte = '\0'; |  | 
|  254     /* Get the position of the right-most turn-on bit */ |  | 
|  255     for (i = 0; i < (value->len) * 8; ++i) { |  | 
|  256         if (i % 8 == 0) |  | 
|  257             onebyte = value->data[i / 8]; |  | 
|  258         if (onebyte & 0x80) |  | 
|  259             len = i; |  | 
|  260         onebyte <<= 1; |  | 
|  261     } |  | 
|  262     bitsmap->data = value->data; |  | 
|  263     /* Add one here since we work with base 1 */ |  | 
|  264     bitsmap->len = len + 1; |  | 
|  265 } |  | 
|  266  |  | 
|  267 SECStatus |  | 
|  268 CERT_EncodeAndAddBitStrExtension(void *exthandle, int idtag, SECItem *value, |  | 
|  269                                  PRBool critical) |  | 
|  270 { |  | 
|  271     SECItem bitsmap; |  | 
|  272  |  | 
|  273     PrepareBitStringForEncoding(&bitsmap, value); |  | 
|  274     return (CERT_EncodeAndAddExtension(exthandle, idtag, &bitsmap, critical, |  | 
|  275                                        SEC_ASN1_GET(SEC_BitStringTemplate))); |  | 
|  276 } |  | 
|  277  |  | 
|  278 SECStatus |  | 
|  279 CERT_FinishExtensions(void *exthandle) |  | 
|  280 { |  | 
|  281     extRec *handle; |  | 
|  282     extNode *node; |  | 
|  283     CERTCertExtension **exts; |  | 
|  284     SECStatus rv = SECFailure; |  | 
|  285  |  | 
|  286     handle = (extRec *)exthandle; |  | 
|  287  |  | 
|  288     /* allocate space for extensions array */ |  | 
|  289     exts = PORT_ArenaNewArray(handle->ownerArena, CERTCertExtension *, |  | 
|  290                               handle->count + 1); |  | 
|  291     if (exts == NULL) { |  | 
|  292         goto loser; |  | 
|  293     } |  | 
|  294  |  | 
|  295 /* put extensions in owner object and update its version number */ |  | 
|  296  |  | 
|  297 #ifdef OLD |  | 
|  298     switch (handle->type) { |  | 
|  299         case CertificateExtensions: |  | 
|  300             handle->owner.cert->extensions = exts; |  | 
|  301             DER_SetUInteger(ownerArena, &(handle->owner.cert->version), |  | 
|  302                             SEC_CERTIFICATE_VERSION_3); |  | 
|  303             break; |  | 
|  304         case CrlExtensions: |  | 
|  305             handle->owner.crl->extensions = exts; |  | 
|  306             DER_SetUInteger(ownerArena, &(handle->owner.crl->version), |  | 
|  307                             SEC_CRL_VERSION_2); |  | 
|  308             break; |  | 
|  309         case OCSPRequestExtensions: |  | 
|  310             handle->owner.request->tbsRequest->requestExtensions = exts; |  | 
|  311             break; |  | 
|  312         case OCSPSingleRequestExtensions: |  | 
|  313             handle->owner.singleRequest->singleRequestExtensions = exts; |  | 
|  314             break; |  | 
|  315         case OCSPResponseSingleExtensions: |  | 
|  316             handle->owner.singleResponse->singleExtensions = exts; |  | 
|  317             break; |  | 
|  318     } |  | 
|  319 #endif |  | 
|  320  |  | 
|  321     handle->setExts(handle->object, exts); |  | 
|  322  |  | 
|  323     /* update the version number */ |  | 
|  324  |  | 
|  325     /* copy each extension pointer */ |  | 
|  326     node = handle->head; |  | 
|  327     while (node) { |  | 
|  328         *exts = node->ext; |  | 
|  329  |  | 
|  330         node = node->next; |  | 
|  331         exts++; |  | 
|  332     } |  | 
|  333  |  | 
|  334     /* terminate the array of extensions */ |  | 
|  335     *exts = 0; |  | 
|  336  |  | 
|  337     rv = SECSuccess; |  | 
|  338  |  | 
|  339 loser: |  | 
|  340     /* free working arena */ |  | 
|  341     PORT_FreeArena(handle->arena, PR_FALSE); |  | 
|  342     return rv; |  | 
|  343 } |  | 
|  344  |  | 
|  345 SECStatus |  | 
|  346 CERT_MergeExtensions(void *exthandle, CERTCertExtension **extensions) |  | 
|  347 { |  | 
|  348     CERTCertExtension *ext; |  | 
|  349     SECStatus rv = SECSuccess; |  | 
|  350     SECOidTag tag; |  | 
|  351     extNode *node; |  | 
|  352     extRec *handle = exthandle; |  | 
|  353  |  | 
|  354     if (!exthandle || !extensions) { |  | 
|  355         PORT_SetError(SEC_ERROR_INVALID_ARGS); |  | 
|  356         return SECFailure; |  | 
|  357     } |  | 
|  358     while ((ext = *extensions++) != NULL) { |  | 
|  359         tag = SECOID_FindOIDTag(&ext->id); |  | 
|  360         for (node = handle->head; node != NULL; node = node->next) { |  | 
|  361             if (tag == 0) { |  | 
|  362                 if (SECITEM_ItemsAreEqual(&ext->id, &node->ext->id)) |  | 
|  363                     break; |  | 
|  364             } else { |  | 
|  365                 if (SECOID_FindOIDTag(&node->ext->id) == tag) { |  | 
|  366                     break; |  | 
|  367                 } |  | 
|  368             } |  | 
|  369         } |  | 
|  370         if (node == NULL) { |  | 
|  371             PRBool critical = (ext->critical.len != 0 && |  | 
|  372                                ext->critical.data[ext->critical.len - 1] != 0); |  | 
|  373             if (critical && tag == SEC_OID_UNKNOWN) { |  | 
|  374                 PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); |  | 
|  375                 rv = SECFailure; |  | 
|  376                 break; |  | 
|  377             } |  | 
|  378             /* add to list */ |  | 
|  379             rv = CERT_AddExtensionByOID(exthandle, &ext->id, &ext->value, |  | 
|  380                                         critical, PR_TRUE); |  | 
|  381             if (rv != SECSuccess) |  | 
|  382                 break; |  | 
|  383         } |  | 
|  384     } |  | 
|  385     return rv; |  | 
|  386 } |  | 
|  387  |  | 
|  388 /* |  | 
|  389  * get the value of the Netscape Certificate Type Extension |  | 
|  390  */ |  | 
|  391 SECStatus |  | 
|  392 CERT_FindBitStringExtension(CERTCertExtension **extensions, int tag, |  | 
|  393                             SECItem *retItem) |  | 
|  394 { |  | 
|  395     SECItem wrapperItem, tmpItem = { siBuffer, 0 }; |  | 
|  396     SECStatus rv; |  | 
|  397     PLArenaPool *arena = NULL; |  | 
|  398  |  | 
|  399     wrapperItem.data = NULL; |  | 
|  400     tmpItem.data = NULL; |  | 
|  401  |  | 
|  402     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |  | 
|  403  |  | 
|  404     if (!arena) { |  | 
|  405         return (SECFailure); |  | 
|  406     } |  | 
|  407  |  | 
|  408     rv = cert_FindExtension(extensions, tag, &wrapperItem); |  | 
|  409     if (rv != SECSuccess) { |  | 
|  410         goto loser; |  | 
|  411     } |  | 
|  412  |  | 
|  413     rv = SEC_QuickDERDecodeItem( |  | 
|  414         arena, &tmpItem, SEC_ASN1_GET(SEC_BitStringTemplate), &wrapperItem); |  | 
|  415  |  | 
|  416     if (rv != SECSuccess) { |  | 
|  417         goto loser; |  | 
|  418     } |  | 
|  419  |  | 
|  420     retItem->data = (unsigned char *)PORT_Alloc((tmpItem.len + 7) >> 3); |  | 
|  421     if (retItem->data == NULL) { |  | 
|  422         goto loser; |  | 
|  423     } |  | 
|  424  |  | 
|  425     PORT_Memcpy(retItem->data, tmpItem.data, (tmpItem.len + 7) >> 3); |  | 
|  426     retItem->len = tmpItem.len; |  | 
|  427  |  | 
|  428     rv = SECSuccess; |  | 
|  429     goto done; |  | 
|  430  |  | 
|  431 loser: |  | 
|  432     rv = SECFailure; |  | 
|  433  |  | 
|  434 done: |  | 
|  435     if (arena) { |  | 
|  436         PORT_FreeArena(arena, PR_FALSE); |  | 
|  437     } |  | 
|  438  |  | 
|  439     if (wrapperItem.data) { |  | 
|  440         PORT_Free(wrapperItem.data); |  | 
|  441     } |  | 
|  442  |  | 
|  443     return (rv); |  | 
|  444 } |  | 
|  445  |  | 
|  446 PRBool |  | 
|  447 cert_HasCriticalExtension(CERTCertExtension **extensions) |  | 
|  448 { |  | 
|  449     CERTCertExtension **exts; |  | 
|  450     CERTCertExtension *ext = NULL; |  | 
|  451     PRBool hasCriticalExten = PR_FALSE; |  | 
|  452  |  | 
|  453     exts = extensions; |  | 
|  454  |  | 
|  455     if (exts) { |  | 
|  456         while (*exts) { |  | 
|  457             ext = *exts; |  | 
|  458             /* If the criticality is omitted, it's non-critical */ |  | 
|  459             if (ext->critical.data && ext->critical.data[0] == 0xff) { |  | 
|  460                 hasCriticalExten = PR_TRUE; |  | 
|  461                 break; |  | 
|  462             } |  | 
|  463             exts++; |  | 
|  464         } |  | 
|  465     } |  | 
|  466     return (hasCriticalExten); |  | 
|  467 } |  | 
|  468  |  | 
|  469 PRBool |  | 
|  470 cert_HasUnknownCriticalExten(CERTCertExtension **extensions) |  | 
|  471 { |  | 
|  472     CERTCertExtension **exts; |  | 
|  473     CERTCertExtension *ext = NULL; |  | 
|  474     PRBool hasUnknownCriticalExten = PR_FALSE; |  | 
|  475  |  | 
|  476     exts = extensions; |  | 
|  477  |  | 
|  478     if (exts) { |  | 
|  479         while (*exts) { |  | 
|  480             ext = *exts; |  | 
|  481             /* If the criticality is omitted, it's non-critical. |  | 
|  482                If an extension is critical, make sure that we know |  | 
|  483                how to process the extension. |  | 
|  484              */ |  | 
|  485             if (ext->critical.data && ext->critical.data[0] == 0xff) { |  | 
|  486                 if (SECOID_KnownCertExtenOID(&ext->id) == PR_FALSE) { |  | 
|  487                     hasUnknownCriticalExten = PR_TRUE; |  | 
|  488                     break; |  | 
|  489                 } |  | 
|  490             } |  | 
|  491             exts++; |  | 
|  492         } |  | 
|  493     } |  | 
|  494     return (hasUnknownCriticalExten); |  | 
|  495 } |  | 
| OLD | NEW |