Back to index

lightning-sunbird  0.9+nobinonly
certxutl.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * Certificate Extensions handling code
00039  *
00040  */
00041 
00042 #include "cert.h"
00043 #include "secitem.h"
00044 #include "secoid.h"
00045 #include "secder.h"
00046 #include "secasn1.h"
00047 #include "certxutl.h"
00048 #include "secerr.h"
00049 
00050 #ifdef OLD
00051 #include "ocspti.h"  /* XXX a better extensions interface would not
00052                       * require knowledge of data structures of callers */
00053 #endif
00054 
00055 static CERTCertExtension *
00056 GetExtension (CERTCertExtension **extensions, SECItem *oid)
00057 {
00058     CERTCertExtension **exts;
00059     CERTCertExtension *ext = NULL;
00060     SECComparison comp;
00061 
00062     exts = extensions;
00063     
00064     if (exts) {
00065        while ( *exts ) {
00066            ext = *exts;
00067            comp = SECITEM_CompareItem(oid, &ext->id);
00068            if ( comp == SECEqual ) 
00069               break;
00070 
00071            exts++;
00072        }
00073        return (*exts ? ext : NULL);
00074     }
00075     return (NULL);
00076 }
00077 
00078 SECStatus
00079 cert_FindExtensionByOID (CERTCertExtension **extensions, SECItem *oid, SECItem *value)
00080 {
00081     CERTCertExtension *ext;
00082     SECStatus rv = SECSuccess;
00083     
00084     ext = GetExtension (extensions, oid);
00085     if (ext == NULL) {
00086        PORT_SetError (SEC_ERROR_EXTENSION_NOT_FOUND);
00087        return (SECFailure);
00088     }
00089     if (value)
00090        rv = SECITEM_CopyItem(NULL, value, &ext->value);
00091     return (rv);
00092 }
00093     
00094 
00095 SECStatus
00096 CERT_GetExtenCriticality (CERTCertExtension **extensions, int tag, PRBool *isCritical)
00097 {
00098     CERTCertExtension *ext;
00099     SECOidData *oid;
00100 
00101     if (!isCritical)
00102        return (SECSuccess);
00103     
00104     /* find the extension in the extensions list */
00105     oid = SECOID_FindOIDByTag((SECOidTag)tag);
00106     if ( !oid ) {
00107        return(SECFailure);
00108     }
00109     ext = GetExtension (extensions, &oid->oid);
00110     if (ext == NULL) {
00111        PORT_SetError (SEC_ERROR_EXTENSION_NOT_FOUND);
00112        return (SECFailure);
00113     }
00114 
00115     /* If the criticality is omitted, then it is false by default.
00116        ex->critical.data is NULL */
00117     if (ext->critical.data == NULL)
00118        *isCritical = PR_FALSE;
00119     else
00120        *isCritical = (ext->critical.data[0] == 0xff) ? PR_TRUE : PR_FALSE;
00121     return (SECSuccess);    
00122 }
00123 
00124 SECStatus
00125 cert_FindExtension(CERTCertExtension **extensions, int tag, SECItem *value)
00126 {
00127     SECOidData *oid;
00128     
00129     oid = SECOID_FindOIDByTag((SECOidTag)tag);
00130     if ( !oid ) {
00131        return(SECFailure);
00132     }
00133 
00134     return(cert_FindExtensionByOID(extensions, &oid->oid, value));
00135 }
00136 
00137 
00138 typedef struct _extNode {
00139     struct _extNode *next;
00140     CERTCertExtension *ext;
00141 } extNode;
00142 
00143 typedef struct {
00144     void (*setExts)(void *object, CERTCertExtension **exts);
00145     void *object;
00146     PRArenaPool *ownerArena;
00147     PRArenaPool *arena;
00148     extNode *head;
00149     int count;
00150 }extRec;
00151 
00152 /*
00153  * cert_StartExtensions
00154  *
00155  * NOTE: This interface changed significantly to remove knowledge
00156  *   about callers data structures (owner objects)
00157  */
00158 void *
00159 cert_StartExtensions(void *owner, PRArenaPool *ownerArena,
00160    void (*setExts)(void *object, CERTCertExtension **exts))
00161 {
00162     PRArenaPool *arena;
00163     extRec *handle;
00164 
00165     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00166     if ( !arena ) {
00167        return(0);
00168     }
00169 
00170     handle = (extRec *)PORT_ArenaAlloc(arena, sizeof(extRec));
00171     if ( !handle ) {
00172        PORT_FreeArena(arena, PR_FALSE);
00173        return(0);
00174     }
00175 
00176     handle->object = owner;
00177     handle->ownerArena = ownerArena;
00178     handle->setExts = setExts;
00179 
00180     handle->arena = arena;
00181     handle->head = 0;
00182     handle->count = 0;
00183     
00184     return(handle);
00185 }
00186 
00187 static unsigned char hextrue = 0xff;
00188 
00189 /*
00190  * Note - assumes that data pointed to by oid->data will not move
00191  */
00192 SECStatus
00193 CERT_AddExtensionByOID (void *exthandle, SECItem *oid, SECItem *value,
00194                      PRBool critical, PRBool copyData)
00195 {
00196     CERTCertExtension *ext;
00197     SECStatus rv;
00198     extNode *node;
00199     extRec *handle;
00200     
00201     handle = (extRec *)exthandle;
00202 
00203     /* allocate space for extension and list node */
00204     ext = (CERTCertExtension*)PORT_ArenaZAlloc(handle->ownerArena,
00205                                                sizeof(CERTCertExtension));
00206     if ( !ext ) {
00207        return(SECFailure);
00208     }
00209 
00210     node = (extNode*)PORT_ArenaAlloc(handle->arena, sizeof(extNode));
00211     if ( !node ) {
00212        return(SECFailure);
00213     }
00214 
00215     /* add to list */
00216     node->next = handle->head;
00217     handle->head = node;
00218    
00219     /* point to ext struct */
00220     node->ext = ext;
00221     
00222     /* the object ID of the extension */
00223     ext->id = *oid;
00224     
00225     /* set critical field */
00226     if ( critical ) {
00227        ext->critical.data = (unsigned char*)&hextrue;
00228        ext->critical.len = 1;
00229     }
00230 
00231     /* set the value */
00232     if ( copyData ) {
00233        rv = SECITEM_CopyItem(handle->ownerArena, &ext->value, value);
00234        if ( rv ) {
00235            return(SECFailure);
00236        }
00237     } else {
00238        ext->value = *value;
00239     }
00240     
00241     handle->count++;
00242     
00243     return(SECSuccess);
00244 
00245 }
00246 
00247 SECStatus
00248 CERT_AddExtension(void *exthandle, int idtag, SECItem *value,
00249                    PRBool critical, PRBool copyData)
00250 {
00251     SECOidData *oid;
00252     
00253     oid = SECOID_FindOIDByTag((SECOidTag)idtag);
00254     if ( !oid ) {
00255        return(SECFailure);
00256     }
00257 
00258     return(CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical, copyData));
00259 }
00260 
00261 SECStatus
00262 CERT_EncodeAndAddExtension(void *exthandle, int idtag, void *value,
00263                         PRBool critical, const SEC_ASN1Template *atemplate)
00264 {
00265     extRec *handle;
00266     SECItem *encitem;
00267 
00268     handle = (extRec *)exthandle;
00269 
00270     encitem = SEC_ASN1EncodeItem(handle->ownerArena, NULL, value, atemplate);
00271     if ( encitem == NULL ) {
00272        return(SECFailure);
00273     }
00274 
00275     return CERT_AddExtension(exthandle, idtag, encitem, critical, PR_FALSE);
00276 }
00277 
00278 void
00279 PrepareBitStringForEncoding (SECItem *bitsmap, SECItem *value)
00280 {
00281   unsigned char onebyte;
00282   unsigned int i, len = 0;
00283 
00284   /* to prevent warning on some platform at compile time */ 
00285   onebyte = '\0';   
00286   /* Get the position of the right-most turn-on bit */ 
00287   for (i = 0; i < (value->len ) * 8; ++i) {
00288       if (i % 8 == 0)
00289          onebyte = value->data[i/8];
00290       if (onebyte & 0x80)
00291          len = i;            
00292       onebyte <<= 1;
00293       
00294   }
00295   bitsmap->data = value->data;
00296   /* Add one here since we work with base 1 */ 
00297   bitsmap->len = len + 1;
00298 }
00299 
00300 SECStatus
00301 CERT_EncodeAndAddBitStrExtension (void *exthandle, int idtag,
00302                               SECItem *value, PRBool critical)
00303 {
00304   SECItem bitsmap;
00305   
00306   PrepareBitStringForEncoding (&bitsmap, value);
00307   return (CERT_EncodeAndAddExtension
00308          (exthandle, idtag, &bitsmap, critical, SEC_BitStringTemplate));
00309 }
00310 
00311 SECStatus
00312 CERT_FinishExtensions(void *exthandle)
00313 {
00314     extRec *handle;
00315     extNode *node;
00316     CERTCertExtension **exts;
00317     SECStatus rv = SECFailure;
00318     
00319     handle = (extRec *)exthandle;
00320 
00321     /* allocate space for extensions array */
00322     exts = PORT_ArenaNewArray(handle->ownerArena, CERTCertExtension *,
00323                            handle->count + 1);
00324     if (exts == NULL) {
00325        goto loser;
00326     }
00327 
00328     /* put extensions in owner object and update its version number */
00329 
00330 #ifdef OLD
00331     switch (handle->type) {
00332       case CertificateExtensions:
00333        handle->owner.cert->extensions = exts;
00334        DER_SetUInteger (ownerArena, &(handle->owner.cert->version),
00335                       SEC_CERTIFICATE_VERSION_3);
00336        break;
00337       case CrlExtensions:
00338        handle->owner.crl->extensions = exts;
00339        DER_SetUInteger (ownerArena, &(handle->owner.crl->version),
00340                       SEC_CRL_VERSION_2);
00341        break;
00342       case OCSPRequestExtensions:
00343        handle->owner.request->tbsRequest->requestExtensions = exts;
00344        break;
00345       case OCSPSingleRequestExtensions:
00346        handle->owner.singleRequest->singleRequestExtensions = exts;   
00347        break;
00348       case OCSPResponseSingleExtensions:
00349        handle->owner.singleResponse->singleExtensions = exts;  
00350        break;
00351     }
00352 #endif
00353 
00354     handle->setExts(handle->object, exts);
00355        
00356     /* update the version number */
00357 
00358     /* copy each extension pointer */
00359     node = handle->head;
00360     while ( node ) {
00361        *exts = node->ext;
00362        
00363        node = node->next;
00364        exts++;
00365     }
00366 
00367     /* terminate the array of extensions */
00368     *exts = 0;
00369 
00370     rv = SECSuccess;
00371 
00372 loser:
00373     /* free working arena */
00374     PORT_FreeArena(handle->arena, PR_FALSE);
00375     return rv;
00376 }
00377 
00378 SECStatus
00379 CERT_MergeExtensions(void *exthandle, CERTCertExtension **extensions)
00380 {
00381     CERTCertExtension *ext;
00382     SECStatus rv = SECSuccess;
00383     SECOidTag tag;
00384     extNode *node;
00385     extRec *handle = exthandle;
00386     
00387     if (!exthandle || !extensions) {
00388        PORT_SetError(SEC_ERROR_INVALID_ARGS);
00389         return SECFailure;
00390     }
00391     while ((ext = *extensions++) != NULL) {
00392         tag = SECOID_FindOIDTag(&ext->id);
00393         for (node=handle->head; node != NULL; node=node->next) {
00394             if (tag == 0) {
00395                 if (SECITEM_ItemsAreEqual(&ext->id, &node->ext->id))
00396                     break;
00397             }
00398             else {
00399                 if (SECOID_FindOIDTag(&node->ext->id) == tag) {
00400                     break;
00401                 }
00402             }
00403         }
00404         if (node == NULL) {
00405             PRBool critical = (ext->critical.len != 0 &&
00406                             ext->critical.data[ext->critical.len - 1] != 0);
00407             if (critical && tag == SEC_OID_UNKNOWN) {
00408                PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
00409                rv = SECFailure;
00410                break;
00411             }
00412             /* add to list */
00413             rv = CERT_AddExtensionByOID (exthandle, &ext->id, &ext->value,
00414                                          critical, PR_TRUE);
00415             if (rv != SECSuccess)
00416                 break;
00417         }
00418     }
00419     return rv;
00420 }
00421 
00422 /*
00423  * get the value of the Netscape Certificate Type Extension
00424  */
00425 SECStatus
00426 CERT_FindBitStringExtension (CERTCertExtension **extensions, int tag,
00427                           SECItem *retItem)
00428 {
00429     SECItem wrapperItem, tmpItem = {siBuffer,0};
00430     SECStatus rv;
00431     PRArenaPool *arena = NULL;
00432     
00433     wrapperItem.data = NULL;
00434     tmpItem.data = NULL;
00435     
00436     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00437     
00438     if ( ! arena ) {
00439        return(SECFailure);
00440     }
00441     
00442     rv = cert_FindExtension(extensions, tag, &wrapperItem);
00443     if ( rv != SECSuccess ) {
00444        goto loser;
00445     }
00446 
00447     rv = SEC_QuickDERDecodeItem(arena, &tmpItem, SEC_BitStringTemplate, 
00448                          &wrapperItem);
00449 
00450     if ( rv != SECSuccess ) {
00451        goto loser;
00452     }
00453 
00454     retItem->data = (unsigned char *)PORT_Alloc( ( tmpItem.len + 7 ) >> 3 );
00455     if ( retItem->data == NULL ) {
00456        goto loser;
00457     }
00458     
00459     PORT_Memcpy(retItem->data, tmpItem.data, ( tmpItem.len + 7 ) >> 3);
00460     retItem->len = tmpItem.len;
00461     
00462     rv = SECSuccess;
00463     goto done;
00464     
00465 loser:
00466     rv = SECFailure;
00467 
00468 done:
00469     if ( arena ) {
00470        PORT_FreeArena(arena, PR_FALSE);
00471     }
00472     
00473     if ( wrapperItem.data ) {
00474        PORT_Free(wrapperItem.data);
00475     }
00476 
00477     return(rv);
00478 }
00479 
00480 PRBool
00481 cert_HasCriticalExtension (CERTCertExtension **extensions)
00482 {
00483     CERTCertExtension **exts;
00484     CERTCertExtension *ext = NULL;
00485     PRBool hasCriticalExten = PR_FALSE;
00486     
00487     exts = extensions;
00488     
00489     if (exts) {
00490        while ( *exts ) {
00491            ext = *exts;
00492            /* If the criticality is omitted, it's non-critical */
00493            if (ext->critical.data && ext->critical.data[0] == 0xff) {
00494               hasCriticalExten = PR_TRUE;
00495               break;
00496            }
00497            exts++;
00498        }
00499     }
00500     return (hasCriticalExten);
00501 }
00502 
00503 PRBool
00504 cert_HasUnknownCriticalExten (CERTCertExtension **extensions)
00505 {
00506     CERTCertExtension **exts;
00507     CERTCertExtension *ext = NULL;
00508     PRBool hasUnknownCriticalExten = PR_FALSE;
00509     
00510     exts = extensions;
00511     
00512     if (exts) {
00513        while ( *exts ) {
00514            ext = *exts;
00515            /* If the criticality is omitted, it's non-critical.
00516               If an extension is critical, make sure that we know
00517               how to process the extension.
00518              */
00519            if (ext->critical.data && ext->critical.data[0] == 0xff) {
00520               if (SECOID_KnownCertExtenOID (&ext->id) == PR_FALSE) {
00521                   hasUnknownCriticalExten = PR_TRUE;
00522                   break;
00523               }
00524            }
00525            exts++;
00526        }
00527     }
00528     return (hasUnknownCriticalExten);
00529 }