security/nss/lib/softoken/legacydb/pcertdb.c

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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/. */
     5 /*
     6  * Permanent Certificate database handling code 
     7  */
     8 #include "lowkeyti.h"
     9 #include "pcert.h"
    10 #include "mcom_db.h"
    11 #include "pcert.h"
    12 #include "secitem.h"
    13 #include "secder.h"
    15 #include "secerr.h"
    16 #include "lgdb.h"
    18 /* forward declaration */
    19 NSSLOWCERTCertificate *
    20 nsslowcert_FindCertByDERCertNoLocking(NSSLOWCERTCertDBHandle *handle, SECItem *derCert);
    21 static SECStatus
    22 nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle, 
    23 	char *emailAddr, SECItem *derSubject, SECItem *emailProfile, 
    24 							SECItem *profileTime);
    25 static SECStatus
    26 nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
    27     NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust);
    28 static SECStatus
    29 nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl, 
    30 			SECItem *crlKey, char *url, PRBool isKRL);
    32 static NSSLOWCERTCertificate *certListHead = NULL;
    33 static NSSLOWCERTTrust *trustListHead = NULL;
    34 static certDBEntryCert *entryListHead = NULL;
    35 static int certListCount = 0;
    36 static int trustListCount = 0;
    37 static int entryListCount = 0;
    38 #define MAX_CERT_LIST_COUNT 10
    39 #define MAX_TRUST_LIST_COUNT 10
    40 #define MAX_ENTRY_LIST_COUNT 10
    42 /*
    43  * the following functions are wrappers for the db library that implement
    44  * a global lock to make the database thread safe.
    45  */
    46 static PZLock *dbLock = NULL;
    47 static PZLock *certRefCountLock = NULL;
    48 static PZLock *certTrustLock = NULL;
    49 static PZLock *freeListLock = NULL;
    51 void
    52 certdb_InitDBLock(NSSLOWCERTCertDBHandle *handle)
    53 {
    54     if (dbLock == NULL) {
    55 	dbLock = PZ_NewLock(nssILockCertDB);
    56 	PORT_Assert(dbLock != NULL);
    57     }
    58 }
    60 SECStatus
    61 nsslowcert_InitLocks(void)
    62 {
    63     if (freeListLock == NULL) {
    64 	freeListLock = PZ_NewLock(nssILockRefLock);
    65 	if (freeListLock == NULL) {
    66 	    return SECFailure;
    67 	}
    68     }
    69     if (certRefCountLock == NULL) {
    70 	certRefCountLock = PZ_NewLock(nssILockRefLock);
    71 	if (certRefCountLock == NULL) {
    72 	    return SECFailure;
    73 	}
    74     }
    75     if (certTrustLock == NULL ) {
    76 	certTrustLock = PZ_NewLock(nssILockCertDB);
    77 	if (certTrustLock == NULL) {
    78 	    return SECFailure;
    79 	}
    80     }
    82     return SECSuccess;
    83 }
    85 /*
    86  * Acquire the global lock on the cert database.
    87  * This lock is currently used for the following operations:
    88  *	adding or deleting a cert to either the temp or perm databases
    89  *	converting a temp to perm or perm to temp
    90  *	changing (maybe just adding!?) the trust of a cert
    91  *      chaning the DB status checking Configuration
    92  */
    93 static void
    94 nsslowcert_LockDB(NSSLOWCERTCertDBHandle *handle)
    95 {
    96     PZ_EnterMonitor(handle->dbMon);
    97     return;
    98 }
   100 /*
   101  * Free the global cert database lock.
   102  */
   103 static void
   104 nsslowcert_UnlockDB(NSSLOWCERTCertDBHandle *handle)
   105 {
   106     PRStatus prstat;
   108     prstat = PZ_ExitMonitor(handle->dbMon);
   110     PORT_Assert(prstat == PR_SUCCESS);
   112     return;
   113 }
   116 /*
   117  * Acquire the cert reference count lock
   118  * There is currently one global lock for all certs, but I'm putting a cert
   119  * arg here so that it will be easy to make it per-cert in the future if
   120  * that turns out to be necessary.
   121  */
   122 static void
   123 nsslowcert_LockCertRefCount(NSSLOWCERTCertificate *cert)
   124 {
   125     PORT_Assert(certRefCountLock != NULL);
   127     PZ_Lock(certRefCountLock);
   128     return;
   129 }
   131 /*
   132  * Free the cert reference count lock
   133  */
   134 static void
   135 nsslowcert_UnlockCertRefCount(NSSLOWCERTCertificate *cert)
   136 {
   137     PRStatus prstat;
   139     PORT_Assert(certRefCountLock != NULL);
   141     prstat = PZ_Unlock(certRefCountLock);
   143     PORT_Assert(prstat == PR_SUCCESS);
   145     return;
   146 }
   148 /*
   149  * Acquire the cert trust lock
   150  * There is currently one global lock for all certs, but I'm putting a cert
   151  * arg here so that it will be easy to make it per-cert in the future if
   152  * that turns out to be necessary.
   153  */
   154 static void
   155 nsslowcert_LockCertTrust(NSSLOWCERTCertificate *cert)
   156 {
   157     PORT_Assert(certTrustLock != NULL);
   159     PZ_Lock(certTrustLock);
   160     return;
   161 }
   163 /*
   164  * Free the cert trust lock
   165  */
   166 static void
   167 nsslowcert_UnlockCertTrust(NSSLOWCERTCertificate *cert)
   168 {
   169     PRStatus prstat;
   171     PORT_Assert(certTrustLock != NULL);
   173     prstat = PZ_Unlock(certTrustLock);
   175     PORT_Assert(prstat == PR_SUCCESS);
   177     return;
   178 }
   181 /*
   182  * Acquire the cert reference count lock
   183  * There is currently one global lock for all certs, but I'm putting a cert
   184  * arg here so that it will be easy to make it per-cert in the future if
   185  * that turns out to be necessary.
   186  */
   187 static void
   188 nsslowcert_LockFreeList(void)
   189 {
   190     PORT_Assert(freeListLock != NULL);
   192     SKIP_AFTER_FORK(PZ_Lock(freeListLock));
   193     return;
   194 }
   196 /*
   197  * Free the cert reference count lock
   198  */
   199 static void
   200 nsslowcert_UnlockFreeList(void)
   201 {
   202     PRStatus prstat = PR_SUCCESS;
   204     PORT_Assert(freeListLock != NULL);
   206     SKIP_AFTER_FORK(prstat = PZ_Unlock(freeListLock));
   208     PORT_Assert(prstat == PR_SUCCESS);
   210     return;
   211 }
   213 NSSLOWCERTCertificate *
   214 nsslowcert_DupCertificate(NSSLOWCERTCertificate *c)
   215 {
   216     if (c) {
   217 	nsslowcert_LockCertRefCount(c);
   218 	++c->referenceCount;
   219 	nsslowcert_UnlockCertRefCount(c);
   220     }
   221     return c;
   222 }
   224 static int
   225 certdb_Get(DB *db, DBT *key, DBT *data, unsigned int flags)
   226 {
   227     PRStatus prstat;
   228     int ret;
   230     PORT_Assert(dbLock != NULL);
   231     PZ_Lock(dbLock);
   233     ret = (* db->get)(db, key, data, flags);
   235     prstat = PZ_Unlock(dbLock);
   237     return(ret);
   238 }
   240 static int
   241 certdb_Put(DB *db, DBT *key, DBT *data, unsigned int flags)
   242 {
   243     PRStatus prstat;
   244     int ret = 0;
   246     PORT_Assert(dbLock != NULL);
   247     PZ_Lock(dbLock);
   249     ret = (* db->put)(db, key, data, flags);
   251     prstat = PZ_Unlock(dbLock);
   253     return(ret);
   254 }
   256 static int
   257 certdb_Sync(DB *db, unsigned int flags)
   258 {
   259     PRStatus prstat;
   260     int ret;
   262     PORT_Assert(dbLock != NULL);
   263     PZ_Lock(dbLock);
   265     ret = (* db->sync)(db, flags);
   267     prstat = PZ_Unlock(dbLock);
   269     return(ret);
   270 }
   272 #define DB_NOT_FOUND -30991  /* from DBM 3.2 */
   273 static int
   274 certdb_Del(DB *db, DBT *key, unsigned int flags)
   275 {
   276     PRStatus prstat;
   277     int ret;
   279     PORT_Assert(dbLock != NULL);
   280     PZ_Lock(dbLock);
   282     ret = (* db->del)(db, key, flags);
   284     prstat = PZ_Unlock(dbLock);
   286     /* don't fail if the record is already deleted */
   287     if (ret == DB_NOT_FOUND) {
   288 	ret = 0;
   289     }
   291     return(ret);
   292 }
   294 static int
   295 certdb_Seq(DB *db, DBT *key, DBT *data, unsigned int flags)
   296 {
   297     PRStatus prstat;
   298     int ret;
   300     PORT_Assert(dbLock != NULL);
   301     PZ_Lock(dbLock);
   303     ret = (* db->seq)(db, key, data, flags);
   305     prstat = PZ_Unlock(dbLock);
   307     return(ret);
   308 }
   310 static void
   311 certdb_Close(DB *db)
   312 {
   313     PRStatus prstat = PR_SUCCESS;
   315     PORT_Assert(dbLock != NULL);
   316     SKIP_AFTER_FORK(PZ_Lock(dbLock));
   318     (* db->close)(db);
   320     SKIP_AFTER_FORK(prstat = PZ_Unlock(dbLock));
   322     return;
   323 }
   325 void
   326 pkcs11_freeNickname(char *nickname, char *space)
   327 {
   328     if (nickname && nickname != space) {
   329 	PORT_Free(nickname);
   330     }
   331 }
   333 char *
   334 pkcs11_copyNickname(char *nickname,char *space, int spaceLen)
   335 {
   336     int len;
   337     char *copy = NULL;
   339     len = PORT_Strlen(nickname)+1;
   340     if (len <= spaceLen) {
   341 	copy = space;
   342 	PORT_Memcpy(copy,nickname,len);
   343     } else {
   344 	copy = PORT_Strdup(nickname);
   345     }
   347     return copy;
   348 }
   350 void
   351 pkcs11_freeStaticData (unsigned char *data, unsigned char *space)
   352 {
   353     if (data && data != space) {
   354 	PORT_Free(data);
   355     }
   356 }
   358 unsigned char *
   359 pkcs11_allocStaticData(int len, unsigned char *space, int spaceLen)
   360 {
   361     unsigned char *data = NULL;
   363     if (len <= spaceLen) {
   364 	data = space;
   365     } else {
   366 	data = (unsigned char *) PORT_Alloc(len);
   367     }
   369     return data;
   370 }
   372 unsigned char *
   373 pkcs11_copyStaticData(unsigned char *data, int len, 
   374 					unsigned char *space, int spaceLen)
   375 {
   376     unsigned char *copy = pkcs11_allocStaticData(len, space, spaceLen);
   377     if (copy) {
   378 	PORT_Memcpy(copy,data,len);
   379     }
   381     return copy;
   382 }
   384 /*
   385  * destroy a database entry
   386  */
   387 static void
   388 DestroyDBEntry(certDBEntry *entry)
   389 {
   390     PLArenaPool *arena = entry->common.arena;
   392     /* must be one of our certDBEntry from the free list */
   393     if (arena == NULL) {
   394 	certDBEntryCert *certEntry;
   395 	if ( entry->common.type != certDBEntryTypeCert) {
   396 	    return;
   397 	}
   398 	certEntry = (certDBEntryCert *)entry;
   400 	pkcs11_freeStaticData(certEntry->derCert.data, certEntry->derCertSpace);
   401 	pkcs11_freeNickname(certEntry->nickname, certEntry->nicknameSpace);
   403 	nsslowcert_LockFreeList();
   404 	if (entryListCount > MAX_ENTRY_LIST_COUNT) {
   405 	    PORT_Free(certEntry);
   406 	} else {
   407 	    entryListCount++;
   408 	    PORT_Memset(certEntry, 0, sizeof( *certEntry));
   409 	    certEntry->next = entryListHead;
   410 	    entryListHead = certEntry;
   411 	}
   412 	nsslowcert_UnlockFreeList();
   413 	return;
   414     }
   417     /* Zero out the entry struct, so that any further attempts to use it
   418      * will cause an exception (e.g. null pointer reference). */
   419     PORT_Memset(&entry->common, 0, sizeof entry->common);
   420     PORT_FreeArena(arena, PR_FALSE);
   422     return;
   423 }
   425 /* forward references */
   426 static void nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert);
   428 static SECStatus
   429 DeleteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, SECItem *dbkey)
   430 {
   431     DBT key;
   432     int ret;
   434     /* init the database key */
   435     key.data = dbkey->data;
   436     key.size = dbkey->len;
   438     dbkey->data[0] = (unsigned char)type;
   440     /* delete entry from database */
   441     ret = certdb_Del(handle->permCertDB, &key, 0 );
   442     if ( ret != 0 ) {
   443 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
   444 	goto loser;
   445     }
   447     ret = certdb_Sync(handle->permCertDB, 0);
   448     if ( ret ) {
   449 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
   450 	goto loser;
   451     }
   453     return(SECSuccess);
   455 loser:
   456     return(SECFailure);
   457 }
   459 static SECStatus
   460 ReadDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
   461 	    SECItem *dbkey, SECItem *dbentry, PLArenaPool *arena)
   462 {
   463     DBT data, key;
   464     int ret;
   465     unsigned char *buf;
   467     /* init the database key */
   468     key.data = dbkey->data;
   469     key.size = dbkey->len;
   471     dbkey->data[0] = (unsigned char)entry->type;
   473     /* read entry from database */
   474     ret = certdb_Get(handle->permCertDB, &key, &data, 0 );
   475     if ( ret != 0 ) {
   476 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
   477 	goto loser;
   478     }
   480     /* validate the entry */
   481     if ( data.size < SEC_DB_ENTRY_HEADER_LEN ) {
   482 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
   483 	goto loser;
   484     }
   485     buf = (unsigned char *)data.data;
   486     /* version 7 has the same schema, we may be using a v7 db if we openned
   487      * the databases readonly. */
   488     if (!((buf[0] == (unsigned char)CERT_DB_FILE_VERSION) 
   489 		|| (buf[0] == (unsigned char) CERT_DB_V7_FILE_VERSION))) {
   490 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
   491 	goto loser;
   492     }
   493     if ( buf[1] != (unsigned char)entry->type ) {
   494 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
   495 	goto loser;
   496     }
   498     /* copy out header information */
   499     entry->version = (unsigned int)buf[0];
   500     entry->type = (certDBEntryType)buf[1];
   501     entry->flags = (unsigned int)buf[2];
   503     /* format body of entry for return to caller */
   504     dbentry->len = data.size - SEC_DB_ENTRY_HEADER_LEN;
   505     if ( dbentry->len ) {
   506 	if (arena) {
   507 	    dbentry->data = (unsigned char *)
   508 				PORT_ArenaAlloc(arena, dbentry->len);
   509 	    if ( dbentry->data == NULL ) {
   510 		PORT_SetError(SEC_ERROR_NO_MEMORY);
   511 		goto loser;
   512 	    }
   514 	    PORT_Memcpy(dbentry->data, &buf[SEC_DB_ENTRY_HEADER_LEN],
   515 		  dbentry->len);
   516 	} else {
   517 	    dbentry->data = &buf[SEC_DB_ENTRY_HEADER_LEN];
   518 	}
   519     } else {
   520 	dbentry->data = NULL;
   521     }
   523     return(SECSuccess);
   525 loser:
   526     return(SECFailure);
   527 }
   529 /**
   530  ** Implement low level database access
   531  **/
   532 static SECStatus
   533 WriteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
   534 	     SECItem *dbkey, SECItem *dbentry)
   535 {
   536     int ret;
   537     DBT data, key;
   538     unsigned char *buf;
   540     data.data = dbentry->data;
   541     data.size = dbentry->len;
   543     buf = (unsigned char*)data.data;
   545     buf[0] = (unsigned char)entry->version;
   546     buf[1] = (unsigned char)entry->type;
   547     buf[2] = (unsigned char)entry->flags;
   549     key.data = dbkey->data;
   550     key.size = dbkey->len;
   552     dbkey->data[0] = (unsigned char)entry->type;
   554     /* put the record into the database now */
   555     ret = certdb_Put(handle->permCertDB, &key, &data, 0);
   557     if ( ret != 0 ) {
   558 	goto loser;
   559     }
   561     ret = certdb_Sync( handle->permCertDB, 0 );
   563     if ( ret ) {
   564 	goto loser;
   565     }
   567     return(SECSuccess);
   569 loser:
   570     return(SECFailure);
   571 }
   573 /*
   574  * encode a database cert record
   575  */
   576 static SECStatus
   577 EncodeDBCertEntry(certDBEntryCert *entry, PLArenaPool *arena, SECItem *dbitem)
   578 {
   579     unsigned int nnlen;
   580     unsigned char *buf;
   581     char *nn;
   582     char zbuf = 0;
   584     if ( entry->nickname ) {
   585 	nn = entry->nickname;
   586     } else {
   587 	nn = &zbuf;
   588     }
   589     nnlen = PORT_Strlen(nn) + 1;
   591     /* allocate space for encoded database record, including space
   592      * for low level header
   593      */
   594     dbitem->len = entry->derCert.len + nnlen + DB_CERT_ENTRY_HEADER_LEN +
   595 	SEC_DB_ENTRY_HEADER_LEN;
   597     dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
   598     if ( dbitem->data == NULL) {
   599 	PORT_SetError(SEC_ERROR_NO_MEMORY);
   600 	goto loser;
   601     }
   603     /* fill in database record */
   604     buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
   606     buf[0] = (PRUint8)( entry->trust.sslFlags >> 8 );
   607     buf[1] = (PRUint8)( entry->trust.sslFlags      );
   608     buf[2] = (PRUint8)( entry->trust.emailFlags >> 8 );
   609     buf[3] = (PRUint8)( entry->trust.emailFlags      );
   610     buf[4] = (PRUint8)( entry->trust.objectSigningFlags >> 8 );
   611     buf[5] = (PRUint8)( entry->trust.objectSigningFlags      );
   612     buf[6] = (PRUint8)( entry->derCert.len >> 8 );
   613     buf[7] = (PRUint8)( entry->derCert.len      );
   614     buf[8] = (PRUint8)( nnlen >> 8 );
   615     buf[9] = (PRUint8)( nnlen      );
   617     PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN], entry->derCert.data,
   618 	      entry->derCert.len);
   620     PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN + entry->derCert.len],
   621 	      nn, nnlen);
   623     return(SECSuccess);
   625 loser:
   626     return(SECFailure);
   627 }
   629 /*
   630  * encode a database key for a cert record
   631  */
   632 static SECStatus
   633 EncodeDBCertKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey)
   634 {
   635     unsigned int len = certKey->len + SEC_DB_KEY_HEADER_LEN;
   636     if (len > NSS_MAX_LEGACY_DB_KEY_SIZE)
   637 	goto loser;
   638     if (arena) {
   639 	dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, len);
   640     } else {
   641 	if (dbkey->len < len) {
   642 	    dbkey->data = (unsigned char *)PORT_Alloc(len);
   643 	}
   644     }
   645     dbkey->len = len;
   646     if ( dbkey->data == NULL ) {
   647 	goto loser;
   648     }
   649     PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
   650 	      certKey->data, certKey->len);
   651     dbkey->data[0] = certDBEntryTypeCert;
   653     return(SECSuccess);
   654 loser:
   655     return(SECFailure);
   656 }
   658 static SECStatus
   659 EncodeDBGenericKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey,
   660 				certDBEntryType entryType)
   661 {
   662     /*
   663      * we only allow _one_ KRL key!
   664      */
   665     if (entryType == certDBEntryTypeKeyRevocation) {
   666 	dbkey->len = SEC_DB_KEY_HEADER_LEN;
   667  	dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
   668 	if ( dbkey->data == NULL ) {
   669 	    goto loser;
   670 	}
   671         dbkey->data[0] = (unsigned char) entryType;
   672         return(SECSuccess);
   673     }
   676     dbkey->len = certKey->len + SEC_DB_KEY_HEADER_LEN;
   677     if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
   678 	goto loser;
   679     dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
   680     if ( dbkey->data == NULL ) {
   681 	goto loser;
   682     }
   683     PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
   684 	       certKey->data, certKey->len);
   685     dbkey->data[0] = (unsigned char) entryType;
   687     return(SECSuccess);
   688 loser:
   689     return(SECFailure);
   690 }
   692 static SECStatus
   693 DecodeDBCertEntry(certDBEntryCert *entry, SECItem *dbentry)
   694 {
   695     unsigned int nnlen;
   696     unsigned int headerlen;
   697     int lenoff;
   699     /* allow updates of old versions of the database */
   700     switch ( entry->common.version ) {
   701       case 5:
   702 	headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
   703 	lenoff = 3;
   704 	break;
   705       case 6:
   706 	/* should not get here */
   707 	PORT_Assert(0);
   708 	headerlen = DB_CERT_V6_ENTRY_HEADER_LEN;
   709 	lenoff = 3;
   710 	break;
   711       case 7:
   712       case 8:
   713 	headerlen = DB_CERT_ENTRY_HEADER_LEN;
   714 	lenoff = 6;
   715 	break;
   716       default:
   717 	/* better not get here */
   718 	PORT_Assert(0);
   719 	headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
   720 	lenoff = 3;
   721 	break;
   722     }
   724     /* is record long enough for header? */
   725     if ( dbentry->len < headerlen ) {
   726 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
   727 	goto loser;
   728     }
   730     /* is database entry correct length? */
   731     entry->derCert.len = ( ( dbentry->data[lenoff] << 8 ) |
   732 			  dbentry->data[lenoff+1] );
   733     nnlen = ( ( dbentry->data[lenoff+2] << 8 ) | dbentry->data[lenoff+3] );
   734     lenoff = dbentry->len - ( entry->derCert.len + nnlen + headerlen );
   735     if ( lenoff ) {
   736 	if ( lenoff < 0 || (lenoff & 0xffff) != 0 ) {
   737 	    PORT_SetError(SEC_ERROR_BAD_DATABASE);
   738 	    goto loser;
   739 	}
   740 	/* The cert size exceeded 64KB.  Reconstruct the correct length. */
   741 	entry->derCert.len += lenoff;
   742     }
   744     /* copy the dercert */
   745     entry->derCert.data = pkcs11_copyStaticData(&dbentry->data[headerlen],
   746 	entry->derCert.len,entry->derCertSpace,sizeof(entry->derCertSpace));
   747     if ( entry->derCert.data == NULL ) {
   748 	PORT_SetError(SEC_ERROR_NO_MEMORY);
   749 	goto loser;
   750     }
   752     /* copy the nickname */
   753     if ( nnlen > 1 ) {
   754 	entry->nickname = (char *)pkcs11_copyStaticData(
   755 			&dbentry->data[headerlen+entry->derCert.len], nnlen,
   756 			(unsigned char *)entry->nicknameSpace, 
   757 			sizeof(entry->nicknameSpace));
   758 	if ( entry->nickname == NULL ) {
   759 	    PORT_SetError(SEC_ERROR_NO_MEMORY);
   760 	    goto loser;
   761 	}
   762     } else {
   763 	entry->nickname = NULL;
   764     }
   766     if ( entry->common.version < 7 ) {
   767 	/* allow updates of v5 db */
   768 	entry->trust.sslFlags = dbentry->data[0];
   769 	entry->trust.emailFlags = dbentry->data[1];
   770 	entry->trust.objectSigningFlags = dbentry->data[2];
   771     } else {
   772 	entry->trust.sslFlags = ( dbentry->data[0] << 8 ) | dbentry->data[1];
   773 	entry->trust.emailFlags = ( dbentry->data[2] << 8 ) | dbentry->data[3];
   774 	entry->trust.objectSigningFlags =
   775 	    ( dbentry->data[4] << 8 ) | dbentry->data[5];
   776     }
   778     return(SECSuccess);
   779 loser:
   780     return(SECFailure);
   781 }
   784 /*
   785  * Create a new certDBEntryCert from existing data
   786  */
   787 static certDBEntryCert *
   788 NewDBCertEntry(SECItem *derCert, char *nickname,
   789 	       NSSLOWCERTCertTrust *trust, int flags)
   790 {
   791     certDBEntryCert *entry;
   792     PLArenaPool *arena = NULL;
   793     int nnlen;
   795     arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
   797     if ( !arena ) {
   798 	goto loser;
   799     }
   801     entry = PORT_ArenaZNew(arena, certDBEntryCert);
   802     if ( entry == NULL ) {
   803 	goto loser;
   804     }
   806     /* fill in the dbCert */
   807     entry->common.arena = arena;
   808     entry->common.type = certDBEntryTypeCert;
   809     entry->common.version = CERT_DB_FILE_VERSION;
   810     entry->common.flags = flags;
   812     if ( trust ) {
   813 	entry->trust = *trust;
   814     }
   816     entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, derCert->len);
   817     if ( !entry->derCert.data ) {
   818 	goto loser;
   819     }
   820     entry->derCert.len = derCert->len;
   821     PORT_Memcpy(entry->derCert.data, derCert->data, derCert->len);
   823     nnlen = ( nickname ? strlen(nickname) + 1 : 0 );
   825     if ( nnlen ) {
   826 	entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
   827 	if ( !entry->nickname ) {
   828 	    goto loser;
   829 	}
   830 	PORT_Memcpy(entry->nickname, nickname, nnlen);
   832     } else {
   833 	entry->nickname = 0;
   834     }
   836     return(entry);
   838 loser:
   840     /* allocation error, free arena and return */
   841     if ( arena ) {
   842 	PORT_FreeArena(arena, PR_FALSE);
   843     }
   845     PORT_SetError(SEC_ERROR_NO_MEMORY);
   846     return(0);
   847 }
   849 /*
   850  * Decode a version 4 DBCert from the byte stream database format
   851  * and construct a current database entry struct
   852  */
   853 static certDBEntryCert *
   854 DecodeV4DBCertEntry(unsigned char *buf, int len)
   855 {
   856     certDBEntryCert *entry;
   857     int certlen;
   858     int nnlen;
   859     PLArenaPool *arena;
   861     /* make sure length is at least long enough for the header */
   862     if ( len < DBCERT_V4_HEADER_LEN ) {
   863 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
   864 	return(0);
   865     }
   867     /* get other lengths */
   868     certlen = buf[3] << 8 | buf[4];
   869     nnlen = buf[5] << 8 | buf[6];
   871     /* make sure DB entry is the right size */
   872     if ( ( certlen + nnlen + DBCERT_V4_HEADER_LEN ) != len ) {
   873 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
   874 	return(0);
   875     }
   877     /* allocate arena */
   878     arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
   880     if ( !arena ) {
   881 	PORT_SetError(SEC_ERROR_NO_MEMORY);
   882 	return(0);
   883     }
   885     /* allocate structure and members */
   886     entry = (certDBEntryCert *)  PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
   888     if ( !entry ) {
   889 	goto loser;
   890     }
   892     entry->common.arena = arena;
   893     entry->common.version = CERT_DB_FILE_VERSION;
   894     entry->common.type = certDBEntryTypeCert;
   895     entry->common.flags = 0;
   896     entry->trust.sslFlags = buf[0];
   897     entry->trust.emailFlags = buf[1];
   898     entry->trust.objectSigningFlags = buf[2];
   900     entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, certlen);
   901     if ( !entry->derCert.data ) {
   902 	goto loser;
   903     }
   904     entry->derCert.len = certlen;
   905     PORT_Memcpy(entry->derCert.data, &buf[DBCERT_V4_HEADER_LEN], certlen);
   907     if ( nnlen ) {
   908         entry->nickname = (char *) PORT_ArenaAlloc(arena, nnlen);
   909         if ( !entry->nickname ) {
   910             goto loser;
   911         }
   912         PORT_Memcpy(entry->nickname, &buf[DBCERT_V4_HEADER_LEN + certlen], nnlen);
   914         if (PORT_Strcmp(entry->nickname, "Server-Cert") == 0) {
   915             entry->trust.sslFlags |= CERTDB_USER;
   916         }
   917     } else {
   918         entry->nickname = 0;
   919     }
   921     return(entry);
   923 loser:
   924     PORT_FreeArena(arena, PR_FALSE);
   925     PORT_SetError(SEC_ERROR_NO_MEMORY);
   926     return(0);
   927 }
   929 /*
   930  * Encode a Certificate database entry into byte stream suitable for
   931  * the database
   932  */
   933 static SECStatus
   934 WriteDBCertEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
   935 {
   936     SECItem dbitem, dbkey;
   937     PLArenaPool *tmparena = NULL;
   938     SECItem tmpitem;
   939     SECStatus rv;
   941     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   942     if ( tmparena == NULL ) {
   943 	goto loser;
   944     }
   946     rv = EncodeDBCertEntry(entry, tmparena, &dbitem);
   947     if ( rv != SECSuccess ) {
   948 	goto loser;
   949     }
   951     /* get the database key and format it */
   952     rv = nsslowcert_KeyFromDERCert(tmparena, &entry->derCert, &tmpitem);
   953     if ( rv == SECFailure ) {
   954 	goto loser;
   955     }
   957     rv = EncodeDBCertKey(&tmpitem, tmparena, &dbkey);
   958     if ( rv == SECFailure ) {
   959 	goto loser;
   960     }
   962     /* now write it to the database */
   963     rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
   964     if ( rv != SECSuccess ) {
   965 	goto loser;
   966     }
   968     PORT_FreeArena(tmparena, PR_FALSE);
   969     return(SECSuccess);
   971 loser:
   972     if ( tmparena ) {
   973 	PORT_FreeArena(tmparena, PR_FALSE);
   974     }
   975     return(SECFailure);
   976 }
   979 /*
   980  * delete a certificate entry
   981  */
   982 static SECStatus
   983 DeleteDBCertEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey)
   984 {
   985     SECItem dbkey;
   986     SECStatus rv;
   988     dbkey.data= NULL;
   989     dbkey.len = 0;
   991     rv = EncodeDBCertKey(certKey, NULL, &dbkey);
   992     if ( rv != SECSuccess ) {
   993 	goto loser;
   994     }
   996     rv = DeleteDBEntry(handle, certDBEntryTypeCert, &dbkey);
   997     if ( rv == SECFailure ) {
   998 	goto loser;
   999     }
  1001     PORT_Free(dbkey.data);
  1003     return(SECSuccess);
  1005 loser:
  1006     if (dbkey.data) {
  1007 	PORT_Free(dbkey.data);
  1009     return(SECFailure);
  1012 static certDBEntryCert *
  1013 CreateCertEntry(void)
  1015     certDBEntryCert *entry;
  1017     nsslowcert_LockFreeList();
  1018     entry = entryListHead;
  1019     if (entry) {
  1020 	entryListCount--;
  1021 	entryListHead = entry->next;
  1023     PORT_Assert(entryListCount >= 0);
  1024     nsslowcert_UnlockFreeList();
  1025     if (entry) {
  1026 	return entry;
  1029     return PORT_ZNew(certDBEntryCert);
  1032 static void
  1033 DestroyCertEntryFreeList(void)
  1035     certDBEntryCert *entry;
  1037     nsslowcert_LockFreeList();
  1038     while (NULL != (entry = entryListHead)) {
  1039 	entryListCount--;
  1040 	entryListHead = entry->next;
  1041 	PORT_Free(entry);
  1043     PORT_Assert(!entryListCount);
  1044     entryListCount = 0;
  1045     nsslowcert_UnlockFreeList();
  1048 /*
  1049  * Read a certificate entry
  1050  */
  1051 static certDBEntryCert *
  1052 ReadDBCertEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
  1054     certDBEntryCert *entry;
  1055     SECItem dbkey;
  1056     SECItem dbentry;
  1057     SECStatus rv;
  1058     unsigned char buf[512];
  1060     dbkey.data = buf;
  1061     dbkey.len = sizeof(buf);
  1063     entry = CreateCertEntry();
  1064     if ( entry == NULL ) {
  1065 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1066 	goto loser;
  1068     entry->common.arena = NULL;
  1069     entry->common.type = certDBEntryTypeCert;
  1071     rv = EncodeDBCertKey(certKey, NULL, &dbkey);
  1072     if ( rv != SECSuccess ) {
  1073 	goto loser;
  1076     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
  1077     if ( rv == SECFailure ) {
  1078 	goto loser;
  1081     rv = DecodeDBCertEntry(entry, &dbentry);
  1082     if ( rv != SECSuccess ) {
  1083 	goto loser;
  1086     pkcs11_freeStaticData(dbkey.data,buf);    
  1087     dbkey.data = NULL;
  1088     return(entry);
  1090 loser:
  1091     pkcs11_freeStaticData(dbkey.data,buf);    
  1092     dbkey.data = NULL;
  1093     if ( entry ) {
  1094         DestroyDBEntry((certDBEntry *)entry);
  1097     return(NULL);
  1100 /*
  1101  * encode a database cert record
  1102  */
  1103 static SECStatus
  1104 EncodeDBCrlEntry(certDBEntryRevocation *entry, PLArenaPool *arena, SECItem *dbitem)
  1106     unsigned int nnlen = 0;
  1107     unsigned char *buf;
  1109     if (entry->url) {  
  1110 	nnlen = PORT_Strlen(entry->url) + 1;
  1113     /* allocate space for encoded database record, including space
  1114      * for low level header
  1115      */
  1116     dbitem->len = entry->derCrl.len + nnlen 
  1117 		+ SEC_DB_ENTRY_HEADER_LEN + DB_CRL_ENTRY_HEADER_LEN;
  1119     dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
  1120     if ( dbitem->data == NULL) {
  1121 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1122 	goto loser;
  1125     /* fill in database record */
  1126     buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
  1128     buf[0] = (PRUint8)( entry->derCrl.len >> 8 );
  1129     buf[1] = (PRUint8)( entry->derCrl.len      );
  1130     buf[2] = (PRUint8)( nnlen >> 8 );
  1131     buf[3] = (PRUint8)( nnlen      );
  1133     PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN], entry->derCrl.data,
  1134 	      entry->derCrl.len);
  1136     if (nnlen != 0) {
  1137 	PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
  1138 	      entry->url, nnlen);
  1141     return(SECSuccess);
  1143 loser:
  1144     return(SECFailure);
  1147 static SECStatus
  1148 DecodeDBCrlEntry(certDBEntryRevocation *entry, SECItem *dbentry)
  1150     unsigned int urlLen;
  1151     int lenDiff;
  1153     /* is record long enough for header? */
  1154     if ( dbentry->len < DB_CRL_ENTRY_HEADER_LEN ) {
  1155 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
  1156 	goto loser;
  1159     /* is database entry correct length? */
  1160     entry->derCrl.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
  1161     urlLen =            ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
  1162     lenDiff = dbentry->len - 
  1163 			(entry->derCrl.len + urlLen + DB_CRL_ENTRY_HEADER_LEN);
  1164     if (lenDiff) {
  1165     	if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
  1166 	    PORT_SetError(SEC_ERROR_BAD_DATABASE);
  1167 	    goto loser;
  1169 	/* CRL entry is greater than 64 K. Hack to make this continue to work */
  1170 	entry->derCrl.len += lenDiff;
  1173     /* copy the der CRL */
  1174     entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
  1175 							 entry->derCrl.len);
  1176     if ( entry->derCrl.data == NULL ) {
  1177 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1178 	goto loser;
  1180     PORT_Memcpy(entry->derCrl.data, &dbentry->data[DB_CRL_ENTRY_HEADER_LEN],
  1181 	      entry->derCrl.len);
  1183     /* copy the url */
  1184     entry->url = NULL;
  1185     if (urlLen != 0) {
  1186 	entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, urlLen);
  1187 	if ( entry->url == NULL ) {
  1188 	    PORT_SetError(SEC_ERROR_NO_MEMORY);
  1189 	    goto loser;
  1191 	PORT_Memcpy(entry->url,
  1192 	      &dbentry->data[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
  1193 	      urlLen);
  1196     return(SECSuccess);
  1197 loser:
  1198     return(SECFailure);
  1201 /*
  1202  * Create a new certDBEntryRevocation from existing data
  1203  */
  1204 static certDBEntryRevocation *
  1205 NewDBCrlEntry(SECItem *derCrl, char * url, certDBEntryType crlType, int flags)
  1207     certDBEntryRevocation *entry;
  1208     PLArenaPool *arena = NULL;
  1209     int nnlen;
  1211     arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
  1213     if ( !arena ) {
  1214 	goto loser;
  1217     entry = PORT_ArenaZNew(arena, certDBEntryRevocation);
  1218     if ( entry == NULL ) {
  1219 	goto loser;
  1222     /* fill in the dbRevolcation */
  1223     entry->common.arena = arena;
  1224     entry->common.type = crlType;
  1225     entry->common.version = CERT_DB_FILE_VERSION;
  1226     entry->common.flags = flags;
  1229     entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(arena, derCrl->len);
  1230     if ( !entry->derCrl.data ) {
  1231 	goto loser;
  1234     if (url) {
  1235 	nnlen = PORT_Strlen(url) + 1;
  1236 	entry->url  = (char *)PORT_ArenaAlloc(arena, nnlen);
  1237 	if ( !entry->url ) {
  1238 	    goto loser;
  1240 	PORT_Memcpy(entry->url, url, nnlen);
  1241     } else {
  1242 	entry->url = NULL;
  1246     entry->derCrl.len = derCrl->len;
  1247     PORT_Memcpy(entry->derCrl.data, derCrl->data, derCrl->len);
  1249     return(entry);
  1251 loser:
  1253     /* allocation error, free arena and return */
  1254     if ( arena ) {
  1255 	PORT_FreeArena(arena, PR_FALSE);
  1258     PORT_SetError(SEC_ERROR_NO_MEMORY);
  1259     return(0);
  1263 static SECStatus
  1264 WriteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryRevocation *entry,
  1265 				SECItem *crlKey )
  1267     SECItem dbkey;
  1268     PLArenaPool *tmparena = NULL;
  1269     SECItem encodedEntry;
  1270     SECStatus rv;
  1272     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1273     if ( tmparena == NULL ) {
  1274 	goto loser;
  1277     rv = EncodeDBCrlEntry(entry, tmparena, &encodedEntry);
  1278     if ( rv == SECFailure ) {
  1279 	goto loser;
  1282     rv = EncodeDBGenericKey(crlKey, tmparena, &dbkey, entry->common.type);
  1283     if ( rv == SECFailure ) {
  1284 	goto loser;
  1287     /* now write it to the database */
  1288     rv = WriteDBEntry(handle, &entry->common, &dbkey, &encodedEntry);
  1289     if ( rv != SECSuccess ) {
  1290 	goto loser;
  1293     PORT_FreeArena(tmparena, PR_FALSE);
  1294     return(SECSuccess);
  1296 loser:
  1297     if ( tmparena ) {
  1298 	PORT_FreeArena(tmparena, PR_FALSE);
  1300     return(SECFailure);
  1302 /*
  1303  * delete a crl entry
  1304  */
  1305 static SECStatus
  1306 DeleteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *crlKey, 
  1307 						certDBEntryType crlType)
  1309     SECItem dbkey;
  1310     PLArenaPool *arena = NULL;
  1311     SECStatus rv;
  1313     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1314     if ( arena == NULL ) {
  1315 	goto loser;
  1318     rv = EncodeDBGenericKey(crlKey, arena, &dbkey, crlType);
  1319     if ( rv != SECSuccess ) {
  1320 	goto loser;
  1323     rv = DeleteDBEntry(handle, crlType, &dbkey);
  1324     if ( rv == SECFailure ) {
  1325 	goto loser;
  1328     PORT_FreeArena(arena, PR_FALSE);
  1329     return(SECSuccess);
  1331 loser:
  1332     if ( arena ) {
  1333 	PORT_FreeArena(arena, PR_FALSE);
  1336     return(SECFailure);
  1339 /*
  1340  * Read a certificate entry
  1341  */
  1342 static certDBEntryRevocation *
  1343 ReadDBCrlEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey,
  1344 						certDBEntryType crlType)
  1346     PLArenaPool *arena = NULL;
  1347     PLArenaPool *tmparena = NULL;
  1348     certDBEntryRevocation *entry;
  1349     SECItem dbkey;
  1350     SECItem dbentry;
  1351     SECStatus rv;
  1353     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1354     if ( arena == NULL ) {
  1355 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1356 	goto loser;
  1359     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1360     if ( tmparena == NULL ) {
  1361 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1362 	goto loser;
  1365     entry = (certDBEntryRevocation *)
  1366 			PORT_ArenaAlloc(arena, sizeof(certDBEntryRevocation));
  1367     if ( entry == NULL ) {
  1368 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1369 	goto loser;
  1371     entry->common.arena = arena;
  1372     entry->common.type = crlType;
  1374     rv = EncodeDBGenericKey(certKey, tmparena, &dbkey, crlType);
  1375     if ( rv != SECSuccess ) {
  1376 	goto loser;
  1379     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
  1380     if ( rv == SECFailure ) {
  1381 	goto loser;
  1384     rv = DecodeDBCrlEntry(entry, &dbentry);
  1385     if ( rv != SECSuccess ) {
  1386 	goto loser;
  1389     PORT_FreeArena(tmparena, PR_FALSE);
  1390     return(entry);
  1392 loser:
  1393     if ( tmparena ) {
  1394 	PORT_FreeArena(tmparena, PR_FALSE);
  1396     if ( arena ) {
  1397 	PORT_FreeArena(arena, PR_FALSE);
  1400     return(NULL);
  1403 void
  1404 nsslowcert_DestroyDBEntry(certDBEntry *entry)
  1406     DestroyDBEntry(entry);
  1407     return;
  1410 /*
  1411  * Encode a database nickname record
  1412  */
  1413 static SECStatus
  1414 EncodeDBNicknameEntry(certDBEntryNickname *entry, PLArenaPool *arena,
  1415 		      SECItem *dbitem)
  1417     unsigned char *buf;
  1419     /* allocate space for encoded database record, including space
  1420      * for low level header
  1421      */
  1422     dbitem->len = entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN +
  1423 	SEC_DB_ENTRY_HEADER_LEN;
  1424     dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
  1425     if ( dbitem->data == NULL) {
  1426 	goto loser;
  1429     /* fill in database record */
  1430     buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
  1431     buf[0] = (PRUint8)( entry->subjectName.len >> 8 );
  1432     buf[1] = (PRUint8)( entry->subjectName.len      );
  1433     PORT_Memcpy(&buf[DB_NICKNAME_ENTRY_HEADER_LEN], entry->subjectName.data,
  1434 	      entry->subjectName.len);
  1436     return(SECSuccess);
  1438 loser:
  1439     return(SECFailure);
  1442 /*
  1443  * Encode a database key for a nickname record
  1444  */
  1445 static SECStatus
  1446 EncodeDBNicknameKey(char *nickname, PLArenaPool *arena,
  1447 		    SECItem *dbkey)
  1449     unsigned int nnlen;
  1451     nnlen = PORT_Strlen(nickname) + 1; /* includes null */
  1453     /* now get the database key and format it */
  1454     dbkey->len = nnlen + SEC_DB_KEY_HEADER_LEN;
  1455     if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
  1456 	goto loser;
  1457     dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
  1458     if ( dbkey->data == NULL ) {
  1459 	goto loser;
  1461     PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], nickname, nnlen);
  1462     dbkey->data[0] = certDBEntryTypeNickname;
  1464     return(SECSuccess);
  1466 loser:
  1467     return(SECFailure);
  1470 static SECStatus
  1471 DecodeDBNicknameEntry(certDBEntryNickname *entry, SECItem *dbentry,
  1472                       char *nickname)
  1474     int lenDiff;
  1476     /* is record long enough for header? */
  1477     if ( dbentry->len < DB_NICKNAME_ENTRY_HEADER_LEN ) {
  1478 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
  1479 	goto loser;
  1482     /* is database entry correct length? */
  1483     entry->subjectName.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
  1484     lenDiff = dbentry->len - 
  1485 	      (entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN);
  1486     if (lenDiff) {
  1487 	if (lenDiff < 0 || (lenDiff & 0xffff) != 0 ) { 
  1488 	    PORT_SetError(SEC_ERROR_BAD_DATABASE);
  1489 	    goto loser;
  1491 	/* The entry size exceeded 64KB.  Reconstruct the correct length. */
  1492 	entry->subjectName.len += lenDiff;
  1495     /* copy the certkey */
  1496     entry->subjectName.data =
  1497 	(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
  1498 					 entry->subjectName.len);
  1499     if ( entry->subjectName.data == NULL ) {
  1500 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1501 	goto loser;
  1503     PORT_Memcpy(entry->subjectName.data,
  1504 	      &dbentry->data[DB_NICKNAME_ENTRY_HEADER_LEN],
  1505 	      entry->subjectName.len);
  1506     entry->subjectName.type = siBuffer;
  1508     entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena, 
  1509                                               PORT_Strlen(nickname)+1);
  1510     if ( entry->nickname ) {
  1511 	PORT_Strcpy(entry->nickname, nickname);
  1514     return(SECSuccess);
  1516 loser:
  1517     return(SECFailure);
  1520 /*
  1521  * create a new nickname entry
  1522  */
  1523 static certDBEntryNickname *
  1524 NewDBNicknameEntry(char *nickname, SECItem *subjectName, unsigned int flags)
  1526     PLArenaPool *arena = NULL;
  1527     certDBEntryNickname *entry;
  1528     int nnlen;
  1529     SECStatus rv;
  1531     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1532     if ( arena == NULL ) {
  1533 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1534 	goto loser;
  1537     entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
  1538 						 sizeof(certDBEntryNickname));
  1539     if ( entry == NULL ) {
  1540 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1541 	goto loser;
  1544     /* init common fields */
  1545     entry->common.arena = arena;
  1546     entry->common.type = certDBEntryTypeNickname;
  1547     entry->common.version = CERT_DB_FILE_VERSION;
  1548     entry->common.flags = flags;
  1550     /* copy the nickname */
  1551     nnlen = PORT_Strlen(nickname) + 1;
  1553     entry->nickname = (char*)PORT_ArenaAlloc(arena, nnlen);
  1554     if ( entry->nickname == NULL ) {
  1555 	goto loser;
  1558     PORT_Memcpy(entry->nickname, nickname, nnlen);
  1560     rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
  1561     if ( rv != SECSuccess ) {
  1562 	goto loser;
  1565     return(entry);
  1566 loser:
  1567     if ( arena ) {
  1568 	PORT_FreeArena(arena, PR_FALSE);
  1571     return(NULL);
  1574 /*
  1575  * delete a nickname entry
  1576  */
  1577 static SECStatus
  1578 DeleteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
  1580     PLArenaPool *arena = NULL;
  1581     SECStatus rv;
  1582     SECItem dbkey;
  1584     if ( nickname == NULL ) {
  1585 	return(SECSuccess);
  1588     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1589     if ( arena == NULL ) {
  1590 	goto loser;
  1593     rv = EncodeDBNicknameKey(nickname, arena, &dbkey);
  1594     if ( rv != SECSuccess ) {
  1595 	goto loser;
  1598     rv = DeleteDBEntry(handle, certDBEntryTypeNickname, &dbkey);
  1599     if ( rv == SECFailure ) {
  1600 	goto loser;
  1603     PORT_FreeArena(arena, PR_FALSE);
  1604     return(SECSuccess);
  1606 loser:
  1607     if ( arena ) {
  1608 	PORT_FreeArena(arena, PR_FALSE);
  1611     return(SECFailure);
  1614 /*
  1615  * Read a nickname entry
  1616  */
  1617 static certDBEntryNickname *
  1618 ReadDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
  1620     PLArenaPool *arena = NULL;
  1621     PLArenaPool *tmparena = NULL;
  1622     certDBEntryNickname *entry;
  1623     SECItem dbkey;
  1624     SECItem dbentry;
  1625     SECStatus rv;
  1627     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1628     if ( arena == NULL ) {
  1629 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1630 	goto loser;
  1633     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1634     if ( tmparena == NULL ) {
  1635 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1636 	goto loser;
  1639     entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
  1640 						 sizeof(certDBEntryNickname));
  1641     if ( entry == NULL ) {
  1642 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1643 	goto loser;
  1645     entry->common.arena = arena;
  1646     entry->common.type = certDBEntryTypeNickname;
  1648     rv = EncodeDBNicknameKey(nickname, tmparena, &dbkey);
  1649     if ( rv != SECSuccess ) {
  1650 	goto loser;
  1653     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
  1654     if ( rv == SECFailure ) {
  1655 	goto loser;
  1658     /* is record long enough for header? */
  1659     if ( dbentry.len < DB_NICKNAME_ENTRY_HEADER_LEN ) {
  1660 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
  1661 	goto loser;
  1664     rv = DecodeDBNicknameEntry(entry, &dbentry, nickname);
  1665     if ( rv != SECSuccess ) {
  1666 	goto loser;
  1669     PORT_FreeArena(tmparena, PR_FALSE);
  1670     return(entry);
  1672 loser:
  1673     if ( tmparena ) {
  1674 	PORT_FreeArena(tmparena, PR_FALSE);
  1676     if ( arena ) {
  1677 	PORT_FreeArena(arena, PR_FALSE);
  1680     return(NULL);
  1683 /*
  1684  * Encode a nickname entry into byte stream suitable for
  1685  * the database
  1686  */
  1687 static SECStatus
  1688 WriteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryNickname *entry)
  1690     SECItem dbitem, dbkey;
  1691     PLArenaPool *tmparena = NULL;
  1692     SECStatus rv;
  1694     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1695     if ( tmparena == NULL ) {
  1696 	goto loser;
  1699     rv = EncodeDBNicknameEntry(entry, tmparena, &dbitem);
  1700     if ( rv != SECSuccess ) {
  1701 	goto loser;
  1704     rv = EncodeDBNicknameKey(entry->nickname, tmparena, &dbkey);
  1705     if ( rv != SECSuccess ) {
  1706 	goto loser;
  1709     /* now write it to the database */
  1710     rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
  1711     if ( rv != SECSuccess ) {
  1712 	goto loser;
  1715     PORT_FreeArena(tmparena, PR_FALSE);
  1716     return(SECSuccess);
  1718 loser:
  1719     if ( tmparena ) {
  1720 	PORT_FreeArena(tmparena, PR_FALSE);
  1722     return(SECFailure);
  1726 static SECStatus
  1727 EncodeDBSMimeEntry(certDBEntrySMime *entry, PLArenaPool *arena,
  1728 		   SECItem *dbitem)
  1730     unsigned char *buf;
  1732     /* allocate space for encoded database record, including space
  1733      * for low level header
  1734      */
  1735     dbitem->len = entry->subjectName.len + entry->smimeOptions.len +
  1736 	entry->optionsDate.len +
  1737 	DB_SMIME_ENTRY_HEADER_LEN + SEC_DB_ENTRY_HEADER_LEN;
  1739     dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
  1740     if ( dbitem->data == NULL) {
  1741 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1742 	goto loser;
  1745     /* fill in database record */
  1746     buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
  1748     buf[0] = (PRUint8)( entry->subjectName.len >> 8 );
  1749     buf[1] = (PRUint8)( entry->subjectName.len      );
  1750     buf[2] = (PRUint8)( entry->smimeOptions.len >> 8 );
  1751     buf[3] = (PRUint8)( entry->smimeOptions.len      );
  1752     buf[4] = (PRUint8)( entry->optionsDate.len >> 8 );
  1753     buf[5] = (PRUint8)( entry->optionsDate.len      );
  1755     /* if no smime options, then there should not be an options date either */
  1756     PORT_Assert( ! ( ( entry->smimeOptions.len == 0 ) &&
  1757 		    ( entry->optionsDate.len != 0 ) ) );
  1759     PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN], entry->subjectName.data,
  1760 	      entry->subjectName.len);
  1761     if ( entry->smimeOptions.len ) {
  1762 	PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN+entry->subjectName.len],
  1763 		    entry->smimeOptions.data,
  1764 		    entry->smimeOptions.len);
  1765 	PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len +
  1766 			 entry->smimeOptions.len],
  1767 		    entry->optionsDate.data,
  1768 		    entry->optionsDate.len);
  1771     return(SECSuccess);
  1773 loser:
  1774     return(SECFailure);
  1777 /*
  1778  * Encode a database key for a SMIME record
  1779  */
  1780 static SECStatus
  1781 EncodeDBSMimeKey(char *emailAddr, PLArenaPool *arena,
  1782 		 SECItem *dbkey)
  1784     unsigned int addrlen;
  1786     addrlen = PORT_Strlen(emailAddr) + 1; /* includes null */
  1788     /* now get the database key and format it */
  1789     dbkey->len = addrlen + SEC_DB_KEY_HEADER_LEN;
  1790     if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
  1791 	goto loser;
  1792     dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
  1793     if ( dbkey->data == NULL ) {
  1794 	goto loser;
  1796     PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], emailAddr, addrlen);
  1797     dbkey->data[0] = certDBEntryTypeSMimeProfile;
  1799     return(SECSuccess);
  1801 loser:
  1802     return(SECFailure);
  1805 /*
  1806  * Decode a database SMIME record
  1807  */
  1808 static SECStatus
  1809 DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
  1811     int lenDiff;
  1813     /* is record long enough for header? */
  1814     if ( dbentry->len < DB_SMIME_ENTRY_HEADER_LEN ) {
  1815 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
  1816 	goto loser;
  1819     /* is database entry correct length? */
  1820     entry->subjectName.len  = (( dbentry->data[0] << 8 ) | dbentry->data[1] );
  1821     entry->smimeOptions.len = (( dbentry->data[2] << 8 ) | dbentry->data[3] );
  1822     entry->optionsDate.len  = (( dbentry->data[4] << 8 ) | dbentry->data[5] );
  1823     lenDiff = dbentry->len - (entry->subjectName.len + 
  1824                               entry->smimeOptions.len + 
  1825 			      entry->optionsDate.len + 
  1826 			      DB_SMIME_ENTRY_HEADER_LEN);
  1827     if (lenDiff) {
  1828 	if (lenDiff < 0 || (lenDiff & 0xffff) != 0 ) { 
  1829 	    PORT_SetError(SEC_ERROR_BAD_DATABASE);
  1830 	    goto loser;
  1832 	/* The entry size exceeded 64KB.  Reconstruct the correct length. */
  1833 	entry->subjectName.len += lenDiff;
  1836     /* copy the subject name */
  1837     entry->subjectName.data =
  1838 	(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
  1839 					 entry->subjectName.len);
  1840     if ( entry->subjectName.data == NULL ) {
  1841 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1842 	goto loser;
  1844     PORT_Memcpy(entry->subjectName.data,
  1845 	      &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN],
  1846 	      entry->subjectName.len);
  1848     /* copy the smime options */
  1849     if ( entry->smimeOptions.len ) {
  1850 	entry->smimeOptions.data =
  1851 	    (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
  1852 					     entry->smimeOptions.len);
  1853 	if ( entry->smimeOptions.data == NULL ) {
  1854 	    PORT_SetError(SEC_ERROR_NO_MEMORY);
  1855 	    goto loser;
  1857 	PORT_Memcpy(entry->smimeOptions.data,
  1858 		    &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
  1859 				   entry->subjectName.len],
  1860 		    entry->smimeOptions.len);
  1862     if ( entry->optionsDate.len ) {
  1863 	entry->optionsDate.data =
  1864 	    (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
  1865 					     entry->optionsDate.len);
  1866 	if ( entry->optionsDate.data == NULL ) {
  1867 	    PORT_SetError(SEC_ERROR_NO_MEMORY);
  1868 	    goto loser;
  1870 	PORT_Memcpy(entry->optionsDate.data,
  1871 		    &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
  1872 				   entry->subjectName.len +
  1873 				   entry->smimeOptions.len],
  1874 		    entry->optionsDate.len);
  1877     /* both options and options date must either exist or not exist */
  1878     if ( ( ( entry->optionsDate.len == 0 ) ||
  1879 	  ( entry->smimeOptions.len == 0 ) ) &&
  1880 	entry->smimeOptions.len != entry->optionsDate.len ) {
  1881 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
  1882 	goto loser;
  1885     entry->emailAddr = (char *)PORT_ArenaAlloc(entry->common.arena,
  1886 						PORT_Strlen(emailAddr)+1);
  1887     if ( entry->emailAddr ) {
  1888 	PORT_Strcpy(entry->emailAddr, emailAddr);
  1891     return(SECSuccess);
  1893 loser:
  1894     return(SECFailure);
  1897 /*
  1898  * create a new SMIME entry
  1899  */
  1900 static certDBEntrySMime *
  1901 NewDBSMimeEntry(char *emailAddr, SECItem *subjectName, SECItem *smimeOptions,
  1902 		SECItem *optionsDate, unsigned int flags)
  1904     PLArenaPool *arena = NULL;
  1905     certDBEntrySMime *entry;
  1906     int addrlen;
  1907     SECStatus rv;
  1909     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1910     if ( arena == NULL ) {
  1911 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1912 	goto loser;
  1915     entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
  1916 						sizeof(certDBEntrySMime));
  1917     if ( entry == NULL ) {
  1918 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  1919 	goto loser;
  1922     /* init common fields */
  1923     entry->common.arena = arena;
  1924     entry->common.type = certDBEntryTypeSMimeProfile;
  1925     entry->common.version = CERT_DB_FILE_VERSION;
  1926     entry->common.flags = flags;
  1928     /* copy the email addr */
  1929     addrlen = PORT_Strlen(emailAddr) + 1;
  1931     entry->emailAddr = (char*)PORT_ArenaAlloc(arena, addrlen);
  1932     if ( entry->emailAddr == NULL ) {
  1933 	goto loser;
  1936     PORT_Memcpy(entry->emailAddr, emailAddr, addrlen);
  1938     /* copy the subject name */
  1939     rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
  1940     if ( rv != SECSuccess ) {
  1941 	goto loser;
  1944     /* copy the smime options */
  1945     if ( smimeOptions ) {
  1946 	rv = SECITEM_CopyItem(arena, &entry->smimeOptions, smimeOptions);
  1947 	if ( rv != SECSuccess ) {
  1948 	    goto loser;
  1950     } else {
  1951 	PORT_Assert(optionsDate == NULL);
  1952 	entry->smimeOptions.data = NULL;
  1953 	entry->smimeOptions.len = 0;
  1956     /* copy the options date */
  1957     if ( optionsDate ) {
  1958 	rv = SECITEM_CopyItem(arena, &entry->optionsDate, optionsDate);
  1959 	if ( rv != SECSuccess ) {
  1960 	    goto loser;
  1962     } else {
  1963 	PORT_Assert(smimeOptions == NULL);
  1964 	entry->optionsDate.data = NULL;
  1965 	entry->optionsDate.len = 0;
  1968     return(entry);
  1969 loser:
  1970     if ( arena ) {
  1971 	PORT_FreeArena(arena, PR_FALSE);
  1974     return(NULL);
  1977 /*
  1978  * delete a SMIME entry
  1979  */
  1980 static SECStatus
  1981 DeleteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
  1983     PLArenaPool *arena = NULL;
  1984     SECStatus rv;
  1985     SECItem dbkey;
  1987     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1988     if ( arena == NULL ) {
  1989 	goto loser;
  1992     rv = EncodeDBSMimeKey(emailAddr, arena, &dbkey);
  1993     if ( rv != SECSuccess ) {
  1994 	goto loser;
  1997     rv = DeleteDBEntry(handle, certDBEntryTypeSMimeProfile, &dbkey);
  1998     if ( rv == SECFailure ) {
  1999 	goto loser;
  2002     PORT_FreeArena(arena, PR_FALSE);
  2003     return(SECSuccess);
  2005 loser:
  2006     if ( arena ) {
  2007 	PORT_FreeArena(arena, PR_FALSE);
  2010     return(SECFailure);
  2013 /*
  2014  * Read a SMIME entry
  2015  */
  2016 certDBEntrySMime *
  2017 nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
  2019     PLArenaPool *arena = NULL;
  2020     PLArenaPool *tmparena = NULL;
  2021     certDBEntrySMime *entry;
  2022     SECItem dbkey;
  2023     SECItem dbentry;
  2024     SECStatus rv;
  2026     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2027     if ( arena == NULL ) {
  2028 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2029 	goto loser;
  2032     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2033     if ( tmparena == NULL ) {
  2034 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2035 	goto loser;
  2038     entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
  2039 						sizeof(certDBEntrySMime));
  2040     if ( entry == NULL ) {
  2041 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2042 	goto loser;
  2044     entry->common.arena = arena;
  2045     entry->common.type = certDBEntryTypeSMimeProfile;
  2047     rv = EncodeDBSMimeKey(emailAddr, tmparena, &dbkey);
  2048     if ( rv != SECSuccess ) {
  2049 	goto loser;
  2052     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
  2053     if ( rv == SECFailure ) {
  2054 	goto loser;
  2057     /* is record long enough for header? */
  2058     if ( dbentry.len < DB_SMIME_ENTRY_HEADER_LEN ) {
  2059 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
  2060 	goto loser;
  2063     rv = DecodeDBSMimeEntry(entry, &dbentry, emailAddr);
  2064     if ( rv != SECSuccess ) {
  2065 	goto loser;
  2068     PORT_FreeArena(tmparena, PR_FALSE);
  2069     return(entry);
  2071 loser:
  2072     if ( tmparena ) {
  2073 	PORT_FreeArena(tmparena, PR_FALSE);
  2075     if ( arena ) {
  2076 	PORT_FreeArena(arena, PR_FALSE);
  2079     return(NULL);
  2082 /*
  2083  * Encode a SMIME entry into byte stream suitable for
  2084  * the database
  2085  */
  2086 static SECStatus
  2087 WriteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySMime *entry)
  2089     SECItem dbitem, dbkey;
  2090     PLArenaPool *tmparena = NULL;
  2091     SECStatus rv;
  2093     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2094     if ( tmparena == NULL ) {
  2095 	goto loser;
  2098     rv = EncodeDBSMimeEntry(entry, tmparena, &dbitem);
  2099     if ( rv != SECSuccess ) {
  2100 	goto loser;
  2103     rv = EncodeDBSMimeKey(entry->emailAddr, tmparena, &dbkey);
  2104     if ( rv != SECSuccess ) {
  2105 	goto loser;
  2108     /* now write it to the database */
  2109     rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
  2110     if ( rv != SECSuccess ) {
  2111 	goto loser;
  2114     PORT_FreeArena(tmparena, PR_FALSE);
  2115     return(SECSuccess);
  2117 loser:
  2118     if ( tmparena ) {
  2119 	PORT_FreeArena(tmparena, PR_FALSE);
  2121     return(SECFailure);
  2125 /*
  2126  * Encode a database subject record
  2127  */
  2128 static SECStatus
  2129 EncodeDBSubjectEntry(certDBEntrySubject *entry, PLArenaPool *arena,
  2130 		     SECItem *dbitem)
  2132     unsigned char *buf;
  2133     int len;
  2134     unsigned int ncerts;
  2135     unsigned int i;
  2136     unsigned char *tmpbuf;
  2137     unsigned int nnlen = 0;
  2138     unsigned int eaddrslen = 0;
  2139     int keyidoff;
  2140     SECItem *certKeys = entry->certKeys;
  2141     SECItem *keyIDs   = entry->keyIDs;;
  2143     if ( entry->nickname ) {
  2144 	nnlen = PORT_Strlen(entry->nickname) + 1;
  2146     if ( entry->emailAddrs ) {
  2147 	eaddrslen = 2;
  2148 	for (i=0; i < entry->nemailAddrs; i++) {
  2149 	    eaddrslen += PORT_Strlen(entry->emailAddrs[i]) + 1 + 2;
  2153     ncerts = entry->ncerts;
  2155     /* compute the length of the entry */
  2156     keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen ;
  2157     len = keyidoff + (4 * ncerts) + eaddrslen;
  2158     for ( i = 0; i < ncerts; i++ ) {
  2159 	if (keyIDs[i].len   > 0xffff ||
  2160 	   (certKeys[i].len > 0xffff)) {
  2161     	    PORT_SetError(SEC_ERROR_INPUT_LEN);
  2162 	    goto loser;
  2164 	len += certKeys[i].len;
  2165 	len += keyIDs[i].len;
  2168     /* allocate space for encoded database record, including space
  2169      * for low level header
  2170      */
  2171     dbitem->len = len + SEC_DB_ENTRY_HEADER_LEN;
  2173     dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
  2174     if ( dbitem->data == NULL) {
  2175 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2176 	goto loser;
  2179     /* fill in database record */
  2180     buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
  2182     buf[0] = (PRUint8)( ncerts >> 8 );
  2183     buf[1] = (PRUint8)( ncerts      );
  2184     buf[2] = (PRUint8)( nnlen >> 8 );
  2185     buf[3] = (PRUint8)( nnlen      );
  2186     /* v7 email field is NULL in v8 */
  2187     buf[4] = 0;
  2188     buf[5] = 0;
  2190     PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen);
  2191     tmpbuf = &buf[keyidoff];   
  2192     for ( i = 0; i < ncerts; i++ ) {
  2193 	tmpbuf[0] = (PRUint8)( certKeys[i].len >> 8 );
  2194 	tmpbuf[1] = (PRUint8)( certKeys[i].len      );
  2195 	tmpbuf += 2;
  2197     for ( i = 0; i < ncerts; i++ ) {
  2198 	tmpbuf[0] = (PRUint8)( keyIDs[i].len >> 8 );
  2199 	tmpbuf[1] = (PRUint8)( keyIDs[i].len      );
  2200 	tmpbuf += 2;
  2203     for ( i = 0; i < ncerts; i++ ) {
  2204 	PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
  2205 	tmpbuf += certKeys[i].len;
  2207     for ( i = 0; i < ncerts; i++ ) {
  2208 	PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
  2209 	tmpbuf += keyIDs[i].len;
  2212     if (entry->emailAddrs) {
  2213 	tmpbuf[0] = (PRUint8)( entry->nemailAddrs >> 8 );
  2214 	tmpbuf[1] = (PRUint8)( entry->nemailAddrs      );
  2215 	tmpbuf += 2;
  2216 	for (i=0; i < entry->nemailAddrs; i++) {
  2217 	    int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1;
  2218 	    tmpbuf[0] = (PRUint8)( nameLen >> 8 );
  2219 	    tmpbuf[1] = (PRUint8)( nameLen      );
  2220 	    tmpbuf += 2;
  2221 	    PORT_Memcpy(tmpbuf,entry->emailAddrs[i],nameLen);
  2222 	    tmpbuf +=nameLen;
  2226     PORT_Assert(tmpbuf == &buf[len]);
  2228     return(SECSuccess);
  2230 loser:
  2231     return(SECFailure);
  2234 /*
  2235  * Encode a database key for a subject record
  2236  */
  2237 static SECStatus
  2238 EncodeDBSubjectKey(SECItem *derSubject, PLArenaPool *arena,
  2239 		   SECItem *dbkey)
  2241     dbkey->len = derSubject->len + SEC_DB_KEY_HEADER_LEN;
  2242     if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
  2243 	goto loser;
  2244     dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
  2245     if ( dbkey->data == NULL ) {
  2246 	goto loser;
  2248     PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], derSubject->data,
  2249 	      derSubject->len);
  2250     dbkey->data[0] = certDBEntryTypeSubject;
  2252     return(SECSuccess);
  2254 loser:
  2255     return(SECFailure);
  2258 static SECStatus
  2259 DecodeDBSubjectEntry(certDBEntrySubject *entry, SECItem *dbentry,
  2260 		     const SECItem *derSubject)
  2262     PLArenaPool *arena     = entry->common.arena;
  2263     unsigned char *tmpbuf;
  2264     unsigned char *end;
  2265     void        *mark      = PORT_ArenaMark(arena);
  2266     unsigned int eaddrlen;
  2267     unsigned int i;
  2268     unsigned int keyidoff;
  2269     unsigned int len;
  2270     unsigned int ncerts    = 0;
  2271     unsigned int nnlen;
  2272     SECStatus rv;
  2274     rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
  2275     if ( rv != SECSuccess ) {
  2276 	goto loser;
  2279     /* is record long enough for header? */
  2280     if ( dbentry->len < DB_SUBJECT_ENTRY_HEADER_LEN ) {
  2281 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
  2282 	goto loser;
  2285     entry->ncerts = ncerts = (( dbentry->data[0] << 8 ) | dbentry->data[1] );
  2286     nnlen =                  (( dbentry->data[2] << 8 ) | dbentry->data[3] );
  2287     eaddrlen =               (( dbentry->data[4] << 8 ) | dbentry->data[5] );
  2288     keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
  2289     len = keyidoff + (4 * ncerts);
  2290     if ( dbentry->len < len) {
  2291 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
  2292 	goto loser;
  2295     entry->certKeys = PORT_ArenaNewArray(arena, SECItem, ncerts);
  2296     entry->keyIDs   = PORT_ArenaNewArray(arena, SECItem, ncerts);
  2297     if ( ( entry->certKeys == NULL ) || ( entry->keyIDs == NULL ) ) {
  2298 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2299 	goto loser;
  2302     if ( nnlen > 1 ) { /* null terminator is stored */
  2303 	entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
  2304 	if ( entry->nickname == NULL ) {
  2305 	    PORT_SetError(SEC_ERROR_NO_MEMORY);
  2306 	    goto loser;
  2308 	PORT_Memcpy(entry->nickname,
  2309 		    &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN],
  2310 		    nnlen);
  2311     } else {
  2312 	entry->nickname = NULL;
  2315     /* if we have an old style email entry, there is only one */    
  2316     entry->nemailAddrs = 0;
  2317     if ( eaddrlen > 1 ) { /* null terminator is stored */
  2318 	entry->emailAddrs = PORT_ArenaNewArray(arena, char *, 2);
  2319 	if ( entry->emailAddrs == NULL ) {
  2320 	    PORT_SetError(SEC_ERROR_NO_MEMORY);
  2321 	    goto loser;
  2323 	entry->emailAddrs[0] = (char *)PORT_ArenaAlloc(arena, eaddrlen);
  2324 	if ( entry->emailAddrs[0] == NULL ) {
  2325 	    PORT_SetError(SEC_ERROR_NO_MEMORY);
  2326 	    goto loser;
  2328 	PORT_Memcpy(entry->emailAddrs[0],
  2329 		    &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN+nnlen],
  2330 		    eaddrlen);
  2331 	 entry->nemailAddrs = 1;
  2332     } else {
  2333 	entry->emailAddrs = NULL;
  2336     /* collect the lengths of the certKeys and keyIDs, and total the
  2337      * overall length.
  2338      */
  2339     tmpbuf = &dbentry->data[keyidoff];
  2340     for ( i = 0; i < ncerts; i++ ) {
  2341         unsigned int itemlen = ( tmpbuf[0] << 8 ) | tmpbuf[1];
  2342         entry->certKeys[i].len = itemlen;
  2343         len += itemlen;
  2344         tmpbuf += 2;
  2346     for ( i = 0; i < ncerts; i++ ) {
  2347         unsigned int itemlen = ( tmpbuf[0] << 8 ) | tmpbuf[1] ;
  2348         entry->keyIDs[i].len = itemlen;
  2349         len += itemlen;
  2350         tmpbuf += 2;
  2353     /* is encoded entry large enough ? */
  2354     if ( len > dbentry->len ){
  2355 	PORT_SetError(SEC_ERROR_BAD_DATABASE);
  2356 	goto loser;
  2359     for ( i = 0; i < ncerts; i++ ) {
  2360 	unsigned int kLen = entry->certKeys[i].len;
  2361 	entry->certKeys[i].data = (unsigned char *)PORT_ArenaAlloc(arena, kLen);
  2362 	if ( entry->certKeys[i].data == NULL ) {
  2363 	    PORT_SetError(SEC_ERROR_NO_MEMORY);
  2364 	    goto loser;
  2366 	PORT_Memcpy(entry->certKeys[i].data, tmpbuf, kLen);
  2367 	tmpbuf += kLen;
  2369     for ( i = 0; i < ncerts; i++ ) {
  2370 	unsigned int iLen = entry->keyIDs[i].len;
  2371 	entry->keyIDs[i].data = (unsigned char *)PORT_ArenaAlloc(arena, iLen);
  2372 	if ( entry->keyIDs[i].data == NULL ) {
  2373 	    PORT_SetError(SEC_ERROR_NO_MEMORY);
  2374 	    goto loser;
  2376 	PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, iLen);
  2377 	tmpbuf += iLen;
  2380     end = dbentry->data + dbentry->len;
  2381     if ((eaddrlen == 0) && (end - tmpbuf > 1)) {
  2382 	/* read in the additional email addresses */
  2383 	entry->nemailAddrs = (((unsigned int)tmpbuf[0]) << 8) | tmpbuf[1];
  2384 	tmpbuf += 2;
  2385 	if (end - tmpbuf < 2 * (int)entry->nemailAddrs)
  2386 	    goto loser;
  2387 	entry->emailAddrs = PORT_ArenaNewArray(arena, char *, entry->nemailAddrs);
  2388 	if (entry->emailAddrs == NULL) {
  2389 	    PORT_SetError(SEC_ERROR_NO_MEMORY);
  2390 	    goto loser;
  2392 	for (i=0; i < entry->nemailAddrs; i++) {
  2393 	    int nameLen;
  2394 	    if (end - tmpbuf < 2) {
  2395 		goto loser;
  2397 	    nameLen = (((int)tmpbuf[0]) << 8) | tmpbuf[1];
  2398 	    tmpbuf += 2;
  2399 	    if (end - tmpbuf < nameLen) {
  2400 		goto loser;
  2402 	    entry->emailAddrs[i] = PORT_ArenaAlloc(arena,nameLen);
  2403 	    if (entry->emailAddrs == NULL) {
  2404 	        PORT_SetError(SEC_ERROR_NO_MEMORY);
  2405 	        goto loser;
  2407 	    PORT_Memcpy(entry->emailAddrs[i], tmpbuf, nameLen);
  2408 	    tmpbuf += nameLen;
  2410 	if (tmpbuf != end) 
  2411 	    goto loser;
  2413     PORT_ArenaUnmark(arena, mark);
  2414     return(SECSuccess);
  2416 loser:
  2417     PORT_ArenaRelease(arena, mark); /* discard above allocations */
  2418     return(SECFailure);
  2421 /*
  2422  * create a new subject entry with a single cert
  2423  */
  2424 static certDBEntrySubject *
  2425 NewDBSubjectEntry(SECItem *derSubject, SECItem *certKey,
  2426 		  SECItem *keyID, char *nickname, char *emailAddr,
  2427 		  unsigned int flags)
  2429     PLArenaPool *arena = NULL;
  2430     certDBEntrySubject *entry;
  2431     SECStatus rv;
  2432     unsigned int nnlen;
  2433     unsigned int eaddrlen;
  2435     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2436     if ( arena == NULL ) {
  2437 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2438 	goto loser;
  2441     entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
  2442 						  sizeof(certDBEntrySubject));
  2443     if ( entry == NULL ) {
  2444 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2445 	goto loser;
  2448     /* init common fields */
  2449     entry->common.arena = arena;
  2450     entry->common.type = certDBEntryTypeSubject;
  2451     entry->common.version = CERT_DB_FILE_VERSION;
  2452     entry->common.flags = flags;
  2454     /* copy the subject */
  2455     rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
  2456     if ( rv != SECSuccess ) {
  2457 	goto loser;
  2460     entry->ncerts = 1;
  2461     entry->nemailAddrs = 0;
  2462     /* copy nickname */
  2463     if ( nickname && ( *nickname != '\0' ) ) {
  2464 	nnlen = PORT_Strlen(nickname) + 1;
  2465 	entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
  2466 	if ( entry->nickname == NULL ) {
  2467 	    goto loser;
  2470 	PORT_Memcpy(entry->nickname, nickname, nnlen);
  2471     } else {
  2472 	entry->nickname = NULL;
  2475     /* copy email addr */
  2476     if ( emailAddr && ( *emailAddr != '\0' ) ) {
  2477 	emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
  2478 	if ( emailAddr == NULL ) {
  2479 	    entry->emailAddrs = NULL;
  2480 	    goto loser;
  2483 	eaddrlen = PORT_Strlen(emailAddr) + 1;
  2484 	entry->emailAddrs = (char **)PORT_ArenaAlloc(arena, sizeof(char *));
  2485 	if ( entry->emailAddrs == NULL ) {
  2486 	    PORT_Free(emailAddr);
  2487 	    goto loser;
  2489 	entry->emailAddrs[0] = PORT_ArenaStrdup(arena,emailAddr);
  2490 	if (entry->emailAddrs[0]) {
  2491 	    entry->nemailAddrs = 1;
  2494 	PORT_Free(emailAddr);
  2495     } else {
  2496 	entry->emailAddrs = NULL;
  2499     /* allocate space for certKeys and keyIDs */
  2500     entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
  2501     entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
  2502     if ( ( entry->certKeys == NULL ) || ( entry->keyIDs == NULL ) ) {
  2503 	goto loser;
  2506     /* copy the certKey and keyID */
  2507     rv = SECITEM_CopyItem(arena, &entry->certKeys[0], certKey);
  2508     if ( rv != SECSuccess ) {
  2509 	goto loser;
  2511     rv = SECITEM_CopyItem(arena, &entry->keyIDs[0], keyID);
  2512     if ( rv != SECSuccess ) {
  2513 	goto loser;
  2516     return(entry);
  2517 loser:
  2518     if ( arena ) {
  2519 	PORT_FreeArena(arena, PR_FALSE);
  2522     return(NULL);
  2525 /*
  2526  * delete a subject entry
  2527  */
  2528 static SECStatus
  2529 DeleteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
  2531     SECItem dbkey;
  2532     PLArenaPool *arena = NULL;
  2533     SECStatus rv;
  2535     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2536     if ( arena == NULL ) {
  2537 	goto loser;
  2540     rv = EncodeDBSubjectKey(derSubject, arena, &dbkey);
  2541     if ( rv != SECSuccess ) {
  2542 	goto loser;
  2545     rv = DeleteDBEntry(handle, certDBEntryTypeSubject, &dbkey);
  2546     if ( rv == SECFailure ) {
  2547 	goto loser;
  2550     PORT_FreeArena(arena, PR_FALSE);
  2551     return(SECSuccess);
  2553 loser:
  2554     if ( arena ) {
  2555 	PORT_FreeArena(arena, PR_FALSE);
  2558     return(SECFailure);
  2561 /*
  2562  * Read the subject entry
  2563  */
  2564 static certDBEntrySubject *
  2565 ReadDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
  2567     PLArenaPool *arena = NULL;
  2568     PLArenaPool *tmparena = NULL;
  2569     certDBEntrySubject *entry;
  2570     SECItem dbkey;
  2571     SECItem dbentry;
  2572     SECStatus rv;
  2574     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2575     if ( arena == NULL ) {
  2576 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2577 	goto loser;
  2580     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2581     if ( tmparena == NULL ) {
  2582 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2583 	goto loser;
  2586     entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
  2587 						sizeof(certDBEntrySubject));
  2588     if ( entry == NULL ) {
  2589 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2590 	goto loser;
  2592     entry->common.arena = arena;
  2593     entry->common.type = certDBEntryTypeSubject;
  2595     rv = EncodeDBSubjectKey(derSubject, tmparena, &dbkey);
  2596     if ( rv != SECSuccess ) {
  2597 	goto loser;
  2600     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
  2601     if ( rv == SECFailure ) {
  2602 	goto loser;
  2605     rv = DecodeDBSubjectEntry(entry, &dbentry, derSubject);
  2606     if ( rv == SECFailure ) {
  2607 	goto loser;
  2610     PORT_FreeArena(tmparena, PR_FALSE);
  2611     return(entry);
  2613 loser:
  2614     if ( tmparena ) {
  2615 	PORT_FreeArena(tmparena, PR_FALSE);
  2617     if ( arena ) {
  2618 	PORT_FreeArena(arena, PR_FALSE);
  2621     return(NULL);
  2624 /*
  2625  * Encode a subject name entry into byte stream suitable for
  2626  * the database
  2627  */
  2628 static SECStatus
  2629 WriteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySubject *entry)
  2631     SECItem dbitem, dbkey;
  2632     PLArenaPool *tmparena = NULL;
  2633     SECStatus rv;
  2635     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2636     if ( tmparena == NULL ) {
  2637 	goto loser;
  2640     rv = EncodeDBSubjectEntry(entry, tmparena, &dbitem);
  2641     if ( rv != SECSuccess ) {
  2642 	goto loser;
  2645     rv = EncodeDBSubjectKey(&entry->derSubject, tmparena, &dbkey);
  2646     if ( rv != SECSuccess ) {
  2647 	goto loser;
  2650     /* now write it to the database */
  2651     rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
  2652     if ( rv != SECSuccess ) {
  2653 	goto loser;
  2656     PORT_FreeArena(tmparena, PR_FALSE);
  2657     return(SECSuccess);
  2659 loser:
  2660     if ( tmparena ) {
  2661 	PORT_FreeArena(tmparena, PR_FALSE);
  2663     return(SECFailure);
  2667 typedef enum { nsslowcert_remove, nsslowcert_add } nsslowcertUpdateType;
  2669 static SECStatus
  2670 nsslowcert_UpdateSubjectEmailAddr(NSSLOWCERTCertDBHandle *dbhandle, 
  2671 	SECItem *derSubject, char *emailAddr, nsslowcertUpdateType updateType)
  2673     certDBEntrySubject *entry = NULL;
  2674     int index = -1, i;
  2675     SECStatus rv;
  2677     if (emailAddr) { 
  2678 	emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
  2679 	if (emailAddr == NULL) {
  2680 	    return SECFailure;
  2682     } else {
  2683 	return SECSuccess;
  2686     entry = ReadDBSubjectEntry(dbhandle,derSubject);    
  2687     if (entry == NULL) {
  2688 	rv = SECFailure;
  2689 	goto done;
  2692     for (i=0; i < (int)(entry->nemailAddrs); i++) {
  2693         if (PORT_Strcmp(entry->emailAddrs[i],emailAddr) == 0) {
  2694 	    index = i;
  2698     if (updateType == nsslowcert_remove) {
  2699 	if (index == -1) {
  2700 	    rv = SECSuccess;
  2701 	    goto done;
  2703 	entry->nemailAddrs--;
  2704 	for (i=index; i < (int)(entry->nemailAddrs); i++) {
  2705 	   entry->emailAddrs[i] = entry->emailAddrs[i+1];
  2707     } else {
  2708 	char **newAddrs = NULL;
  2710 	if (index != -1) {
  2711 	    rv = SECSuccess;
  2712 	    goto done;
  2714 	newAddrs = (char **)PORT_ArenaAlloc(entry->common.arena,
  2715 		(entry->nemailAddrs+1)* sizeof(char *));
  2716 	if (!newAddrs) {
  2717 	    rv = SECFailure;
  2718 	    goto done;
  2720 	for (i=0; i < (int)(entry->nemailAddrs); i++) {
  2721 	   newAddrs[i] = entry->emailAddrs[i];
  2723 	newAddrs[entry->nemailAddrs] = 
  2724 			PORT_ArenaStrdup(entry->common.arena,emailAddr);
  2725 	if (!newAddrs[entry->nemailAddrs]) {
  2726 	   rv = SECFailure;
  2727 	   goto done;
  2729 	entry->emailAddrs = newAddrs;
  2730 	entry->nemailAddrs++;
  2733     /* delete the subject entry */
  2734     DeleteDBSubjectEntry(dbhandle, derSubject);
  2736     /* write the new one */
  2737     rv = WriteDBSubjectEntry(dbhandle, entry);
  2739   done:
  2740     if (entry) DestroyDBEntry((certDBEntry *)entry);
  2741     if (emailAddr) PORT_Free(emailAddr);
  2742     return rv;
  2745 /*
  2746  * writes a nickname to an existing subject entry that does not currently
  2747  * have one
  2748  */
  2749 static SECStatus
  2750 AddNicknameToSubject(NSSLOWCERTCertDBHandle *dbhandle,
  2751 			NSSLOWCERTCertificate *cert, char *nickname)
  2753     certDBEntrySubject *entry;
  2754     SECStatus rv;
  2756     if ( nickname == NULL ) {
  2757 	return(SECFailure);
  2760     entry = ReadDBSubjectEntry(dbhandle,&cert->derSubject);
  2761     PORT_Assert(entry != NULL);
  2762     if ( entry == NULL ) {
  2763 	goto loser;
  2766     PORT_Assert(entry->nickname == NULL);
  2767     if ( entry->nickname != NULL ) {
  2768 	goto loser;
  2771     entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
  2773     if ( entry->nickname == NULL ) {
  2774 	goto loser;
  2777     /* delete the subject entry */
  2778     DeleteDBSubjectEntry(dbhandle, &cert->derSubject);
  2780     /* write the new one */
  2781     rv = WriteDBSubjectEntry(dbhandle, entry);
  2782     if ( rv != SECSuccess ) {
  2783 	goto loser;
  2786     return(SECSuccess);
  2788 loser:
  2789     return(SECFailure);
  2792 /*
  2793  * create a new version entry
  2794  */
  2795 static certDBEntryVersion *
  2796 NewDBVersionEntry(unsigned int flags)
  2798     PLArenaPool *arena = NULL;
  2799     certDBEntryVersion *entry;
  2801     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2802     if ( arena == NULL ) {
  2803 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2804 	goto loser;
  2807     entry = (certDBEntryVersion *)PORT_ArenaAlloc(arena,
  2808 					       sizeof(certDBEntryVersion));
  2809     if ( entry == NULL ) {
  2810 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2811 	goto loser;
  2813     entry->common.arena = arena;
  2814     entry->common.type = certDBEntryTypeVersion;
  2815     entry->common.version = CERT_DB_FILE_VERSION;
  2816     entry->common.flags = flags;
  2818     return(entry);
  2819 loser:
  2820     if ( arena ) {
  2821 	PORT_FreeArena(arena, PR_FALSE);
  2824     return(NULL);
  2827 /*
  2828  * Read the version entry
  2829  */
  2830 static certDBEntryVersion *
  2831 ReadDBVersionEntry(NSSLOWCERTCertDBHandle *handle)
  2833     PLArenaPool *arena = NULL;
  2834     PLArenaPool *tmparena = NULL;
  2835     certDBEntryVersion *entry;
  2836     SECItem dbkey;
  2837     SECItem dbentry;
  2838     SECStatus rv;
  2840     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2841     if ( arena == NULL ) {
  2842 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2843 	goto loser;
  2846     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2847     if ( tmparena == NULL ) {
  2848 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2849 	goto loser;
  2852     entry = PORT_ArenaZNew(arena, certDBEntryVersion);
  2853     if ( entry == NULL ) {
  2854 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2855 	goto loser;
  2857     entry->common.arena = arena;
  2858     entry->common.type = certDBEntryTypeVersion;
  2860     /* now get the database key and format it */
  2861     dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
  2862     dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
  2863     if ( dbkey.data == NULL ) {
  2864 	goto loser;
  2866     PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
  2867 	      SEC_DB_VERSION_KEY_LEN);
  2869     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
  2870     if (rv != SECSuccess) {
  2871 	goto loser;
  2874     PORT_FreeArena(tmparena, PR_FALSE);
  2875     return(entry);
  2877 loser:
  2878     if ( tmparena ) {
  2879 	PORT_FreeArena(tmparena, PR_FALSE);
  2881     if ( arena ) {
  2882 	PORT_FreeArena(arena, PR_FALSE);
  2885     return(NULL);
  2889 /*
  2890  * Encode a version entry into byte stream suitable for
  2891  * the database
  2892  */
  2893 static SECStatus
  2894 WriteDBVersionEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryVersion *entry)
  2896     SECItem dbitem, dbkey;
  2897     PLArenaPool *tmparena = NULL;
  2898     SECStatus rv;
  2900     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  2901     if ( tmparena == NULL ) {
  2902 	goto loser;
  2905     /* allocate space for encoded database record, including space
  2906      * for low level header
  2907      */
  2908     dbitem.len = SEC_DB_ENTRY_HEADER_LEN;
  2910     dbitem.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbitem.len);
  2911     if ( dbitem.data == NULL) {
  2912 	PORT_SetError(SEC_ERROR_NO_MEMORY);
  2913 	goto loser;
  2916     /* now get the database key and format it */
  2917     dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
  2918     dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
  2919     if ( dbkey.data == NULL ) {
  2920 	goto loser;
  2922     PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
  2923 	      SEC_DB_VERSION_KEY_LEN);
  2925     /* now write it to the database */
  2926     rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
  2927     if ( rv != SECSuccess ) {
  2928 	goto loser;
  2931     PORT_FreeArena(tmparena, PR_FALSE);
  2932     return(SECSuccess);
  2934 loser:
  2935     if ( tmparena ) {
  2936 	PORT_FreeArena(tmparena, PR_FALSE);
  2938     return(SECFailure);
  2941 /*
  2942  * cert is no longer a perm cert, but will remain a temp cert
  2943  */
  2944 static SECStatus
  2945 RemovePermSubjectNode(NSSLOWCERTCertificate *cert)
  2947     certDBEntrySubject *entry;
  2948     unsigned int i;
  2949     SECStatus rv;
  2951     entry = ReadDBSubjectEntry(cert->dbhandle,&cert->derSubject);
  2952     if ( entry == NULL ) {
  2953 	return(SECFailure);
  2956     PORT_Assert(entry->ncerts);
  2957     rv = SECFailure;
  2959     if ( entry->ncerts > 1 ) {
  2960 	for ( i = 0; i < entry->ncerts; i++ ) {
  2961 	    if ( SECITEM_CompareItem(&entry->certKeys[i], &cert->certKey) ==
  2962 		SECEqual ) {
  2963 		/* copy rest of list forward one entry */
  2964 		for ( i = i + 1; i < entry->ncerts; i++ ) {
  2965 		    entry->certKeys[i-1] = entry->certKeys[i];
  2966 		    entry->keyIDs[i-1] = entry->keyIDs[i];
  2968 		entry->ncerts--;
  2969 		DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
  2970 		rv = WriteDBSubjectEntry(cert->dbhandle, entry);
  2971 		break;
  2974     } else {
  2975 	/* no entries left, delete the perm entry in the DB */
  2976 	if ( entry->emailAddrs ) {
  2977 	    /* if the subject had an email record, then delete it too */
  2978 	    for (i=0; i < entry->nemailAddrs; i++) {
  2979 		DeleteDBSMimeEntry(cert->dbhandle, entry->emailAddrs[i]);
  2982 	if ( entry->nickname ) {
  2983 	    DeleteDBNicknameEntry(cert->dbhandle, entry->nickname);
  2986 	DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
  2988     DestroyDBEntry((certDBEntry *)entry);
  2990     return(rv);
  2993 /*
  2994  * add a cert to the perm subject list
  2995  */
  2996 static SECStatus
  2997 AddPermSubjectNode(certDBEntrySubject *entry, NSSLOWCERTCertificate *cert, 
  2998 								char *nickname)
  3000     SECItem *newCertKeys, *newKeyIDs;
  3001     unsigned int i, new_i;
  3002     SECStatus rv;
  3003     unsigned int ncerts;
  3005     PORT_Assert(entry);    
  3006     ncerts = entry->ncerts;
  3008     if ( nickname && entry->nickname ) {
  3009 	/* nicknames must be the same */
  3010 	PORT_Assert(PORT_Strcmp(nickname, entry->nickname) == 0);
  3013     if ( ( entry->nickname == NULL ) && ( nickname != NULL ) ) {
  3014 	/* copy nickname into the entry */
  3015 	entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
  3016 	if ( entry->nickname == NULL ) {
  3017 	    return(SECFailure);
  3021     /* a DB entry already exists, so add this cert */
  3022     newCertKeys = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
  3023     newKeyIDs   = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
  3025     if ( ( newCertKeys == NULL ) || ( newKeyIDs == NULL ) ) {
  3026 	    return(SECFailure);
  3029     /* Step 1: copy certs older than "cert" into new entry. */
  3030     for ( i = 0, new_i=0; i < ncerts; i++ ) {
  3031 	NSSLOWCERTCertificate *cmpcert;
  3032 	PRBool isNewer;
  3033 	cmpcert = nsslowcert_FindCertByKey(cert->dbhandle,
  3034 						  &entry->certKeys[i]);
  3035 	/* The entry has been corrupted, remove it from the list */
  3036 	if (!cmpcert) {
  3037 	    continue;
  3040 	isNewer = nsslowcert_IsNewer(cert, cmpcert);
  3041 	nsslowcert_DestroyCertificate(cmpcert);
  3042 	if ( isNewer ) 
  3043 	    break;
  3044 	/* copy this cert entry */
  3045 	newCertKeys[new_i] = entry->certKeys[i];
  3046 	newKeyIDs[new_i]   = entry->keyIDs[i];
  3047 	new_i++; 
  3050     /* Step 2: Add "cert" to the entry. */
  3051     rv = SECITEM_CopyItem(entry->common.arena, &newCertKeys[new_i],
  3052 			      &cert->certKey);
  3053     if ( rv != SECSuccess ) {
  3054 	return(SECFailure);
  3056     rv = SECITEM_CopyItem(entry->common.arena, &newKeyIDs[new_i],
  3057 			      &cert->subjectKeyID);
  3058     if ( rv != SECSuccess ) {
  3059 	return(SECFailure);
  3061     new_i++;
  3063     /* Step 3: copy remaining certs (if any) from old entry to new. */
  3064     for ( ; i < ncerts; i++ ,new_i++) {
  3065 	newCertKeys[new_i] = entry->certKeys[i];
  3066 	newKeyIDs[new_i]   = entry->keyIDs[i];
  3069     /* update certKeys and keyIDs */
  3070     entry->certKeys = newCertKeys;
  3071     entry->keyIDs   = newKeyIDs;
  3073     /* set new count value */
  3074     entry->ncerts = new_i;
  3076     DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
  3077     rv = WriteDBSubjectEntry(cert->dbhandle, entry);
  3078     return(rv);
  3082 SECStatus
  3083 nsslowcert_TraversePermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
  3084 				 SECItem *derSubject,
  3085 				 NSSLOWCERTCertCallback cb, void *cbarg)
  3087     certDBEntrySubject *entry;
  3088     unsigned int i;
  3089     NSSLOWCERTCertificate *cert;
  3090     SECStatus rv = SECSuccess;
  3092     entry = ReadDBSubjectEntry(handle, derSubject);
  3094     if ( entry == NULL ) {
  3095 	return(SECFailure);
  3098     for( i = 0; i < entry->ncerts; i++ ) {
  3099 	cert = nsslowcert_FindCertByKey(handle, &entry->certKeys[i]);
  3100 	if (!cert) {
  3101 	    continue;
  3103 	rv = (* cb)(cert, cbarg);
  3104 	nsslowcert_DestroyCertificate(cert);
  3105 	if ( rv == SECFailure ) {
  3106 	    break;
  3110     DestroyDBEntry((certDBEntry *)entry);
  3112     return(rv);
  3115 int
  3116 nsslowcert_NumPermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
  3117 							 SECItem *derSubject)
  3119     certDBEntrySubject *entry;
  3120     int ret;
  3122     entry = ReadDBSubjectEntry(handle, derSubject);
  3124     if ( entry == NULL ) {
  3125 	return(SECFailure);
  3128     ret = entry->ncerts;
  3130     DestroyDBEntry((certDBEntry *)entry);
  3132     return(ret);
  3135 SECStatus
  3136 nsslowcert_TraversePermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
  3137 		 	char *nickname, NSSLOWCERTCertCallback cb, void *cbarg)
  3139     certDBEntryNickname *nnentry = NULL;
  3140     certDBEntrySMime *smentry = NULL;
  3141     SECStatus rv;
  3142     SECItem *derSubject = NULL;
  3144     nnentry = ReadDBNicknameEntry(handle, nickname);
  3145     if ( nnentry ) {
  3146 	derSubject = &nnentry->subjectName;
  3147     } else {
  3148 	smentry = nsslowcert_ReadDBSMimeEntry(handle, nickname);
  3149 	if ( smentry ) {
  3150 	    derSubject = &smentry->subjectName;
  3154     if ( derSubject ) {
  3155 	rv = nsslowcert_TraversePermCertsForSubject(handle, derSubject,
  3156 					      cb, cbarg);
  3157     } else {
  3158 	rv = SECFailure;
  3161     if ( nnentry ) {
  3162 	DestroyDBEntry((certDBEntry *)nnentry);
  3164     if ( smentry ) {
  3165 	DestroyDBEntry((certDBEntry *)smentry);
  3168     return(rv);
  3171 int
  3172 nsslowcert_NumPermCertsForNickname(NSSLOWCERTCertDBHandle *handle, 
  3173 								char *nickname)
  3175     certDBEntryNickname *entry;
  3176     int ret;
  3178     entry = ReadDBNicknameEntry(handle, nickname);
  3180     if ( entry ) {
  3181 	ret = nsslowcert_NumPermCertsForSubject(handle, &entry->subjectName);
  3182 	DestroyDBEntry((certDBEntry *)entry);
  3183     } else {
  3184 	ret = 0;
  3186     return(ret);
  3189 /*
  3190  * add a nickname to a cert that doesn't have one
  3191  */
  3192 static SECStatus
  3193 AddNicknameToPermCert(NSSLOWCERTCertDBHandle *dbhandle,
  3194 				NSSLOWCERTCertificate *cert, char *nickname)
  3196     certDBEntryCert *entry;
  3197     int rv;
  3199     entry = cert->dbEntry;
  3200     PORT_Assert(entry != NULL);
  3201     if ( entry == NULL ) {
  3202 	goto loser;
  3205     pkcs11_freeNickname(entry->nickname,entry->nicknameSpace);
  3206     entry->nickname = NULL;
  3207     entry->nickname = pkcs11_copyNickname(nickname,entry->nicknameSpace,
  3208 					sizeof(entry->nicknameSpace));
  3210     rv = WriteDBCertEntry(dbhandle, entry);
  3211     if ( rv ) {
  3212 	goto loser;
  3215     pkcs11_freeNickname(cert->nickname,cert->nicknameSpace);
  3216     cert->nickname = NULL;
  3217     cert->nickname = pkcs11_copyNickname(nickname,cert->nicknameSpace,
  3218 					sizeof(cert->nicknameSpace));
  3220     return(SECSuccess);
  3222 loser:
  3223     return(SECFailure);
  3226 /*
  3227  * add a nickname to a cert that is already in the perm database, but doesn't
  3228  * have one yet (it is probably an e-mail cert).
  3229  */
  3230 SECStatus
  3231 nsslowcert_AddPermNickname(NSSLOWCERTCertDBHandle *dbhandle,
  3232 				NSSLOWCERTCertificate *cert, char *nickname)
  3234     SECStatus rv = SECFailure;
  3235     certDBEntrySubject *entry = NULL;
  3236     certDBEntryNickname *nicknameEntry = NULL;
  3238     nsslowcert_LockDB(dbhandle);
  3240     entry = ReadDBSubjectEntry(dbhandle, &cert->derSubject);
  3241     if (entry == NULL) goto loser;
  3243     if ( entry->nickname == NULL ) {
  3245 	/* no nickname for subject */
  3246 	rv = AddNicknameToSubject(dbhandle, cert, nickname);
  3247 	if ( rv != SECSuccess ) {
  3248 	    goto loser;
  3250 	rv = AddNicknameToPermCert(dbhandle, cert, nickname);
  3251 	if ( rv != SECSuccess ) {
  3252 	    goto loser;
  3254 	nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
  3255 	if ( nicknameEntry == NULL ) {
  3256 	    goto loser;
  3259 	rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
  3260 	if ( rv != SECSuccess ) {
  3261 	    goto loser;
  3263     } else {
  3264 	/* subject already has a nickname */
  3265 	rv = AddNicknameToPermCert(dbhandle, cert, entry->nickname);
  3266 	if ( rv != SECSuccess ) {
  3267 	    goto loser;
  3269 	/* make sure nickname entry exists. If the database was corrupted,
  3270 	 * we may have lost the nickname entry. Add it back now  */
  3271 	nicknameEntry = ReadDBNicknameEntry(dbhandle, entry->nickname);
  3272 	if (nicknameEntry == NULL ) {
  3273 	    nicknameEntry = NewDBNicknameEntry(entry->nickname, 
  3274 							&cert->derSubject, 0);
  3275 	    if ( nicknameEntry == NULL ) {
  3276 		goto loser;
  3279 	    rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
  3280 	    if ( rv != SECSuccess ) {
  3281 		goto loser;
  3285     rv = SECSuccess;
  3287 loser:
  3288     if (entry) {
  3289 	DestroyDBEntry((certDBEntry *)entry);
  3291     if (nicknameEntry) {
  3292 	DestroyDBEntry((certDBEntry *)nicknameEntry);
  3294     nsslowcert_UnlockDB(dbhandle);
  3295     return(rv);
  3298 static certDBEntryCert *
  3299 AddCertToPermDB(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTCertificate *cert,
  3300 		char *nickname, NSSLOWCERTCertTrust *trust)
  3302     certDBEntryCert *certEntry = NULL;
  3303     certDBEntryNickname *nicknameEntry = NULL;
  3304     certDBEntrySubject *subjectEntry = NULL;
  3305     int state = 0;
  3306     SECStatus rv;
  3307     PRBool donnentry = PR_FALSE;
  3309     if ( nickname ) {
  3310 	donnentry = PR_TRUE;
  3313     subjectEntry = ReadDBSubjectEntry(handle, &cert->derSubject);
  3315     if ( subjectEntry && subjectEntry->nickname ) {
  3316 	donnentry = PR_FALSE;
  3317 	nickname = subjectEntry->nickname;
  3320     certEntry = NewDBCertEntry(&cert->derCert, nickname, trust, 0);
  3321     if ( certEntry == NULL ) {
  3322 	goto loser;
  3325     if ( donnentry ) {
  3326 	nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
  3327 	if ( nicknameEntry == NULL ) {
  3328 	    goto loser;
  3332     rv = WriteDBCertEntry(handle, certEntry);
  3333     if ( rv != SECSuccess ) {
  3334 	goto loser;
  3336     state = 1;
  3338     if ( nicknameEntry ) {
  3339 	rv = WriteDBNicknameEntry(handle, nicknameEntry);
  3340 	if ( rv != SECSuccess ) {
  3341 	    goto loser;
  3345     state = 2;
  3347     /* "Change" handles if necessary */
  3348     cert->dbhandle = handle;
  3350     /* add to or create new subject entry */
  3351     if ( subjectEntry ) {
  3352 	/* REWRITE BASED ON SUBJECT ENTRY */
  3353 	rv = AddPermSubjectNode(subjectEntry, cert, nickname);
  3354 	if ( rv != SECSuccess ) {
  3355 	    goto loser;
  3357     } else {
  3358 	/* make a new subject entry - this case is only used when updating
  3359 	 * an old version of the database.  This is OK because the oldnickname
  3360 	 * db format didn't allow multiple certs with the same subject.
  3361 	 */
  3362 	/* where does subjectKeyID and certKey come from? */
  3363 	subjectEntry = NewDBSubjectEntry(&cert->derSubject, &cert->certKey,
  3364 					 &cert->subjectKeyID, nickname,
  3365 					 NULL, 0);
  3366 	if ( subjectEntry == NULL ) {
  3367 	    goto loser;
  3369 	rv = WriteDBSubjectEntry(handle, subjectEntry);
  3370 	if ( rv != SECSuccess ) {
  3371 	    goto loser;
  3375     state = 3;
  3377     if ( nicknameEntry ) {
  3378 	DestroyDBEntry((certDBEntry *)nicknameEntry);
  3381     if ( subjectEntry ) {
  3382 	DestroyDBEntry((certDBEntry *)subjectEntry);
  3385     return(certEntry);
  3387 loser:
  3388     /* don't leave partial entry in the database */
  3389     if ( state > 0 ) {
  3390 	rv = DeleteDBCertEntry(handle, &cert->certKey);
  3392     if ( ( state > 1 ) && donnentry ) {
  3393 	rv = DeleteDBNicknameEntry(handle, nickname);
  3395     if ( state > 2 ) {
  3396 	rv = DeleteDBSubjectEntry(handle, &cert->derSubject);
  3398     if ( certEntry ) {
  3399 	DestroyDBEntry((certDBEntry *)certEntry);
  3401     if ( nicknameEntry ) {
  3402 	DestroyDBEntry((certDBEntry *)nicknameEntry);
  3404     if ( subjectEntry ) {
  3405 	DestroyDBEntry((certDBEntry *)subjectEntry);
  3408     return(NULL);
  3411 /* forward declaration */
  3412 static SECStatus
  3413 UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb);
  3415 /*
  3416  * version 8 uses the same schema as version 7. The only differences are
  3417  * 1) version 8 db uses the blob shim to store data entries > 32k.
  3418  * 2) version 8 db sets the db block size to 32k.
  3419  * both of these are dealt with by the handle.
  3420  */
  3422 static SECStatus
  3423 UpdateV8DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
  3425     return UpdateV7DB(handle,updatedb);
  3429 /*
  3430  * we could just blindly sequence through reading key data pairs and writing
  3431  * them back out, but some cert.db's have gotten quite large and may have some
  3432  * subtle corruption problems, so instead we cycle through the certs and
  3433  * CRL's and S/MIME profiles and rebuild our subject lists from those records.
  3434  */
  3435 static SECStatus
  3436 UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
  3438     DBT key, data;
  3439     int ret;
  3440     NSSLOWCERTCertificate *cert;
  3441     PRBool isKRL = PR_FALSE;
  3442     certDBEntryType entryType;
  3443     SECItem dbEntry, dbKey;
  3444     certDBEntryRevocation crlEntry;
  3445     certDBEntryCert certEntry;
  3446     certDBEntrySMime smimeEntry;
  3447     SECStatus rv;
  3449     ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
  3451     if ( ret ) {
  3452 	return(SECFailure);
  3455     do {
  3456 	unsigned char *dataBuf = (unsigned char *)data.data;
  3457 	unsigned char *keyBuf = (unsigned char *)key.data;
  3458 	dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
  3459 	dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
  3460  	entryType = (certDBEntryType) keyBuf[0];
  3461 	dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
  3462 	dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
  3463 	if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
  3464 	    continue;
  3467 	switch (entryType) {
  3468 	/* these entries will get regenerated as we read the 
  3469 	 * rest of the data from the database */
  3470 	case certDBEntryTypeVersion:
  3471 	case certDBEntryTypeSubject:
  3472 	case certDBEntryTypeContentVersion:
  3473 	case certDBEntryTypeNickname:
  3474 	/* smime profiles need entries created after the certs have
  3475          * been imported, loop over them in a second run */
  3476 	case certDBEntryTypeSMimeProfile:
  3477 	    break;
  3479 	case certDBEntryTypeCert:
  3480 	    /* decode Entry */
  3481     	    certEntry.common.version = (unsigned int)dataBuf[0];
  3482 	    certEntry.common.type = entryType;
  3483 	    certEntry.common.flags = (unsigned int)dataBuf[2];
  3484 	    rv = DecodeDBCertEntry(&certEntry,&dbEntry);
  3485 	    if (rv != SECSuccess) {
  3486 		break;
  3488 	    /* should we check for existing duplicates? */
  3489 	    cert = nsslowcert_DecodeDERCertificate(&certEntry.derCert, 
  3490 						certEntry.nickname);
  3491 	    if (cert) {
  3492 		nsslowcert_UpdatePermCert(handle, cert, certEntry.nickname,
  3493 					 		&certEntry.trust);
  3494 		nsslowcert_DestroyCertificate(cert);
  3496 	    /* free any data the decode may have allocated. */
  3497 	    pkcs11_freeStaticData(certEntry.derCert.data, 
  3498 						certEntry.derCertSpace);
  3499 	    pkcs11_freeNickname(certEntry.nickname, certEntry.nicknameSpace);
  3500 	    break;
  3502 	case certDBEntryTypeKeyRevocation:
  3503 	    isKRL = PR_TRUE;
  3504 	    /* fall through */
  3505 	case certDBEntryTypeRevocation:
  3506     	    crlEntry.common.version = (unsigned int)dataBuf[0];
  3507 	    crlEntry.common.type = entryType;
  3508 	    crlEntry.common.flags = (unsigned int)dataBuf[2];
  3509 	    crlEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  3510 	    if (crlEntry.common.arena == NULL) {
  3511 		break;
  3513 	    rv = DecodeDBCrlEntry(&crlEntry,&dbEntry);
  3514 	    if (rv != SECSuccess) {
  3515 		break;
  3517 	    nsslowcert_UpdateCrl(handle, &crlEntry.derCrl, &dbKey, 
  3518 						crlEntry.url, isKRL);
  3519 	    /* free data allocated by the decode */
  3520 	    PORT_FreeArena(crlEntry.common.arena, PR_FALSE);
  3521 	    crlEntry.common.arena = NULL;
  3522 	    break;
  3524 	default:
  3525 	    break;
  3527     } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
  3529     /* now loop again updating just the SMimeProfile. */
  3530     ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
  3532     if ( ret ) {
  3533 	return(SECFailure);
  3536     do {
  3537 	unsigned char *dataBuf = (unsigned char *)data.data;
  3538 	unsigned char *keyBuf = (unsigned char *)key.data;
  3539 	dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
  3540 	dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
  3541  	entryType = (certDBEntryType) keyBuf[0];
  3542 	if (entryType != certDBEntryTypeSMimeProfile) {
  3543 	    continue;
  3545 	dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
  3546 	dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
  3547 	if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
  3548 	    continue;
  3550         smimeEntry.common.version = (unsigned int)dataBuf[0];
  3551 	smimeEntry.common.type = entryType;
  3552 	smimeEntry.common.flags = (unsigned int)dataBuf[2];
  3553 	smimeEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  3554 	/* decode entry */
  3555 	rv = DecodeDBSMimeEntry(&smimeEntry,&dbEntry,(char *)dbKey.data);
  3556 	if (rv == SECSuccess) {
  3557 	    nsslowcert_UpdateSMimeProfile(handle, smimeEntry.emailAddr,
  3558 		&smimeEntry.subjectName, &smimeEntry.smimeOptions,
  3559 						 &smimeEntry.optionsDate);
  3561 	PORT_FreeArena(smimeEntry.common.arena, PR_FALSE);
  3562 	smimeEntry.common.arena = NULL;
  3563     } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
  3565     (* updatedb->close)(updatedb);
  3567     /* a database update is a good time to go back and verify the integrity of
  3568      * the keys and certs */
  3569     handle->dbVerify = PR_TRUE; 
  3570     return(SECSuccess);
  3573 /*
  3574  * NOTE - Version 6 DB did not go out to the real world in a release,
  3575  * so we can remove this function in a later release.
  3576  */
  3577 static SECStatus
  3578 UpdateV6DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
  3580     int ret;
  3581     DBT key, data;
  3582     unsigned char *buf, *tmpbuf = NULL;
  3583     certDBEntryType type;
  3584     certDBEntryNickname *nnEntry = NULL;
  3585     certDBEntrySubject *subjectEntry = NULL;
  3586     certDBEntrySMime *emailEntry = NULL;
  3587     char *nickname;
  3588     char *emailAddr;
  3589     SECStatus rv;
  3591     /*
  3592      * Sequence through the old database and copy all of the entries
  3593      * to the new database.  Subject name entries will have the new
  3594      * fields inserted into them (with zero length).
  3595      */
  3596     ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
  3597     if ( ret ) {
  3598 	return(SECFailure);
  3601     do {
  3602 	buf = (unsigned char *)data.data;
  3604 	if ( data.size >= 3 ) {
  3605 	    if ( buf[0] == 6 ) { /* version number */
  3606 		type = (certDBEntryType)buf[1];
  3607 		if ( type == certDBEntryTypeSubject ) {
  3608 		    /* expando subjecto entrieo */
  3609 		    tmpbuf = (unsigned char *)PORT_Alloc(data.size + 4);
  3610 		    if ( tmpbuf ) {
  3611 			/* copy header stuff */
  3612 			PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN + 2);
  3613 			/* insert 4 more bytes of zero'd header */
  3614 			PORT_Memset(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 2],
  3615 				    0, 4);
  3616 			/* copy rest of the data */
  3617 			PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
  3618 				    &buf[SEC_DB_ENTRY_HEADER_LEN + 2],
  3619 				    data.size - (SEC_DB_ENTRY_HEADER_LEN + 2));
  3621 			data.data = (void *)tmpbuf;
  3622 			data.size += 4;
  3623 			buf = tmpbuf;
  3625 		} else if ( type == certDBEntryTypeCert ) {
  3626 		    /* expando certo entrieo */
  3627 		    tmpbuf = (unsigned char *)PORT_Alloc(data.size + 3);
  3628 		    if ( tmpbuf ) {
  3629 			/* copy header stuff */
  3630 			PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN);
  3632 			/* copy trust flage, setting msb's to 0 */
  3633 			tmpbuf[SEC_DB_ENTRY_HEADER_LEN] = 0;
  3634 			tmpbuf[SEC_DB_ENTRY_HEADER_LEN+1] =
  3635 			    buf[SEC_DB_ENTRY_HEADER_LEN];
  3636 			tmpbuf[SEC_DB_ENTRY_HEADER_LEN+2] = 0;
  3637 			tmpbuf[SEC_DB_ENTRY_HEADER_LEN+3] =
  3638 			    buf[SEC_DB_ENTRY_HEADER_LEN+1];
  3639 			tmpbuf[SEC_DB_ENTRY_HEADER_LEN+4] = 0;
  3640 			tmpbuf[SEC_DB_ENTRY_HEADER_LEN+5] =
  3641 			    buf[SEC_DB_ENTRY_HEADER_LEN+2];
  3643 			/* copy rest of the data */
  3644 			PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
  3645 				    &buf[SEC_DB_ENTRY_HEADER_LEN + 3],
  3646 				    data.size - (SEC_DB_ENTRY_HEADER_LEN + 3));
  3648 			data.data = (void *)tmpbuf;
  3649 			data.size += 3;
  3650 			buf = tmpbuf;
  3655 		/* update the record version number */
  3656 		buf[0] = CERT_DB_FILE_VERSION;
  3658 		/* copy to the new database */
  3659 		ret = certdb_Put(handle->permCertDB, &key, &data, 0);
  3660 		if ( tmpbuf ) {
  3661 		    PORT_Free(tmpbuf);
  3662 		    tmpbuf = NULL;
  3666     } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
  3668     ret = certdb_Sync(handle->permCertDB, 0);
  3670     ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
  3671     if ( ret ) {
  3672 	return(SECFailure);
  3675     do {
  3676 	buf = (unsigned char *)data.data;
  3678 	if ( data.size >= 3 ) {
  3679 	    if ( buf[0] == CERT_DB_FILE_VERSION ) { /* version number */
  3680 		type = (certDBEntryType)buf[1];
  3681 		if ( type == certDBEntryTypeNickname ) {
  3682 		    nickname = &((char *)key.data)[1];
  3684 		    /* get the matching nickname entry in the new DB */
  3685 		    nnEntry = ReadDBNicknameEntry(handle, nickname);
  3686 		    if ( nnEntry == NULL ) {
  3687 			goto endloop;
  3690 		    /* find the subject entry pointed to by nickname */
  3691 		    subjectEntry = ReadDBSubjectEntry(handle,
  3692 						      &nnEntry->subjectName);
  3693 		    if ( subjectEntry == NULL ) {
  3694 			goto endloop;
  3697 		    subjectEntry->nickname =
  3698 			(char *)PORT_ArenaAlloc(subjectEntry->common.arena,
  3699 						key.size - 1);
  3700 		    if ( subjectEntry->nickname ) {
  3701 			PORT_Memcpy(subjectEntry->nickname, nickname,
  3702 				    key.size - 1);
  3703 			rv = WriteDBSubjectEntry(handle, subjectEntry);
  3705 		} else if ( type == certDBEntryTypeSMimeProfile ) {
  3706 		    emailAddr = &((char *)key.data)[1];
  3708 		    /* get the matching smime entry in the new DB */
  3709 		    emailEntry = nsslowcert_ReadDBSMimeEntry(handle, emailAddr);
  3710 		    if ( emailEntry == NULL ) {
  3711 			goto endloop;
  3714 		    /* find the subject entry pointed to by nickname */
  3715 		    subjectEntry = ReadDBSubjectEntry(handle,
  3716 						      &emailEntry->subjectName);
  3717 		    if ( subjectEntry == NULL ) {
  3718 			goto endloop;
  3721 		    subjectEntry->emailAddrs = (char **)
  3722 				PORT_ArenaAlloc(subjectEntry->common.arena,
  3723 						sizeof(char *));
  3724 		    if ( subjectEntry->emailAddrs ) {
  3725 			subjectEntry->emailAddrs[0] =
  3726 			     (char *)PORT_ArenaAlloc(subjectEntry->common.arena,
  3727 						key.size - 1);
  3728 			if ( subjectEntry->emailAddrs[0] ) {
  3729 			    PORT_Memcpy(subjectEntry->emailAddrs[0], emailAddr,
  3730 				    key.size - 1);
  3731 			    subjectEntry->nemailAddrs = 1;
  3732 			    rv = WriteDBSubjectEntry(handle, subjectEntry);
  3737 endloop:
  3738 		if ( subjectEntry ) {
  3739 		    DestroyDBEntry((certDBEntry *)subjectEntry);
  3740 		    subjectEntry = NULL;
  3742 		if ( nnEntry ) {
  3743 		    DestroyDBEntry((certDBEntry *)nnEntry);
  3744 		    nnEntry = NULL;
  3746 		if ( emailEntry ) {
  3747 		    DestroyDBEntry((certDBEntry *)emailEntry);
  3748 		    emailEntry = NULL;
  3752     } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
  3754     ret = certdb_Sync(handle->permCertDB, 0);
  3756     (* updatedb->close)(updatedb);
  3757     return(SECSuccess);
  3761 static SECStatus
  3762 updateV5Callback(NSSLOWCERTCertificate *cert, SECItem *k, void *pdata)
  3764     NSSLOWCERTCertDBHandle *handle;
  3765     certDBEntryCert *entry;
  3766     NSSLOWCERTCertTrust *trust;
  3768     handle = (NSSLOWCERTCertDBHandle *)pdata;
  3769     trust = &cert->dbEntry->trust;
  3771     /* SSL user certs can be used for email if they have an email addr */
  3772     if ( cert->emailAddr && ( trust->sslFlags & CERTDB_USER ) &&
  3773 	( trust->emailFlags == 0 ) ) {
  3774 	trust->emailFlags = CERTDB_USER;
  3776     /* servers didn't set the user flags on the server cert.. */
  3777     if (PORT_Strcmp(cert->dbEntry->nickname,"Server-Cert") == 0) {
  3778 	trust->sslFlags |= CERTDB_USER;
  3781     entry = AddCertToPermDB(handle, cert, cert->dbEntry->nickname,
  3782 			    &cert->dbEntry->trust);
  3783     if ( entry ) {
  3784 	DestroyDBEntry((certDBEntry *)entry);
  3787     return(SECSuccess);
  3790 static SECStatus
  3791 UpdateV5DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
  3793     NSSLOWCERTCertDBHandle updatehandle;
  3794     SECStatus rv;
  3796     updatehandle.permCertDB = updatedb;
  3797     updatehandle.dbMon = PZ_NewMonitor(nssILockCertDB);
  3798     updatehandle.dbVerify = 0;
  3799     updatehandle.ref      = 1; /* prevent premature close */
  3801     rv = nsslowcert_TraversePermCerts(&updatehandle, updateV5Callback,
  3802 			       (void *)handle);
  3804     PZ_DestroyMonitor(updatehandle.dbMon);
  3806     (* updatedb->close)(updatedb);
  3807     return(SECSuccess);
  3810 static PRBool
  3811 isV4DB(DB *db) {
  3812     DBT key,data;
  3813     int ret;
  3815     key.data = "Version";
  3816     key.size = 7;
  3818     ret = (*db->get)(db, &key, &data, 0);
  3819     if (ret) {
  3820 	return PR_FALSE;
  3823     if ((data.size == 1) && (*(unsigned char *)data.data <= 4))  {
  3824 	return PR_TRUE;
  3827     return PR_FALSE;
  3830 static SECStatus
  3831 UpdateV4DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
  3833     DBT key, data;
  3834     certDBEntryCert *entry, *entry2;
  3835     int ret;
  3836     PLArenaPool *arena = NULL;
  3837     NSSLOWCERTCertificate *cert;
  3839     ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
  3841     if ( ret ) {
  3842 	return(SECFailure);
  3845     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  3846     if (arena == NULL) {
  3847 	return(SECFailure);
  3850     do {
  3851 	if ( data.size != 1 ) { /* skip version number */
  3853 	    /* decode the old DB entry */
  3854 	    entry = (certDBEntryCert *)
  3855 		DecodeV4DBCertEntry((unsigned char*)data.data, data.size);
  3857 	    if ( entry ) {
  3858 		cert = nsslowcert_DecodeDERCertificate(&entry->derCert, 
  3859 						 entry->nickname);
  3861 		if ( cert != NULL ) {
  3862 		    /* add to new database */
  3863 		    entry2 = AddCertToPermDB(handle, cert, entry->nickname,
  3864 					     &entry->trust);
  3866 		    nsslowcert_DestroyCertificate(cert);
  3867 		    if ( entry2 ) {
  3868 			DestroyDBEntry((certDBEntry *)entry2);
  3871 		DestroyDBEntry((certDBEntry *)entry);
  3874     } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
  3876     PORT_FreeArena(arena, PR_FALSE);
  3877     (* updatedb->close)(updatedb);
  3878     return(SECSuccess);
  3882 /*
  3883  * return true if a database key conflict exists
  3884  */
  3885 PRBool
  3886 nsslowcert_CertDBKeyConflict(SECItem *derCert, NSSLOWCERTCertDBHandle *handle)
  3888     SECStatus rv;
  3889     DBT tmpdata;
  3890     DBT namekey;
  3891     int ret;
  3892     SECItem keyitem;
  3893     PLArenaPool *arena = NULL;
  3894     SECItem derKey;
  3896     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  3897     if ( arena == NULL ) {
  3898 	goto loser;
  3901     /* get the db key of the cert */
  3902     rv = nsslowcert_KeyFromDERCert(arena, derCert, &derKey);
  3903     if ( rv != SECSuccess ) {
  3904         goto loser;
  3907     rv = EncodeDBCertKey(&derKey, arena, &keyitem);
  3908     if ( rv != SECSuccess ) {
  3909 	goto loser;
  3912     namekey.data = keyitem.data;
  3913     namekey.size = keyitem.len;
  3915     ret = certdb_Get(handle->permCertDB, &namekey, &tmpdata, 0);
  3916     if ( ret == 0 ) {
  3917 	goto loser;
  3920     PORT_FreeArena(arena, PR_FALSE);
  3922     return(PR_FALSE);
  3923 loser:
  3924     if ( arena ) {
  3925 	PORT_FreeArena(arena, PR_FALSE);
  3928     return(PR_TRUE);
  3931 /*
  3932  * return true if a nickname conflict exists
  3933  * NOTE: caller must have already made sure that this exact cert
  3934  * doesn't exist in the DB
  3935  */
  3936 static PRBool
  3937 nsslowcert_CertNicknameConflict(char *nickname, SECItem *derSubject,
  3938 			 NSSLOWCERTCertDBHandle *handle)
  3940     PRBool rv;
  3941     certDBEntryNickname *entry;
  3943     if ( nickname == NULL ) {
  3944 	return(PR_FALSE);
  3947     entry = ReadDBNicknameEntry(handle, nickname);
  3949     if ( entry == NULL ) {
  3950 	/* no entry for this nickname, so no conflict */
  3951 	return(PR_FALSE);
  3954     rv = PR_TRUE;
  3955     if ( SECITEM_CompareItem(derSubject, &entry->subjectName) == SECEqual ) {
  3956 	/* if subject names are the same, then no conflict */
  3957 	rv = PR_FALSE;
  3960     DestroyDBEntry((certDBEntry *)entry);
  3961     return(rv);
  3964 #ifdef DBM_USING_NSPR
  3965 #define NO_RDONLY	PR_RDONLY
  3966 #define NO_RDWR		PR_RDWR
  3967 #define NO_CREATE	(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE)
  3968 #else
  3969 #define NO_RDONLY	O_RDONLY
  3970 #define NO_RDWR		O_RDWR
  3971 #define NO_CREATE	(O_RDWR | O_CREAT | O_TRUNC)
  3972 #endif
  3974 /*
  3975  * open an old database that needs to be updated
  3976  */
  3977 static DB *
  3978 nsslowcert_openolddb(NSSLOWCERTDBNameFunc namecb, void *cbarg, int version)
  3980     char * tmpname;
  3981     DB *updatedb = NULL;
  3983     tmpname = (* namecb)(cbarg, version);	/* get v6 db name */
  3984     if ( tmpname ) {
  3985 	updatedb = dbopen( tmpname, NO_RDONLY, 0600, DB_HASH, 0 );
  3986 	PORT_Free(tmpname);
  3988     return updatedb;
  3991 static SECStatus
  3992 openNewCertDB(const char *appName, const char *prefix, const char *certdbname, 
  3993     NSSLOWCERTCertDBHandle *handle, NSSLOWCERTDBNameFunc namecb, void *cbarg)
  3995     SECStatus rv;
  3996     certDBEntryVersion *versionEntry = NULL;
  3997     DB *updatedb = NULL;
  3998     int status = RDB_FAIL;
  4000     if (appName) {
  4001 	handle->permCertDB=rdbopen( appName, prefix, "cert", NO_CREATE, &status);
  4002     } else {
  4003 	handle->permCertDB=dbsopen(certdbname, NO_CREATE, 0600, DB_HASH, 0);
  4006     /* if create fails then we lose */
  4007     if ( handle->permCertDB == 0 ) {
  4008 	return status == RDB_RETRY ? SECWouldBlock : SECFailure;
  4011     /* Verify version number; */
  4012     versionEntry = NewDBVersionEntry(0);
  4013     if ( versionEntry == NULL ) {
  4014 	rv = SECFailure;
  4015 	goto loser;
  4018     rv = WriteDBVersionEntry(handle, versionEntry);
  4020     DestroyDBEntry((certDBEntry *)versionEntry);
  4022     if ( rv != SECSuccess ) {
  4023 	goto loser;
  4026     /* rv must already be Success here because of previous if statement */
  4027     /* try to upgrade old db here */
  4028     if (appName &&
  4029        (updatedb = dbsopen(certdbname, NO_RDONLY, 0600, DB_HASH, 0)) != NULL) {
  4030 	rv = UpdateV8DB(handle, updatedb);
  4031     } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,7)) != NULL) {
  4032 	rv = UpdateV7DB(handle, updatedb);
  4033     } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,6)) != NULL) {
  4034 	rv = UpdateV6DB(handle, updatedb);
  4035     } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,5)) != NULL) {
  4036 	rv = UpdateV5DB(handle, updatedb);
  4037     } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,4)) != NULL) {
  4038 	/* NES has v5 format db's with v4 db names! */
  4039 	if (isV4DB(updatedb)) {
  4040 	    rv = UpdateV4DB(handle,updatedb);
  4041 	} else {
  4042 	    rv = UpdateV5DB(handle,updatedb);
  4047 loser:
  4048     db_InitComplete(handle->permCertDB);
  4049     return rv;
  4052 static int
  4053 nsslowcert_GetVersionNumber( NSSLOWCERTCertDBHandle *handle)
  4055     certDBEntryVersion *versionEntry = NULL;
  4056     int version = 0;
  4058     versionEntry = ReadDBVersionEntry(handle); 
  4059     if ( versionEntry == NULL ) {
  4060 	return 0;
  4062     version = versionEntry->common.version;
  4063     DestroyDBEntry((certDBEntry *)versionEntry);
  4064     return version;
  4067 /*
  4068  * Open the certificate database and index databases.  Create them if
  4069  * they are not there or bad.
  4070  */
  4071 static SECStatus
  4072 nsslowcert_OpenPermCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
  4073 		   		const char *appName, const char *prefix,
  4074 				NSSLOWCERTDBNameFunc namecb, void *cbarg)
  4076     SECStatus rv;
  4077     int openflags;
  4078     char *certdbname;
  4079     int version = 0;
  4081     certdbname = (* namecb)(cbarg, CERT_DB_FILE_VERSION);
  4082     if ( certdbname == NULL ) {
  4083 	return(SECFailure);
  4086     openflags = readOnly ? NO_RDONLY : NO_RDWR;
  4088     /*
  4089      * first open the permanent file based database.
  4090      */
  4091     if (appName) {
  4092 	handle->permCertDB = rdbopen( appName, prefix, "cert", openflags, NULL);
  4093     } else {
  4094 	handle->permCertDB = dbsopen( certdbname, openflags, 0600, DB_HASH, 0 );
  4097     /* check for correct version number */
  4098     if ( handle->permCertDB ) {
  4099 	version = nsslowcert_GetVersionNumber(handle);
  4100 	if ((version != CERT_DB_FILE_VERSION) &&
  4101 		!(appName && version == CERT_DB_V7_FILE_VERSION)) {
  4102 	    goto loser;
  4104     } else if ( readOnly ) {
  4105 	/* don't create if readonly */
  4106 	    /* Try openning a version 7 database */
  4107 	    handle->permCertDB = nsslowcert_openolddb(namecb,cbarg, 7);
  4108 	    if (!handle->permCertDB) {
  4109 		goto loser;
  4111 	    if (nsslowcert_GetVersionNumber(handle) != 7) {
  4112 		goto loser;
  4114     } else {
  4115         /* if first open fails, try to create a new DB */
  4116 	rv = openNewCertDB(appName,prefix,certdbname,handle,namecb,cbarg);
  4117 	if (rv == SECWouldBlock) {
  4118 	    /* only the rdb version can fail with wouldblock */
  4119 	    handle->permCertDB = 
  4120 			rdbopen( appName, prefix, "cert", openflags, NULL);
  4122 	    /* check for correct version number */
  4123 	    if ( !handle->permCertDB ) {
  4124 		goto loser;
  4126 	    version = nsslowcert_GetVersionNumber(handle);
  4127 	    if ((version != CERT_DB_FILE_VERSION) &&
  4128 		!(appName && version == CERT_DB_V7_FILE_VERSION)) {
  4129 		goto loser;
  4131 	} else if (rv != SECSuccess) {
  4132 	    goto loser;
  4136     PORT_Free(certdbname);
  4138     return (SECSuccess);
  4140 loser:
  4142     PORT_SetError(SEC_ERROR_BAD_DATABASE);
  4144     if ( handle->permCertDB ) {
  4145 	certdb_Close(handle->permCertDB);
  4146 	handle->permCertDB = 0;
  4149     PORT_Free(certdbname);
  4151     return(SECFailure);
  4154 /*
  4155  * delete all DB records associated with a particular certificate
  4156  */
  4157 static SECStatus
  4158 DeletePermCert(NSSLOWCERTCertificate *cert)
  4160     SECStatus rv;
  4161     SECStatus ret;
  4163     ret = SECSuccess;
  4165     rv = DeleteDBCertEntry(cert->dbhandle, &cert->certKey);
  4166     if ( rv != SECSuccess ) {
  4167 	ret = SECFailure;
  4170     rv = RemovePermSubjectNode(cert);
  4173     return(ret);
  4176 /*
  4177  * Delete a certificate from the permanent database.
  4178  */
  4179 SECStatus
  4180 nsslowcert_DeletePermCertificate(NSSLOWCERTCertificate *cert)
  4182     SECStatus rv;
  4184     nsslowcert_LockDB(cert->dbhandle);
  4186     /* delete the records from the permanent database */
  4187     rv = DeletePermCert(cert);
  4189     /* get rid of dbcert and stuff pointing to it */
  4190     DestroyDBEntry((certDBEntry *)cert->dbEntry);
  4191     cert->dbEntry = NULL;
  4192     cert->trust = NULL;
  4194     nsslowcert_UnlockDB(cert->dbhandle);
  4195     return(rv);
  4198 /*
  4199  * Traverse all of the entries in the database of a particular type
  4200  * call the given function for each one.
  4201  */
  4202 SECStatus
  4203 nsslowcert_TraverseDBEntries(NSSLOWCERTCertDBHandle *handle,
  4204 		      certDBEntryType type,
  4205 		      SECStatus (* callback)(SECItem *data, SECItem *key,
  4206 					    certDBEntryType type, void *pdata),
  4207 		      void *udata )
  4209     DBT data;
  4210     DBT key;
  4211     SECStatus rv = SECSuccess;
  4212     int ret;
  4213     SECItem dataitem;
  4214     SECItem keyitem;
  4215     unsigned char *buf;
  4216     unsigned char *keybuf;
  4218     ret = certdb_Seq(handle->permCertDB, &key, &data, R_FIRST);
  4219     if ( ret ) {
  4220 	return(SECFailure);
  4222     /* here, ret is zero and rv is SECSuccess.  
  4223      * Below here, ret is a count of successful calls to the callback function.
  4224      */
  4225     do {
  4226 	buf = (unsigned char *)data.data;
  4228 	if ( buf[1] == (unsigned char)type ) {
  4229 	    dataitem.len = data.size;
  4230 	    dataitem.data = buf;
  4231             dataitem.type = siBuffer;
  4232 	    keyitem.len = key.size - SEC_DB_KEY_HEADER_LEN;
  4233 	    keybuf = (unsigned char *)key.data;
  4234 	    keyitem.data = &keybuf[SEC_DB_KEY_HEADER_LEN];
  4235             keyitem.type = siBuffer;
  4236 	    /* type should equal keybuf[0].  */
  4238 	    rv = (* callback)(&dataitem, &keyitem, type, udata);
  4239 	    if ( rv == SECSuccess ) {
  4240 		++ret;
  4243     } while ( certdb_Seq(handle->permCertDB, &key, &data, R_NEXT) == 0 );
  4244     /* If any callbacks succeeded, or no calls to callbacks were made, 
  4245      * then report success.  Otherwise, report failure.
  4246      */
  4247     return (ret ? SECSuccess : rv);
  4249 /*
  4250  * Decode a certificate and enter it into the temporary certificate database.
  4251  * Deal with nicknames correctly
  4253  * This is the private entry point.
  4254  */
  4255 static NSSLOWCERTCertificate *
  4256 DecodeACert(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
  4258     NSSLOWCERTCertificate *cert = NULL;
  4260     cert = nsslowcert_DecodeDERCertificate(&entry->derCert, entry->nickname );
  4262     if ( cert == NULL ) {
  4263 	goto loser;
  4266     cert->dbhandle = handle;
  4267     cert->dbEntry = entry;
  4268     cert->trust = &entry->trust;
  4270     return(cert);
  4272 loser:
  4273     return(0);
  4276 static NSSLOWCERTTrust *
  4277 CreateTrust(void)
  4279     NSSLOWCERTTrust *trust = NULL;
  4281     nsslowcert_LockFreeList();
  4282     trust = trustListHead;
  4283     if (trust) {
  4284 	trustListCount--;
  4285 	trustListHead = trust->next;
  4287     PORT_Assert(trustListCount >= 0);
  4288     nsslowcert_UnlockFreeList();
  4289     if (trust) {
  4290 	return trust;
  4293     return PORT_ZNew(NSSLOWCERTTrust);
  4296 static void
  4297 DestroyTrustFreeList(void)
  4299     NSSLOWCERTTrust *trust;
  4301     nsslowcert_LockFreeList();
  4302     while (NULL != (trust = trustListHead)) {
  4303 	trustListCount--;
  4304 	trustListHead = trust->next;
  4305 	PORT_Free(trust);
  4307     PORT_Assert(!trustListCount);
  4308     trustListCount = 0;
  4309     nsslowcert_UnlockFreeList();
  4312 static NSSLOWCERTTrust * 
  4313 DecodeTrustEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry, 
  4314                  const SECItem *dbKey)
  4316     NSSLOWCERTTrust *trust = CreateTrust();
  4317     if (trust == NULL) {
  4318 	return trust;
  4320     trust->dbhandle = handle;
  4321     trust->dbEntry = entry;
  4322     trust->dbKey.data = pkcs11_copyStaticData(dbKey->data,dbKey->len,
  4323 				trust->dbKeySpace, sizeof(trust->dbKeySpace));
  4324     if (!trust->dbKey.data) {
  4325 	PORT_Free(trust);
  4326 	return NULL;
  4328     trust->dbKey.len = dbKey->len;
  4330     trust->trust = &entry->trust;
  4331     trust->derCert = &entry->derCert;
  4333     return(trust);
  4336 typedef struct {
  4337     PermCertCallback certfunc;
  4338     NSSLOWCERTCertDBHandle *handle;
  4339     void *data;
  4340 } PermCertCallbackState;
  4342 /*
  4343  * traversal callback to decode certs and call callers callback
  4344  */
  4345 static SECStatus
  4346 certcallback(SECItem *dbdata, SECItem *dbkey, certDBEntryType type, void *data)
  4348     PermCertCallbackState *mystate;
  4349     SECStatus rv;
  4350     certDBEntryCert *entry;
  4351     SECItem entryitem;
  4352     NSSLOWCERTCertificate *cert;
  4353     PLArenaPool *arena = NULL;
  4355     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  4356     if ( arena == NULL ) {
  4357 	goto loser;
  4360     entry = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
  4361     mystate = (PermCertCallbackState *)data;
  4362     entry->common.version = (unsigned int)dbdata->data[0];
  4363     entry->common.type = (certDBEntryType)dbdata->data[1];
  4364     entry->common.flags = (unsigned int)dbdata->data[2];
  4365     entry->common.arena = arena;
  4367     entryitem.len = dbdata->len - SEC_DB_ENTRY_HEADER_LEN;
  4368     entryitem.data = &dbdata->data[SEC_DB_ENTRY_HEADER_LEN];
  4370     rv = DecodeDBCertEntry(entry, &entryitem);
  4371     if (rv != SECSuccess ) {
  4372 	goto loser;
  4374     entry->derCert.type = siBuffer;
  4376     /* note: Entry is 'inheritted'.  */
  4377     cert = DecodeACert(mystate->handle, entry);
  4379     rv = (* mystate->certfunc)(cert, dbkey, mystate->data);
  4381     /* arena stored in entry destroyed by nsslowcert_DestroyCertificate */
  4382     nsslowcert_DestroyCertificateNoLocking(cert);
  4384     return(rv);
  4386 loser:
  4387     if ( arena ) {
  4388 	PORT_FreeArena(arena, PR_FALSE);
  4390     return(SECFailure);
  4393 /*
  4394  * Traverse all of the certificates in the permanent database and
  4395  * call the given function for each one; expect the caller to have lock.
  4396  */
  4397 static SECStatus
  4398 TraversePermCertsNoLocking(NSSLOWCERTCertDBHandle *handle,
  4399 			   SECStatus (* certfunc)(NSSLOWCERTCertificate *cert,
  4400 						  SECItem *k,
  4401 						  void *pdata),
  4402 			   void *udata )
  4404     SECStatus rv;
  4405     PermCertCallbackState mystate;
  4407     mystate.certfunc = certfunc;
  4408     mystate.handle = handle;
  4409     mystate.data = udata;
  4410     rv = nsslowcert_TraverseDBEntries(handle, certDBEntryTypeCert, certcallback,
  4411 			       (void *)&mystate);
  4413     return(rv);
  4416 /*
  4417  * Traverse all of the certificates in the permanent database and
  4418  * call the given function for each one.
  4419  */
  4420 SECStatus
  4421 nsslowcert_TraversePermCerts(NSSLOWCERTCertDBHandle *handle,
  4422 		      SECStatus (* certfunc)(NSSLOWCERTCertificate *cert, SECItem *k,
  4423 					    void *pdata),
  4424 		      void *udata )
  4426     SECStatus rv;
  4428     nsslowcert_LockDB(handle);
  4429     rv = TraversePermCertsNoLocking(handle, certfunc, udata);
  4430     nsslowcert_UnlockDB(handle);
  4432     return(rv);
  4437 /*
  4438  * Close the database
  4439  */
  4440 void
  4441 nsslowcert_ClosePermCertDB(NSSLOWCERTCertDBHandle *handle)
  4443     if ( handle ) {
  4444 	if ( handle->permCertDB ) {
  4445 	    certdb_Close( handle->permCertDB );
  4446 	    handle->permCertDB = NULL;
  4448 	if (handle->dbMon) {
  4449     	    PZ_DestroyMonitor(handle->dbMon);
  4450 	    handle->dbMon = NULL;
  4452 	PORT_Free(handle);
  4454     return;
  4457 /*
  4458  * Get the trust attributes from a certificate
  4459  */
  4460 SECStatus
  4461 nsslowcert_GetCertTrust(NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
  4463     SECStatus rv;
  4465     nsslowcert_LockCertTrust(cert);
  4467     if ( cert->trust == NULL ) {
  4468 	rv = SECFailure;
  4469     } else {
  4470 	*trust = *cert->trust;
  4471 	rv = SECSuccess;
  4474     nsslowcert_UnlockCertTrust(cert);
  4475     return(rv);
  4478 /*
  4479  * Change the trust attributes of a certificate and make them permanent
  4480  * in the database.
  4481  */
  4482 SECStatus
  4483 nsslowcert_ChangeCertTrust(NSSLOWCERTCertDBHandle *handle, 
  4484 	 	 	NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
  4486     certDBEntryCert *entry;
  4487     int rv;
  4488     SECStatus ret;
  4490     nsslowcert_LockDB(handle);
  4491     nsslowcert_LockCertTrust(cert);
  4492     /* only set the trust on permanent certs */
  4493     if ( cert->trust == NULL ) {
  4494 	ret = SECFailure;
  4495 	goto done;
  4498     *cert->trust = *trust;
  4499     if ( cert->dbEntry == NULL ) {
  4500 	ret = SECSuccess; /* not in permanent database */
  4501 	goto done;
  4504     entry = cert->dbEntry;
  4505     entry->trust = *trust;
  4507     rv = WriteDBCertEntry(handle, entry);
  4508     if ( rv ) {
  4509 	ret = SECFailure;
  4510 	goto done;
  4513     ret = SECSuccess;
  4515 done:
  4516     nsslowcert_UnlockCertTrust(cert);
  4517     nsslowcert_UnlockDB(handle);
  4518     return(ret);
  4522 static SECStatus
  4523 nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
  4524     NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
  4526     char *oldnn;
  4527     certDBEntryCert *entry;
  4528     PRBool conflict;
  4529     SECStatus ret;
  4531     PORT_Assert(!cert->dbEntry);
  4533     /* don't add a conflicting nickname */
  4534     conflict = nsslowcert_CertNicknameConflict(nickname, &cert->derSubject,
  4535 					dbhandle);
  4536     if ( conflict ) {
  4537 	ret = SECFailure;
  4538 	goto done;
  4541     /* save old nickname so that we can delete it */
  4542     oldnn = cert->nickname;
  4544     entry = AddCertToPermDB(dbhandle, cert, nickname, trust);
  4546     if ( entry == NULL ) {
  4547 	ret = SECFailure;
  4548 	goto done;
  4551     pkcs11_freeNickname(oldnn,cert->nicknameSpace);
  4553     cert->nickname = (entry->nickname) ? pkcs11_copyNickname(entry->nickname,
  4554 		cert->nicknameSpace, sizeof(cert->nicknameSpace)) : NULL;
  4555     cert->trust = &entry->trust;
  4556     cert->dbEntry = entry;
  4558     ret = SECSuccess;
  4559 done:
  4560     return(ret);
  4563 SECStatus
  4564 nsslowcert_AddPermCert(NSSLOWCERTCertDBHandle *dbhandle,
  4565     NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
  4567     SECStatus ret;
  4569     nsslowcert_LockDB(dbhandle);
  4571     ret = nsslowcert_UpdatePermCert(dbhandle, cert, nickname, trust);
  4573     nsslowcert_UnlockDB(dbhandle);
  4574     return(ret);
  4577 /*
  4578  * Open the certificate database and index databases.  Create them if
  4579  * they are not there or bad.
  4580  */
  4581 SECStatus
  4582 nsslowcert_OpenCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
  4583 	        const char *appName, const char *prefix,
  4584 		NSSLOWCERTDBNameFunc namecb, void *cbarg, PRBool openVolatile)
  4586     int rv;
  4588     certdb_InitDBLock(handle);
  4590     handle->dbMon = PZ_NewMonitor(nssILockCertDB);
  4591     PORT_Assert(handle->dbMon != NULL);
  4592     handle->dbVerify = PR_FALSE;
  4594     rv = nsslowcert_OpenPermCertDB(handle, readOnly, appName, prefix, 
  4595 							namecb, cbarg);
  4596     if ( rv ) {
  4597 	goto loser;
  4600     return (SECSuccess);
  4602 loser:
  4603     if (handle->dbMon) {
  4604         PZ_DestroyMonitor(handle->dbMon);
  4605         handle->dbMon = NULL;
  4607     PORT_SetError(SEC_ERROR_BAD_DATABASE);
  4608     return(SECFailure);
  4611 PRBool
  4612 nsslowcert_needDBVerify(NSSLOWCERTCertDBHandle *handle)
  4614     if (!handle) return PR_FALSE;
  4615     return handle->dbVerify;
  4618 void
  4619 nsslowcert_setDBVerify(NSSLOWCERTCertDBHandle *handle, PRBool value)
  4621     handle->dbVerify = value;
  4625 /*
  4626  * Lookup a certificate in the databases.
  4627  */
  4628 static NSSLOWCERTCertificate *
  4629 FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
  4631     NSSLOWCERTCertificate *cert = NULL;
  4632     certDBEntryCert *entry;
  4633     PRBool locked = PR_FALSE;
  4635     if ( lockdb ) {
  4636 	locked = PR_TRUE;
  4637 	nsslowcert_LockDB(handle);
  4640     /* find in perm database */
  4641     entry = ReadDBCertEntry(handle, certKey);
  4643     if ( entry == NULL ) {
  4644  	goto loser;
  4647     /* inherit entry */  
  4648     cert = DecodeACert(handle, entry);
  4650 loser:
  4651     if (cert == NULL) {
  4652 	if (entry) {
  4653 	    DestroyDBEntry((certDBEntry *)entry);
  4657     if ( locked ) {
  4658 	nsslowcert_UnlockDB(handle);
  4661     return(cert);
  4664 /*
  4665  * Lookup a certificate in the databases.
  4666  */
  4667 static NSSLOWCERTTrust *
  4668 FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
  4670     NSSLOWCERTTrust *trust = NULL;
  4671     certDBEntryCert *entry;
  4672     PRBool locked = PR_FALSE;
  4674     if ( lockdb ) {
  4675 	locked = PR_TRUE;
  4676 	nsslowcert_LockDB(handle);
  4679     /* find in perm database */
  4680     entry = ReadDBCertEntry(handle, certKey);
  4682     if ( entry == NULL ) {
  4683  	goto loser;
  4686     if (!nsslowcert_hasTrust(&entry->trust)) {
  4687 	goto loser;
  4690     /* inherit entry */  
  4691     trust = DecodeTrustEntry(handle, entry, certKey);
  4693 loser:
  4694     if (trust == NULL) {
  4695 	if (entry) {
  4696 	    DestroyDBEntry((certDBEntry *)entry);
  4700     if ( locked ) {
  4701 	nsslowcert_UnlockDB(handle);
  4704     return(trust);
  4707 /*
  4708  * Lookup a certificate in the databases without locking
  4709  */
  4710 NSSLOWCERTCertificate *
  4711 nsslowcert_FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
  4713     return(FindCertByKey(handle, certKey, PR_FALSE));
  4716 /*
  4717  * Lookup a trust object in the databases without locking
  4718  */
  4719 NSSLOWCERTTrust *
  4720 nsslowcert_FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
  4722     return(FindTrustByKey(handle, certKey, PR_FALSE));
  4725 /*
  4726  * Generate a key from an issuerAndSerialNumber, and find the
  4727  * associated cert in the database.
  4728  */
  4729 NSSLOWCERTCertificate *
  4730 nsslowcert_FindCertByIssuerAndSN(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTIssuerAndSN *issuerAndSN)
  4732     SECItem certKey;
  4733     SECItem *sn = &issuerAndSN->serialNumber;
  4734     SECItem *issuer = &issuerAndSN->derIssuer;
  4735     NSSLOWCERTCertificate *cert;
  4736     int data_left = sn->len-1;
  4737     int data_len = sn->len;
  4738     int index = 0;
  4740     /* automatically detect DER encoded serial numbers and remove the der
  4741      * encoding since the database expects unencoded data. 
  4742      * if it's DER encoded, there must be at least 3 bytes, tag, len, data */
  4743     if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
  4744 	/* remove the der encoding of the serial number before generating the
  4745 	 * key.. */
  4746 	data_left = sn->len-2;
  4747 	data_len = sn->data[1];
  4748 	index = 2;
  4750 	/* extended length ? (not very likely for a serial number) */
  4751 	if (data_len & 0x80) {
  4752 	    int len_count = data_len & 0x7f;
  4754 	    data_len = 0;
  4755 	    data_left -= len_count;
  4756 	    if (data_left > 0) {
  4757 		while (len_count --) {
  4758 		    data_len = (data_len << 8) | sn->data[index++];
  4762 	/* XXX leaving any leading zeros on the serial number for backwards
  4763 	 * compatibility
  4764 	 */
  4765 	/* not a valid der, must be just an unlucky serial number value */
  4766 	if (data_len != data_left) {
  4767 	    data_len = sn->len;
  4768 	    index = 0;
  4772     certKey.type = 0;
  4773     certKey.data = (unsigned char*)PORT_Alloc(sn->len + issuer->len);
  4774     certKey.len = data_len + issuer->len;
  4776     if ( certKey.data == NULL ) {
  4777 	return(0);
  4780     /* first try the serial number as hand-decoded above*/
  4781     /* copy the serialNumber */
  4782     PORT_Memcpy(certKey.data, &sn->data[index], data_len);
  4784     /* copy the issuer */
  4785     PORT_Memcpy( &certKey.data[data_len],issuer->data,issuer->len);
  4787     cert = nsslowcert_FindCertByKey(handle, &certKey);
  4788     if (cert) {
  4789 	PORT_Free(certKey.data);
  4790 	return (cert);
  4793     /* didn't find it, try by der encoded serial number */
  4794     /* copy the serialNumber */
  4795     PORT_Memcpy(certKey.data, sn->data, sn->len);
  4797     /* copy the issuer */
  4798     PORT_Memcpy( &certKey.data[sn->len], issuer->data, issuer->len);
  4799     certKey.len = sn->len + issuer->len;
  4801     cert = nsslowcert_FindCertByKey(handle, &certKey);
  4803     PORT_Free(certKey.data);
  4805     return(cert);
  4808 /*
  4809  * Generate a key from an issuerAndSerialNumber, and find the
  4810  * associated cert in the database.
  4811  */
  4812 NSSLOWCERTTrust *
  4813 nsslowcert_FindTrustByIssuerAndSN(NSSLOWCERTCertDBHandle *handle, 
  4814 					NSSLOWCERTIssuerAndSN *issuerAndSN)
  4816     SECItem certKey;
  4817     SECItem *sn = &issuerAndSN->serialNumber;
  4818     SECItem *issuer = &issuerAndSN->derIssuer;
  4819     NSSLOWCERTTrust *trust;
  4820     unsigned char keyBuf[512];
  4821     int data_left = sn->len-1;
  4822     int data_len = sn->len;
  4823     int index = 0;
  4824     int len;
  4826     /* automatically detect DER encoded serial numbers and remove the der
  4827      * encoding since the database expects unencoded data. 
  4828      * if it's DER encoded, there must be at least 3 bytes, tag, len, data */
  4829     if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
  4830 	/* remove the der encoding of the serial number before generating the
  4831 	 * key.. */
  4832 	data_left = sn->len-2;
  4833 	data_len = sn->data[1];
  4834 	index = 2;
  4836 	/* extended length ? (not very likely for a serial number) */
  4837 	if (data_len & 0x80) {
  4838 	    int len_count = data_len & 0x7f;
  4840 	    data_len = 0;
  4841 	    data_left -= len_count;
  4842 	    if (data_left > 0) {
  4843 		while (len_count --) {
  4844 		    data_len = (data_len << 8) | sn->data[index++];
  4848 	/* XXX leaving any leading zeros on the serial number for backwards
  4849 	 * compatibility
  4850 	 */
  4851 	/* not a valid der, must be just an unlucky serial number value */
  4852 	if (data_len != data_left) {
  4853 	    data_len = sn->len;
  4854 	    index = 0;
  4858     certKey.type = 0;
  4859     certKey.len = data_len + issuer->len;
  4860     len = sn->len + issuer->len;
  4861     if (len > sizeof (keyBuf)) {
  4862 	certKey.data = (unsigned char*)PORT_Alloc(len);
  4863     } else {
  4864 	certKey.data = keyBuf;
  4867     if ( certKey.data == NULL ) {
  4868 	return(0);
  4871     /* first try the serial number as hand-decoded above*/
  4872     /* copy the serialNumber */
  4873     PORT_Memcpy(certKey.data, &sn->data[index], data_len);
  4875     /* copy the issuer */
  4876     PORT_Memcpy( &certKey.data[data_len],issuer->data,issuer->len);
  4878     trust = nsslowcert_FindTrustByKey(handle, &certKey);
  4879     if (trust) {
  4880 	pkcs11_freeStaticData(certKey.data, keyBuf);
  4881 	return (trust);
  4884     if (index == 0) {
  4885 	pkcs11_freeStaticData(certKey.data, keyBuf);
  4886 	return NULL;
  4889     /* didn't find it, try by der encoded serial number */
  4890     /* copy the serialNumber */
  4891     PORT_Memcpy(certKey.data, sn->data, sn->len);
  4893     /* copy the issuer */
  4894     PORT_Memcpy( &certKey.data[sn->len], issuer->data, issuer->len);
  4895     certKey.len = sn->len + issuer->len;
  4897     trust = nsslowcert_FindTrustByKey(handle, &certKey);
  4899     pkcs11_freeStaticData(certKey.data, keyBuf);
  4901     return(trust);
  4904 /*
  4905  * look for the given DER certificate in the database
  4906  */
  4907 NSSLOWCERTCertificate *
  4908 nsslowcert_FindCertByDERCert(NSSLOWCERTCertDBHandle *handle, SECItem *derCert)
  4910     PLArenaPool *arena;
  4911     SECItem certKey;
  4912     SECStatus rv;
  4913     NSSLOWCERTCertificate *cert = NULL;
  4915     /* create a scratch arena */
  4916     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  4917     if ( arena == NULL ) {
  4918 	return(NULL);
  4921     /* extract the database key from the cert */
  4922     rv = nsslowcert_KeyFromDERCert(arena, derCert, &certKey);
  4923     if ( rv != SECSuccess ) {
  4924 	goto loser;
  4927     /* find the certificate */
  4928     cert = nsslowcert_FindCertByKey(handle, &certKey);
  4930 loser:
  4931     PORT_FreeArena(arena, PR_FALSE);
  4932     return(cert);
  4935 static void
  4936 DestroyCertificate(NSSLOWCERTCertificate *cert, PRBool lockdb)
  4938     int refCount;
  4939     NSSLOWCERTCertDBHandle *handle;
  4941     if ( cert ) {
  4943 	handle = cert->dbhandle;
  4945 	/*
  4946 	 * handle may be NULL, for example if the cert was created with
  4947 	 * nsslowcert_DecodeDERCertificate.
  4948 	 */
  4949 	if ( lockdb && handle ) {
  4950 	    nsslowcert_LockDB(handle);
  4953         nsslowcert_LockCertRefCount(cert);
  4954 	PORT_Assert(cert->referenceCount > 0);
  4955 	refCount = --cert->referenceCount;
  4956         nsslowcert_UnlockCertRefCount(cert);
  4958 	if ( refCount == 0 ) {
  4959 	    certDBEntryCert *entry  = cert->dbEntry;
  4961 	    if ( entry ) {
  4962 		DestroyDBEntry((certDBEntry *)entry);
  4965 	    pkcs11_freeNickname(cert->nickname,cert->nicknameSpace);
  4966 	    pkcs11_freeNickname(cert->emailAddr,cert->emailAddrSpace);
  4967 	    pkcs11_freeStaticData(cert->certKey.data,cert->certKeySpace);
  4968 	    cert->certKey.data = NULL;
  4969 	    cert->nickname = NULL;
  4971 	    /* zero cert before freeing. Any stale references to this cert
  4972 	     * after this point will probably cause an exception.  */
  4973 	    PORT_Memset(cert, 0, sizeof *cert);
  4975 	    /* use reflock to protect the free list */
  4976 	    nsslowcert_LockFreeList();
  4977 	    if (certListCount > MAX_CERT_LIST_COUNT) {
  4978 		PORT_Free(cert);
  4979 	    } else {
  4980 		certListCount++;
  4981 		cert->next = certListHead;
  4982 		certListHead = cert;
  4984 	    nsslowcert_UnlockFreeList();
  4985 	    cert = NULL;
  4987 	if ( lockdb && handle ) {
  4988 	    nsslowcert_UnlockDB(handle);
  4992     return;
  4995 NSSLOWCERTCertificate *
  4996 nsslowcert_CreateCert(void)
  4998     NSSLOWCERTCertificate *cert;
  4999     nsslowcert_LockFreeList();
  5000     cert = certListHead;
  5001     if (cert) {
  5002 	certListHead = cert->next;
  5003 	certListCount--;
  5005     PORT_Assert(certListCount >= 0);
  5006     nsslowcert_UnlockFreeList();
  5007     if (cert) {
  5008 	return cert;
  5010     return PORT_ZNew(NSSLOWCERTCertificate);
  5013 static void
  5014 DestroyCertFreeList(void)
  5016     NSSLOWCERTCertificate *cert;
  5018     nsslowcert_LockFreeList();
  5019     while (NULL != (cert = certListHead)) {
  5020 	certListCount--;
  5021 	certListHead = cert->next;
  5022 	PORT_Free(cert);
  5024     PORT_Assert(!certListCount);
  5025     certListCount = 0;
  5026     nsslowcert_UnlockFreeList();
  5029 void
  5030 nsslowcert_DestroyTrust(NSSLOWCERTTrust *trust)
  5032     certDBEntryCert *entry  = trust->dbEntry;
  5034     if ( entry ) {
  5035 	DestroyDBEntry((certDBEntry *)entry);
  5037     pkcs11_freeStaticData(trust->dbKey.data,trust->dbKeySpace);
  5038     PORT_Memset(trust, 0, sizeof(*trust));
  5040     nsslowcert_LockFreeList();
  5041     if (trustListCount > MAX_TRUST_LIST_COUNT) {
  5042 	PORT_Free(trust);
  5043     } else {
  5044 	trustListCount++;
  5045 	trust->next = trustListHead;
  5046 	trustListHead = trust;
  5048     nsslowcert_UnlockFreeList();
  5050     return;
  5053 void
  5054 nsslowcert_DestroyCertificate(NSSLOWCERTCertificate *cert)
  5056     DestroyCertificate(cert, PR_TRUE);
  5057     return;
  5060 static void
  5061 nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert)
  5063     DestroyCertificate(cert, PR_FALSE);
  5064     return;
  5067 /*
  5068  * Lookup a CRL in the databases. We mirror the same fast caching data base
  5069  *  caching stuff used by certificates....?
  5070  */
  5071 certDBEntryRevocation *
  5072 nsslowcert_FindCrlByKey(NSSLOWCERTCertDBHandle *handle, 
  5073 						SECItem *crlKey, PRBool isKRL)
  5075     SECItem keyitem;
  5076     DBT key;
  5077     SECStatus rv;
  5078     PLArenaPool *arena = NULL;
  5079     certDBEntryRevocation *entry = NULL;
  5080     certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation  
  5081 					: certDBEntryTypeRevocation;
  5083     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  5084     if ( arena == NULL ) {
  5085 	goto loser;
  5088     rv = EncodeDBGenericKey(crlKey, arena, &keyitem, crlType);
  5089     if ( rv != SECSuccess ) {
  5090 	goto loser;
  5093     key.data = keyitem.data;
  5094     key.size = keyitem.len;
  5096     /* find in perm database */
  5097     entry = ReadDBCrlEntry(handle, crlKey, crlType);
  5099     if ( entry == NULL ) {
  5100 	goto loser;
  5103 loser:
  5104     if ( arena ) {
  5105 	PORT_FreeArena(arena, PR_FALSE);
  5108     return entry;
  5111 /*
  5112  * replace the existing URL in the data base with a new one
  5113  */
  5114 static SECStatus
  5115 nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl, 
  5116 			SECItem *crlKey, char *url, PRBool isKRL)
  5118     SECStatus rv = SECFailure;
  5119     certDBEntryRevocation *entry = NULL;
  5120     certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation  
  5121 					: certDBEntryTypeRevocation;
  5122     DeleteDBCrlEntry(handle, crlKey, crlType);
  5124     /* Write the new entry into the data base */
  5125     entry = NewDBCrlEntry(derCrl, url, crlType, 0);
  5126     if (entry == NULL) goto done;
  5128     rv = WriteDBCrlEntry(handle, entry, crlKey);
  5129     if (rv != SECSuccess) goto done;
  5131 done:
  5132     if (entry) {
  5133 	DestroyDBEntry((certDBEntry *)entry);
  5135     return rv;
  5138 SECStatus
  5139 nsslowcert_AddCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl, 
  5140 			SECItem *crlKey, char *url, PRBool isKRL)
  5142     SECStatus rv;
  5144     rv = nsslowcert_UpdateCrl(handle, derCrl, crlKey, url, isKRL);
  5146     return rv;
  5149 SECStatus
  5150 nsslowcert_DeletePermCRL(NSSLOWCERTCertDBHandle *handle, const SECItem *derName,
  5151 								 PRBool isKRL)
  5153     SECStatus rv;
  5154     certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation  
  5155 					: certDBEntryTypeRevocation;
  5157     rv = DeleteDBCrlEntry(handle, derName, crlType);
  5158     if (rv != SECSuccess) goto done;
  5160 done:
  5161     return rv;
  5165 PRBool
  5166 nsslowcert_hasTrust(NSSLOWCERTCertTrust *trust)
  5168     if (trust == NULL) {
  5169 	return PR_FALSE;
  5171     return !((trust->sslFlags & CERTDB_TRUSTED_UNKNOWN) && 
  5172 		(trust->emailFlags & CERTDB_TRUSTED_UNKNOWN) && 
  5173 			(trust->objectSigningFlags & CERTDB_TRUSTED_UNKNOWN));
  5176 /*
  5177  * This function has the logic that decides if another person's cert and
  5178  * email profile from an S/MIME message should be saved.  It can deal with
  5179  * the case when there is no profile.
  5180  */
  5181 static SECStatus
  5182 nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle, 
  5183 	char *emailAddr, SECItem *derSubject, SECItem *emailProfile, 
  5184 							SECItem *profileTime)
  5186     certDBEntrySMime *entry = NULL;
  5187     SECStatus rv = SECFailure;;
  5190     /* find our existing entry */
  5191     entry = nsslowcert_ReadDBSMimeEntry(dbhandle, emailAddr);
  5193     if ( entry ) {
  5194 	/* keep our old db entry consistant for old applications. */
  5195 	if (!SECITEM_ItemsAreEqual(derSubject, &entry->subjectName)) {
  5196 	    nsslowcert_UpdateSubjectEmailAddr(dbhandle, &entry->subjectName, 
  5197 				emailAddr, nsslowcert_remove);
  5199 	DestroyDBEntry((certDBEntry *)entry);
  5200 	entry = NULL;
  5203     /* now save the entry */
  5204     entry = NewDBSMimeEntry(emailAddr, derSubject, emailProfile,
  5205 				profileTime, 0);
  5206     if ( entry == NULL ) {
  5207 	rv = SECFailure;
  5208 	goto loser;
  5211     nsslowcert_LockDB(dbhandle);
  5213     rv = DeleteDBSMimeEntry(dbhandle, emailAddr);
  5214     /* if delete fails, try to write new entry anyway... */
  5216     /* link subject entry back here */
  5217     rv = nsslowcert_UpdateSubjectEmailAddr(dbhandle, derSubject, emailAddr,
  5218 					nsslowcert_add);
  5219     if ( rv != SECSuccess ) {
  5220 	    nsslowcert_UnlockDB(dbhandle);
  5221 	    goto loser;
  5224     rv = WriteDBSMimeEntry(dbhandle, entry);
  5225     if ( rv != SECSuccess ) {
  5226 	    nsslowcert_UnlockDB(dbhandle);
  5227 	    goto loser;
  5230     nsslowcert_UnlockDB(dbhandle);
  5232     rv = SECSuccess;
  5234 loser:
  5235     if ( entry ) {
  5236 	DestroyDBEntry((certDBEntry *)entry);
  5238     return(rv);
  5241 SECStatus
  5242 nsslowcert_SaveSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle, char *emailAddr, 
  5243 	SECItem *derSubject, SECItem *emailProfile, SECItem *profileTime)
  5245     SECStatus rv = SECFailure;;
  5248     rv = nsslowcert_UpdateSMimeProfile(dbhandle, emailAddr, 
  5249 	 derSubject, emailProfile, profileTime);
  5251     return(rv);
  5254 void
  5255 nsslowcert_DestroyFreeLists(void)
  5257     if (freeListLock == NULL) {
  5258 	return;
  5260     DestroyCertEntryFreeList();
  5261     DestroyTrustFreeList();
  5262     DestroyCertFreeList();
  5263     SKIP_AFTER_FORK(PZ_DestroyLock(freeListLock));
  5264     freeListLock = NULL;
  5267 void
  5268 nsslowcert_DestroyGlobalLocks(void)
  5270     if (dbLock) {
  5271 	SKIP_AFTER_FORK(PZ_DestroyLock(dbLock));
  5272 	dbLock = NULL;
  5274     if (certRefCountLock) {
  5275 	SKIP_AFTER_FORK(PZ_DestroyLock(certRefCountLock));
  5276 	certRefCountLock = NULL;
  5278     if (certTrustLock) {
  5279 	SKIP_AFTER_FORK(PZ_DestroyLock(certTrustLock));
  5280 	certTrustLock = NULL;
  5284 certDBEntry *
  5285 nsslowcert_DecodeAnyDBEntry(SECItem *dbData, const SECItem *dbKey, 
  5286                  certDBEntryType entryType, void *pdata)
  5288     PLArenaPool *arena = NULL;
  5289     certDBEntry *entry;
  5290     SECStatus rv;
  5291     SECItem dbEntry;
  5294     if ((dbData->len < SEC_DB_ENTRY_HEADER_LEN) || (dbKey->len == 0)) {
  5295 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
  5296 	goto loser;
  5298     dbEntry.data = &dbData->data[SEC_DB_ENTRY_HEADER_LEN];
  5299     dbEntry.len  = dbData->len - SEC_DB_ENTRY_HEADER_LEN;
  5301     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  5302     if (arena == NULL) {
  5303 	goto loser;
  5305     entry = PORT_ArenaZNew(arena, certDBEntry);
  5306     if (!entry)
  5307     	goto loser;
  5309     entry->common.version = (unsigned int)dbData->data[0];
  5310     entry->common.flags   = (unsigned int)dbData->data[2];
  5311     entry->common.type    = entryType;
  5312     entry->common.arena   = arena;
  5314     switch (entryType) {
  5315     case certDBEntryTypeContentVersion: /* This type appears to be unused */
  5316     case certDBEntryTypeVersion:        /* This type has only the common hdr */
  5317 	rv = SECSuccess;
  5318     	break;
  5320     case certDBEntryTypeSubject:
  5321 	rv = DecodeDBSubjectEntry(&entry->subject, &dbEntry, dbKey);
  5322     	break;
  5324     case certDBEntryTypeNickname:
  5325 	rv = DecodeDBNicknameEntry(&entry->nickname, &dbEntry,
  5326                                    (char *)dbKey->data);
  5327     	break;
  5329     /* smime profiles need entries created after the certs have
  5330      * been imported, loop over them in a second run */
  5331     case certDBEntryTypeSMimeProfile:
  5332 	rv = DecodeDBSMimeEntry(&entry->smime, &dbEntry, (char *)dbKey->data);
  5333 	break;
  5335     case certDBEntryTypeCert:
  5336 	rv = DecodeDBCertEntry(&entry->cert, &dbEntry);
  5337 	break;
  5339     case certDBEntryTypeKeyRevocation:
  5340     case certDBEntryTypeRevocation:
  5341 	rv = DecodeDBCrlEntry(&entry->revocation, &dbEntry);
  5342 	break;
  5344     default:
  5345 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
  5346 	rv = SECFailure;
  5349     if (rv == SECSuccess)
  5350 	return entry;
  5352 loser:
  5353     if (arena)
  5354 	PORT_FreeArena(arena, PR_FALSE);
  5355     return NULL;

mercurial