Back to index

lightning-sunbird  0.9+nobinonly
certv3.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  * Code for dealing with X509.V3 extensions.
00039  *
00040  * $Id: certv3.c,v 1.8 2004/04/25 15:03:03 gerv%gerv.net Exp $
00041  */
00042 
00043 #include "cert.h"
00044 #include "secitem.h"
00045 #include "secoid.h"
00046 #include "secder.h"
00047 #include "secasn1.h"
00048 #include "certxutl.h"
00049 #include "secerr.h"
00050 
00051 SECStatus
00052 CERT_FindCertExtensionByOID(CERTCertificate *cert, SECItem *oid,
00053                          SECItem *value)
00054 {
00055     return (cert_FindExtensionByOID (cert->extensions, oid, value));
00056 }
00057     
00058 
00059 SECStatus
00060 CERT_FindCertExtension(CERTCertificate *cert, int tag, SECItem *value)
00061 {
00062     return (cert_FindExtension (cert->extensions, tag, value));
00063 }
00064 
00065 static void
00066 SetExts(void *object, CERTCertExtension **exts)
00067 {
00068     CERTCertificate *cert = (CERTCertificate *)object;
00069 
00070     cert->extensions = exts;
00071     DER_SetUInteger (cert->arena, &(cert->version), SEC_CERTIFICATE_VERSION_3);
00072 }
00073 
00074 void *
00075 CERT_StartCertExtensions(CERTCertificate *cert)
00076 {
00077     return (cert_StartExtensions ((void *)cert, cert->arena, SetExts));
00078 }
00079 
00080 /* find the given extension in the certificate of the Issuer of 'cert' */
00081 SECStatus
00082 CERT_FindIssuerCertExtension(CERTCertificate *cert, int tag, SECItem *value)
00083 {
00084     CERTCertificate *issuercert;
00085     SECStatus rv;
00086 
00087     issuercert = CERT_FindCertByName(cert->dbhandle, &cert->derIssuer);
00088     if ( issuercert ) {
00089        rv = cert_FindExtension(issuercert->extensions, tag, value);
00090        CERT_DestroyCertificate(issuercert);
00091     } else {
00092        rv = SECFailure;
00093     }
00094     
00095     return(rv);
00096 }
00097 
00098 /* find a URL extension in the cert or its CA
00099  * apply the base URL string if it exists
00100  */
00101 char *
00102 CERT_FindCertURLExtension(CERTCertificate *cert, int tag, int catag)
00103 {
00104     SECStatus rv;
00105     SECItem urlitem;
00106     SECItem baseitem;
00107     SECItem urlstringitem = {siBuffer,0};
00108     SECItem basestringitem = {siBuffer,0};
00109     PRArenaPool *arena = NULL;
00110     PRBool hasbase;
00111     char *urlstring;
00112     char *str;
00113     int len;
00114     unsigned int i;
00115     
00116     urlstring = NULL;
00117 
00118     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00119     if ( ! arena ) {
00120        goto loser;
00121     }
00122     
00123     hasbase = PR_FALSE;
00124     urlitem.data = NULL;
00125     baseitem.data = NULL;
00126     
00127     rv = cert_FindExtension(cert->extensions, tag, &urlitem);
00128     if ( rv == SECSuccess ) {
00129        rv = cert_FindExtension(cert->extensions, SEC_OID_NS_CERT_EXT_BASE_URL,
00130                                &baseitem);
00131        if ( rv == SECSuccess ) {
00132            hasbase = PR_TRUE;
00133        }
00134        
00135     } else if ( catag ) {
00136        /* if the cert doesn't have the extensions, see if the issuer does */
00137        rv = CERT_FindIssuerCertExtension(cert, catag, &urlitem);
00138        if ( rv != SECSuccess ) {
00139            goto loser;
00140        }          
00141        rv = CERT_FindIssuerCertExtension(cert, SEC_OID_NS_CERT_EXT_BASE_URL,
00142                                     &baseitem);
00143        if ( rv == SECSuccess ) {
00144            hasbase = PR_TRUE;
00145        }
00146     } else {
00147        goto loser;
00148     }
00149 
00150     rv = SEC_QuickDERDecodeItem(arena, &urlstringitem, SEC_IA5StringTemplate, 
00151                          &urlitem);
00152 
00153     if ( rv != SECSuccess ) {
00154        goto loser;
00155     }
00156     if ( hasbase ) {
00157        rv = SEC_QuickDERDecodeItem(arena, &basestringitem, SEC_IA5StringTemplate,
00158                             &baseitem);
00159 
00160        if ( rv != SECSuccess ) {
00161            goto loser;
00162        }
00163     }
00164     
00165     len = urlstringitem.len + ( hasbase ? basestringitem.len : 0 ) + 1;
00166     
00167     str = urlstring = (char *)PORT_Alloc(len);
00168     if ( urlstring == NULL ) {
00169        goto loser;
00170     }
00171     
00172     /* copy the URL base first */
00173     if ( hasbase ) {
00174 
00175        /* if the urlstring has a : in it, then we assume it is an absolute
00176         * URL, and will not get the base string pre-pended
00177         */
00178        for ( i = 0; i < urlstringitem.len; i++ ) {
00179            if ( urlstringitem.data[i] == ':' ) {
00180               goto nobase;
00181            }
00182        }
00183        
00184        PORT_Memcpy(str, basestringitem.data, basestringitem.len);
00185        str += basestringitem.len;
00186        
00187     }
00188 
00189 nobase:
00190     /* copy the rest (or all) of the URL */
00191     PORT_Memcpy(str, urlstringitem.data, urlstringitem.len);
00192     str += urlstringitem.len;
00193     
00194     *str = '\0';
00195     goto done;
00196     
00197 loser:
00198     if ( urlstring ) {
00199        PORT_Free(urlstring);
00200     }
00201     
00202     urlstring = NULL;
00203 done:
00204     if ( arena ) {
00205        PORT_FreeArena(arena, PR_FALSE);
00206     }
00207     if ( baseitem.data ) {
00208        PORT_Free(baseitem.data);
00209     }
00210     if ( urlitem.data ) {
00211        PORT_Free(urlitem.data);
00212     }
00213 
00214     return(urlstring);
00215 }
00216 
00217 /*
00218  * get the value of the Netscape Certificate Type Extension
00219  */
00220 SECStatus
00221 CERT_FindNSCertTypeExtension(CERTCertificate *cert, SECItem *retItem)
00222 {
00223 
00224     return (CERT_FindBitStringExtension
00225            (cert->extensions, SEC_OID_NS_CERT_EXT_CERT_TYPE, retItem));    
00226 }
00227 
00228 
00229 /*
00230  * get the value of a string type extension
00231  */
00232 char *
00233 CERT_FindNSStringExtension(CERTCertificate *cert, int oidtag)
00234 {
00235     SECItem wrapperItem, tmpItem = {siBuffer,0};
00236     SECStatus rv;
00237     PRArenaPool *arena = NULL;
00238     char *retstring = NULL;
00239     
00240     wrapperItem.data = NULL;
00241     tmpItem.data = NULL;
00242     
00243     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00244     
00245     if ( ! arena ) {
00246        goto loser;
00247     }
00248     
00249     rv = cert_FindExtension(cert->extensions, oidtag,
00250                             &wrapperItem);
00251     if ( rv != SECSuccess ) {
00252        goto loser;
00253     }
00254 
00255     rv = SEC_QuickDERDecodeItem(arena, &tmpItem, SEC_IA5StringTemplate, 
00256                          &wrapperItem);
00257 
00258     if ( rv != SECSuccess ) {
00259        goto loser;
00260     }
00261 
00262     retstring = (char *)PORT_Alloc(tmpItem.len + 1 );
00263     if ( retstring == NULL ) {
00264        goto loser;
00265     }
00266     
00267     PORT_Memcpy(retstring, tmpItem.data, tmpItem.len);
00268     retstring[tmpItem.len] = '\0';
00269 
00270 loser:
00271     if ( arena ) {
00272        PORT_FreeArena(arena, PR_FALSE);
00273     }
00274     
00275     if ( wrapperItem.data ) {
00276        PORT_Free(wrapperItem.data);
00277     }
00278 
00279     return(retstring);
00280 }
00281 
00282 /*
00283  * get the value of the X.509 v3 Key Usage Extension
00284  */
00285 SECStatus
00286 CERT_FindKeyUsageExtension(CERTCertificate *cert, SECItem *retItem)
00287 {
00288 
00289     return (CERT_FindBitStringExtension(cert->extensions,
00290                                    SEC_OID_X509_KEY_USAGE, retItem));    
00291 }
00292 
00293 /*
00294  * get the value of the X.509 v3 Key Usage Extension
00295  */
00296 SECStatus
00297 CERT_FindSubjectKeyIDExtension(CERTCertificate *cert, SECItem *retItem)
00298 {
00299 
00300     SECStatus rv;
00301     SECItem encodedValue = {siBuffer, NULL, 0 };
00302     SECItem decodedValue = {siBuffer, NULL, 0 };
00303 
00304     rv = cert_FindExtension
00305         (cert->extensions, SEC_OID_X509_SUBJECT_KEY_ID, &encodedValue);
00306     if (rv == SECSuccess) {
00307        PLArenaPool * tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00308        if (tmpArena) {
00309            rv = SEC_QuickDERDecodeItem(tmpArena, &decodedValue, 
00310                                        SEC_OctetStringTemplate, 
00311                                    &encodedValue);
00312            if (rv == SECSuccess) {
00313                rv = SECITEM_CopyItem(NULL, retItem, &decodedValue);
00314            }
00315            PORT_FreeArena(tmpArena, PR_FALSE);
00316        } else {
00317            rv = SECFailure;
00318        }
00319     }
00320     SECITEM_FreeItem(&encodedValue, PR_FALSE);
00321     return rv;
00322 }
00323 
00324 SECStatus
00325 CERT_FindBasicConstraintExten(CERTCertificate *cert,
00326                            CERTBasicConstraints *value)
00327 {
00328     SECItem encodedExtenValue;
00329     SECStatus rv;
00330 
00331     encodedExtenValue.data = NULL;
00332     encodedExtenValue.len = 0;
00333 
00334     rv = cert_FindExtension(cert->extensions, SEC_OID_X509_BASIC_CONSTRAINTS,
00335                          &encodedExtenValue);
00336     if ( rv != SECSuccess ) {
00337        return (rv);
00338     }
00339 
00340     rv = CERT_DecodeBasicConstraintValue (value, &encodedExtenValue);
00341     
00342     /* free the raw extension data */
00343     PORT_Free(encodedExtenValue.data);
00344     encodedExtenValue.data = NULL;
00345     
00346     return(rv);
00347 }
00348 
00349 CERTAuthKeyID *
00350 CERT_FindAuthKeyIDExten (PRArenaPool *arena, CERTCertificate *cert)
00351 {
00352     SECItem encodedExtenValue;
00353     SECStatus rv;
00354     CERTAuthKeyID *ret;
00355     
00356     encodedExtenValue.data = NULL;
00357     encodedExtenValue.len = 0;
00358 
00359     rv = cert_FindExtension(cert->extensions, SEC_OID_X509_AUTH_KEY_ID,
00360                          &encodedExtenValue);
00361     if ( rv != SECSuccess ) {
00362        return (NULL);
00363     }
00364 
00365     ret = CERT_DecodeAuthKeyID (arena, &encodedExtenValue);
00366 
00367     PORT_Free(encodedExtenValue.data);
00368     encodedExtenValue.data = NULL;
00369     
00370     return(ret);
00371 }
00372 
00373 SECStatus
00374 CERT_CheckCertUsage(CERTCertificate *cert, unsigned char usage)
00375 {
00376     SECItem keyUsage;
00377     SECStatus rv;
00378 
00379     /* There is no extension, v1 or v2 certificate */
00380     if (cert->extensions == NULL) {
00381        return (SECSuccess);
00382     }
00383     
00384     keyUsage.data = NULL;
00385 
00386     /* This code formerly ignored the Key Usage extension if it was
00387     ** marked non-critical.  That was wrong.  Since we do understand it,
00388     ** we are obligated to honor it, whether or not it is critical.
00389     */
00390     rv = CERT_FindKeyUsageExtension(cert, &keyUsage);
00391     if (rv == SECFailure) {
00392         rv = (PORT_GetError () == SEC_ERROR_EXTENSION_NOT_FOUND) ?
00393            SECSuccess : SECFailure;
00394     } else if (!(keyUsage.data[0] & usage)) {
00395        PORT_SetError (SEC_ERROR_CERT_USAGES_INVALID);
00396        rv = SECFailure;
00397     }
00398     PORT_Free (keyUsage.data);
00399     return (rv);
00400 }