Back to index

lightning-sunbird  0.9+nobinonly
certread.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 #include "nssrenam.h"
00038 #include "cert.h"
00039 #include "secpkcs7.h"
00040 #include "base64.h"
00041 #include "secitem.h"
00042 #include "secder.h"
00043 #include "secasn1.h"
00044 #include "secoid.h"
00045 #include "secerr.h"
00046 
00047 SEC_ASN1_MKSUB(SEC_AnyTemplate)
00048 
00049 SECStatus
00050 SEC_ReadPKCS7Certs(SECItem *pkcs7Item, CERTImportCertificateFunc f, void *arg)
00051 {
00052     SEC_PKCS7ContentInfo *contentInfo = NULL;
00053     SECStatus rv;
00054     SECItem **certs;
00055     int count;
00056 
00057     contentInfo = SEC_PKCS7DecodeItem(pkcs7Item, NULL, NULL, NULL, NULL, NULL, 
00058                                   NULL, NULL);
00059     if ( contentInfo == NULL ) {
00060        goto loser;
00061     }
00062 
00063     if ( SEC_PKCS7ContentType (contentInfo) != SEC_OID_PKCS7_SIGNED_DATA ) {
00064        goto loser;
00065     }
00066 
00067     certs = contentInfo->content.signedData->rawCerts;
00068     if ( certs ) {
00069        count = 0;
00070        
00071        while ( *certs ) {
00072            count++;
00073            certs++;
00074        }
00075        rv = (* f)(arg, contentInfo->content.signedData->rawCerts, count);
00076     }
00077     
00078     rv = SECSuccess;
00079     
00080     goto done;
00081 loser:
00082     rv = SECFailure;
00083     
00084 done:
00085     if ( contentInfo ) {
00086        SEC_PKCS7DestroyContentInfo(contentInfo);
00087     }
00088 
00089     return(rv);
00090 }
00091 
00092 const SEC_ASN1Template SEC_CertSequenceTemplate[] = {
00093     { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) }
00094 };
00095 
00096 SECStatus
00097 SEC_ReadCertSequence(SECItem *certsItem, CERTImportCertificateFunc f, void *arg)
00098 {
00099     SECStatus rv;
00100     SECItem **certs;
00101     int count;
00102     SECItem **rawCerts = NULL;
00103     PRArenaPool *arena;
00104     SEC_PKCS7ContentInfo *contentInfo = NULL;
00105 
00106     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00107     if (arena == NULL) {
00108        return SECFailure;
00109     }
00110 
00111     contentInfo = SEC_PKCS7DecodeItem(certsItem, NULL, NULL, NULL, NULL, NULL, 
00112                                   NULL, NULL);
00113     if ( contentInfo == NULL ) {
00114        goto loser;
00115     }
00116 
00117     if ( SEC_PKCS7ContentType (contentInfo) != SEC_OID_NS_TYPE_CERT_SEQUENCE ) {
00118        goto loser;
00119     }
00120 
00121 
00122     rv = SEC_QuickDERDecodeItem(arena, &rawCerts, SEC_CertSequenceTemplate,
00123                   contentInfo->content.data);
00124 
00125     if (rv != SECSuccess) {
00126        goto loser;
00127     }
00128 
00129     certs = rawCerts;
00130     if ( certs ) {
00131        count = 0;
00132        
00133        while ( *certs ) {
00134            count++;
00135            certs++;
00136        }
00137        rv = (* f)(arg, rawCerts, count);
00138     }
00139     
00140     rv = SECSuccess;
00141     
00142     goto done;
00143 loser:
00144     rv = SECFailure;
00145     
00146 done:
00147     if ( contentInfo ) {
00148        SEC_PKCS7DestroyContentInfo(contentInfo);
00149     }
00150 
00151     if ( arena ) {
00152        PORT_FreeArena(arena, PR_FALSE);
00153     }
00154     
00155     return(rv);
00156 }
00157 
00158 CERTCertificate *
00159 CERT_ConvertAndDecodeCertificate(char *certstr)
00160 {
00161     CERTCertificate *cert;
00162     SECStatus rv;
00163     SECItem der;
00164 
00165     rv = ATOB_ConvertAsciiToItem(&der, certstr);
00166     if (rv != SECSuccess)
00167        return NULL;
00168 
00169     cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), 
00170                                    &der, NULL, PR_FALSE, PR_TRUE);
00171 
00172     PORT_Free(der.data);
00173     return cert;
00174 }
00175 
00176 #define NS_CERT_HEADER "-----BEGIN CERTIFICATE-----"
00177 #define NS_CERT_TRAILER "-----END CERTIFICATE-----"
00178 
00179 #define CERTIFICATE_TYPE_STRING "certificate"
00180 #define CERTIFICATE_TYPE_LEN (sizeof(CERTIFICATE_TYPE_STRING)-1)
00181 
00182 CERTPackageType
00183 CERT_CertPackageType(SECItem *package, SECItem *certitem)
00184 {
00185     unsigned char *cp;
00186     unsigned int seqLen, seqLenLen;
00187     SECItem oiditem;
00188     SECOidData *oiddata;
00189     CERTPackageType type = certPackageNone;
00190     
00191     cp = package->data;
00192 
00193     /* is a DER encoded certificate of some type? */
00194     if ( ( *cp  & 0x1f ) == SEC_ASN1_SEQUENCE ) {
00195        cp++;
00196        
00197        if ( *cp & 0x80) {
00198            /* Multibyte length */
00199            seqLenLen = cp[0] & 0x7f;
00200            
00201            switch (seqLenLen) {
00202              case 4:
00203               seqLen = ((unsigned long)cp[1]<<24) |
00204                   ((unsigned long)cp[2]<<16) | (cp[3]<<8) | cp[4];
00205               break;
00206              case 3:
00207               seqLen = ((unsigned long)cp[1]<<16) | (cp[2]<<8) | cp[3];
00208               break;
00209              case 2:
00210               seqLen = (cp[1]<<8) | cp[2];
00211               break;
00212              case 1:
00213               seqLen = cp[1];
00214               break;
00215              default:
00216               /* indefinite length */
00217               seqLen = 0;
00218            }
00219            cp += ( seqLenLen + 1 );
00220 
00221        } else {
00222            seqLenLen = 0;
00223            seqLen = *cp;
00224            cp++;
00225        }
00226 
00227        /* check entire length if definite length */
00228        if ( seqLen || seqLenLen ) {
00229            if ( package->len != ( seqLen + seqLenLen + 2 ) ) {
00230               /* not a DER package */
00231               return(type);
00232            }
00233        }
00234        
00235        /* check the type string */
00236        /* netscape wrapped DER cert */
00237        if ( ( cp[0] == SEC_ASN1_OCTET_STRING ) &&
00238            ( cp[1] == CERTIFICATE_TYPE_LEN ) &&
00239            ( PORT_Strcmp((char *)&cp[2], CERTIFICATE_TYPE_STRING) ) ) {
00240            
00241            cp += ( CERTIFICATE_TYPE_LEN + 2 );
00242 
00243            /* it had better be a certificate by now!! */
00244            if ( certitem ) {
00245               certitem->data = cp;
00246               certitem->len = package->len -
00247                   ( cp - (unsigned char *)package->data );
00248            }
00249            type = certPackageNSCertWrap;
00250            
00251        } else if ( cp[0] == SEC_ASN1_OBJECT_ID ) {
00252            /* XXX - assume DER encoding of OID len!! */
00253            oiditem.len = cp[1];
00254            oiditem.data = (unsigned char *)&cp[2];
00255            oiddata = SECOID_FindOID(&oiditem);
00256            if ( oiddata == NULL ) {
00257               /* failure */
00258               return(type);
00259            }
00260 
00261            if ( certitem ) {
00262               certitem->data = package->data;
00263               certitem->len = package->len;
00264            }
00265            
00266            switch ( oiddata->offset ) {
00267              case SEC_OID_PKCS7_SIGNED_DATA:
00268               type = certPackagePKCS7;
00269               break;
00270              case SEC_OID_NS_TYPE_CERT_SEQUENCE:
00271               type = certPackageNSCertSeq;
00272               break;
00273              default:
00274               break;
00275            }
00276            
00277        } else {
00278            /* it had better be a certificate by now!! */
00279            if ( certitem ) {
00280               certitem->data = package->data;
00281               certitem->len = package->len;
00282            }
00283            
00284            type = certPackageCert;
00285        }
00286     }
00287 
00288     return(type);
00289 }
00290 
00291 /*
00292  * read an old style ascii or binary certificate chain
00293  */
00294 SECStatus
00295 CERT_DecodeCertPackage(char *certbuf,
00296                      int certlen,
00297                      CERTImportCertificateFunc f,
00298                      void *arg)
00299 {
00300     unsigned char *cp;
00301     unsigned char *bincert = NULL;
00302     char *         ascCert = NULL;
00303     SECStatus      rv;
00304     
00305     if ( certbuf == NULL ) {
00306        return(SECFailure);
00307     }
00308     
00309     cp = (unsigned char *)certbuf;
00310 
00311     /* is a DER encoded certificate of some type? */
00312     if ( ( *cp  & 0x1f ) == SEC_ASN1_SEQUENCE ) {
00313        SECItem certitem;
00314        SECItem *pcertitem = &certitem;
00315        int seqLen, seqLenLen;
00316 
00317        cp++;
00318        
00319        if ( *cp & 0x80) {
00320            /* Multibyte length */
00321            seqLenLen = cp[0] & 0x7f;
00322            
00323            switch (seqLenLen) {
00324              case 4:
00325               seqLen = ((unsigned long)cp[1]<<24) |
00326                   ((unsigned long)cp[2]<<16) | (cp[3]<<8) | cp[4];
00327               break;
00328              case 3:
00329               seqLen = ((unsigned long)cp[1]<<16) | (cp[2]<<8) | cp[3];
00330               break;
00331              case 2:
00332               seqLen = (cp[1]<<8) | cp[2];
00333               break;
00334              case 1:
00335               seqLen = cp[1];
00336               break;
00337              default:
00338               /* indefinite length */
00339               seqLen = 0;
00340            }
00341            cp += ( seqLenLen + 1 );
00342 
00343        } else {
00344            seqLenLen = 0;
00345            seqLen = *cp;
00346            cp++;
00347        }
00348 
00349        /* check entire length if definite length */
00350        if ( seqLen || seqLenLen ) {
00351            if ( certlen != ( seqLen + seqLenLen + 2 ) ) {
00352               if (certlen > ( seqLen + seqLenLen + 2 ))
00353                   PORT_SetError(SEC_ERROR_EXTRA_INPUT);
00354               else 
00355                   PORT_SetError(SEC_ERROR_INPUT_LEN);
00356               goto notder;
00357            }
00358        }
00359        
00360        /* check the type string */
00361        /* netscape wrapped DER cert */
00362        if ( ( cp[0] == SEC_ASN1_OCTET_STRING ) &&
00363            ( cp[1] == CERTIFICATE_TYPE_LEN ) &&
00364            ( PORT_Strcmp((char *)&cp[2], CERTIFICATE_TYPE_STRING) ) ) {
00365            
00366            cp += ( CERTIFICATE_TYPE_LEN + 2 );
00367 
00368            /* it had better be a certificate by now!! */
00369            certitem.data = cp;
00370            certitem.len = certlen - ( cp - (unsigned char *)certbuf );
00371            
00372            rv = (* f)(arg, &pcertitem, 1);
00373            
00374            return(rv);
00375        } else if ( cp[0] == SEC_ASN1_OBJECT_ID ) {
00376            SECOidData *oiddata;
00377            SECItem oiditem;
00378            /* XXX - assume DER encoding of OID len!! */
00379            oiditem.len = cp[1];
00380            oiditem.data = (unsigned char *)&cp[2];
00381            oiddata = SECOID_FindOID(&oiditem);
00382            if ( oiddata == NULL ) {
00383               return(SECFailure);
00384            }
00385 
00386            certitem.data = (unsigned char*)certbuf;
00387            certitem.len = certlen;
00388            
00389            switch ( oiddata->offset ) {
00390              case SEC_OID_PKCS7_SIGNED_DATA:
00391               return(SEC_ReadPKCS7Certs(&certitem, f, arg));
00392               break;
00393              case SEC_OID_NS_TYPE_CERT_SEQUENCE:
00394               return(SEC_ReadCertSequence(&certitem, f, arg));
00395               break;
00396              default:
00397               break;
00398            }
00399            
00400        } else {
00401            /* it had better be a certificate by now!! */
00402            certitem.data = (unsigned char*)certbuf;
00403            certitem.len = certlen;
00404            
00405            rv = (* f)(arg, &pcertitem, 1);
00406            return(rv);
00407        }
00408     }
00409 
00410     /* now look for a netscape base64 ascii encoded cert */
00411 notder:
00412   {
00413     unsigned char *certbegin = NULL; 
00414     unsigned char *certend   = NULL;
00415     char          *pc;
00416     int cl;
00417 
00418     /* Convert the ASCII data into a nul-terminated string */
00419     ascCert = (char *)PORT_Alloc(certlen + 1);
00420     if (!ascCert) {
00421         rv = SECFailure;
00422        goto loser;
00423     }
00424 
00425     PORT_Memcpy(ascCert, certbuf, certlen);
00426     ascCert[certlen] = '\0';
00427 
00428     pc = PORT_Strchr(ascCert, '\n');  /* find an EOL */
00429     if (!pc) { /* maybe this is a MAC file */
00430        pc = ascCert;
00431        while (*pc && NULL != (pc = PORT_Strchr(pc, '\r'))) {
00432            *pc++ = '\n';
00433        }
00434     }
00435 
00436     cp = (unsigned char *)ascCert;
00437     cl = certlen;
00438 
00439     /* find the beginning marker */
00440     while ( cl > sizeof(NS_CERT_HEADER) ) {
00441        if ( !PORT_Strncasecmp((char *)cp, NS_CERT_HEADER,
00442                           sizeof(NS_CERT_HEADER)-1) ) {
00443            cp = cp + sizeof(NS_CERT_HEADER);
00444            certbegin = cp;
00445            break;
00446        }
00447        
00448        /* skip to next eol */
00449        do {
00450            cp++;
00451            cl--;
00452        } while ( ( *cp != '\n') && cl );
00453 
00454        /* skip all blank lines */
00455        while ( ( *cp == '\n') && cl ) {
00456            cp++;
00457            cl--;
00458        }
00459     }
00460 
00461     if ( certbegin ) {
00462        /* find the ending marker */
00463        while ( cl > sizeof(NS_CERT_TRAILER) ) {
00464            if ( !PORT_Strncasecmp((char *)cp, NS_CERT_TRAILER,
00465                              sizeof(NS_CERT_TRAILER)-1) ) {
00466               certend = (unsigned char *)cp;
00467               break;
00468            }
00469 
00470            /* skip to next eol */
00471            do {
00472               cp++;
00473               cl--;
00474            } while ( ( *cp != '\n') && cl );
00475 
00476            /* skip all blank lines */
00477            while ( ( *cp == '\n') && cl ) {
00478               cp++;
00479               cl--;
00480            }
00481        }
00482     }
00483 
00484     if ( certbegin && certend ) {
00485        unsigned int binLen;
00486 
00487        *certend = 0;
00488        /* convert to binary */
00489        bincert = ATOB_AsciiToData(certbegin, &binLen);
00490        if (!bincert) {
00491            rv = SECFailure;
00492            goto loser;
00493        }
00494 
00495        /* now recurse to decode the binary */
00496        rv = CERT_DecodeCertPackage((char *)bincert, binLen, f, arg);
00497        
00498     } else {
00499        rv = SECFailure;
00500     }
00501   }
00502 
00503 loser:
00504 
00505     if ( bincert ) {
00506        PORT_Free(bincert);
00507     }
00508 
00509     if ( ascCert ) {
00510        PORT_Free(ascCert);
00511     }
00512 
00513     return(rv);
00514 }
00515 
00516 typedef struct {
00517     PRArenaPool *arena;
00518     SECItem cert;
00519 } collect_args;
00520 
00521 static SECStatus
00522 collect_certs(void *arg, SECItem **certs, int numcerts)
00523 {
00524     SECStatus rv;
00525     collect_args *collectArgs;
00526     
00527     collectArgs = (collect_args *)arg;
00528     
00529     rv = SECITEM_CopyItem(collectArgs->arena, &collectArgs->cert, *certs);
00530 
00531     return(rv);
00532 }
00533 
00534 
00535 /*
00536  * read an old style ascii or binary certificate
00537  */
00538 CERTCertificate *
00539 CERT_DecodeCertFromPackage(char *certbuf, int certlen)
00540 {
00541     collect_args collectArgs;
00542     SECStatus rv;
00543     CERTCertificate *cert = NULL;
00544     
00545     collectArgs.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00546     
00547     rv = CERT_DecodeCertPackage(certbuf, certlen, collect_certs,
00548                             (void *)&collectArgs);
00549     if ( rv == SECSuccess ) {
00550        cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
00551                                       &collectArgs.cert, NULL, 
00552                                       PR_FALSE, PR_TRUE);
00553     }
00554     
00555     PORT_FreeArena(collectArgs.arena, PR_FALSE);
00556     
00557     return(cert);
00558 }