1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /*
6  * Permanent Certificate database handling code
7  */
8 #include "lowkeyti.h"
9 #include "pcert.h"
10 #include <db.h>
11 #include <fcntl.h>
12 #include "pcert.h"
13 #include "secitem.h"
14 #include "secder.h"
15 
16 #include "secerr.h"
17 #include "lgdb.h"
18 
19 /* forward declaration */
20 NSSLOWCERTCertificate *
21 nsslowcert_FindCertByDERCertNoLocking(NSSLOWCERTCertDBHandle *handle, SECItem *derCert);
22 static SECStatus
23 nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
24                               char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
25                               SECItem *profileTime);
26 static SECStatus
27 nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
28                           NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust);
29 static SECStatus
30 nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
31                      SECItem *crlKey, char *url, PRBool isKRL);
32 
33 static NSSLOWCERTCertificate *certListHead = NULL;
34 static NSSLOWCERTTrust *trustListHead = NULL;
35 static certDBEntryCert *entryListHead = NULL;
36 static int certListCount = 0;
37 static int trustListCount = 0;
38 static int entryListCount = 0;
39 #define MAX_CERT_LIST_COUNT 10
40 #define MAX_TRUST_LIST_COUNT 10
41 #define MAX_ENTRY_LIST_COUNT 10
42 
43 /*
44  * the following functions are wrappers for the db library that implement
45  * a global lock to make the database thread safe.
46  */
47 static PZLock *dbLock = NULL;
48 static PZLock *certRefCountLock = NULL;
49 static PZLock *certTrustLock = NULL;
50 static PZLock *freeListLock = NULL;
51 
52 void
certdb_InitDBLock(NSSLOWCERTCertDBHandle * handle)53 certdb_InitDBLock(NSSLOWCERTCertDBHandle *handle)
54 {
55     if (dbLock == NULL) {
56         dbLock = PZ_NewLock(nssILockCertDB);
57         PORT_Assert(dbLock != NULL);
58     }
59 }
60 
61 SECStatus
nsslowcert_InitLocks(void)62 nsslowcert_InitLocks(void)
63 {
64     if (freeListLock == NULL) {
65         freeListLock = PZ_NewLock(nssILockRefLock);
66         if (freeListLock == NULL) {
67             return SECFailure;
68         }
69     }
70     if (certRefCountLock == NULL) {
71         certRefCountLock = PZ_NewLock(nssILockRefLock);
72         if (certRefCountLock == NULL) {
73             return SECFailure;
74         }
75     }
76     if (certTrustLock == NULL) {
77         certTrustLock = PZ_NewLock(nssILockCertDB);
78         if (certTrustLock == NULL) {
79             return SECFailure;
80         }
81     }
82 
83     return SECSuccess;
84 }
85 
86 /*
87  * Acquire the global lock on the cert database.
88  * This lock is currently used for the following operations:
89  *  adding or deleting a cert to either the temp or perm databases
90  *  converting a temp to perm or perm to temp
91  *  changing (maybe just adding!?) the trust of a cert
92  *      chaning the DB status checking Configuration
93  */
94 static void
nsslowcert_LockDB(NSSLOWCERTCertDBHandle * handle)95 nsslowcert_LockDB(NSSLOWCERTCertDBHandle *handle)
96 {
97     PZ_EnterMonitor(handle->dbMon);
98     return;
99 }
100 
101 /*
102  * Free the global cert database lock.
103  */
104 static void
nsslowcert_UnlockDB(NSSLOWCERTCertDBHandle * handle)105 nsslowcert_UnlockDB(NSSLOWCERTCertDBHandle *handle)
106 {
107 #ifdef DEBUG
108     PRStatus prstat = PZ_ExitMonitor(handle->dbMon);
109     PORT_Assert(prstat == PR_SUCCESS);
110 #else
111     PZ_ExitMonitor(handle->dbMon);
112 #endif
113 }
114 
115 /*
116  * Acquire the cert reference count lock
117  * There is currently one global lock for all certs, but I'm putting a cert
118  * arg here so that it will be easy to make it per-cert in the future if
119  * that turns out to be necessary.
120  */
121 static void
nsslowcert_LockCertRefCount(NSSLOWCERTCertificate * cert)122 nsslowcert_LockCertRefCount(NSSLOWCERTCertificate *cert)
123 {
124     PORT_Assert(certRefCountLock != NULL);
125 
126     PZ_Lock(certRefCountLock);
127     return;
128 }
129 
130 /*
131  * Free the cert reference count lock
132  */
133 static void
nsslowcert_UnlockCertRefCount(NSSLOWCERTCertificate * cert)134 nsslowcert_UnlockCertRefCount(NSSLOWCERTCertificate *cert)
135 {
136     PORT_Assert(certRefCountLock != NULL);
137 
138 #ifdef DEBUG
139     {
140         PRStatus prstat = PZ_Unlock(certRefCountLock);
141         PORT_Assert(prstat == PR_SUCCESS);
142     }
143 #else
144     PZ_Unlock(certRefCountLock);
145 #endif
146 }
147 
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
nsslowcert_LockCertTrust(NSSLOWCERTCertificate * cert)155 nsslowcert_LockCertTrust(NSSLOWCERTCertificate *cert)
156 {
157     PORT_Assert(certTrustLock != NULL);
158 
159     PZ_Lock(certTrustLock);
160     return;
161 }
162 
163 /*
164  * Free the cert trust lock
165  */
166 static void
nsslowcert_UnlockCertTrust(NSSLOWCERTCertificate * cert)167 nsslowcert_UnlockCertTrust(NSSLOWCERTCertificate *cert)
168 {
169     PORT_Assert(certTrustLock != NULL);
170 
171 #ifdef DEBUG
172     {
173         PRStatus prstat = PZ_Unlock(certTrustLock);
174         PORT_Assert(prstat == PR_SUCCESS);
175     }
176 #else
177     PZ_Unlock(certTrustLock);
178 #endif
179 }
180 
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
nsslowcert_LockFreeList(void)188 nsslowcert_LockFreeList(void)
189 {
190     PORT_Assert(freeListLock != NULL);
191 
192     SKIP_AFTER_FORK(PZ_Lock(freeListLock));
193     return;
194 }
195 
196 /*
197  * Free the cert reference count lock
198  */
199 static void
nsslowcert_UnlockFreeList(void)200 nsslowcert_UnlockFreeList(void)
201 {
202     PORT_Assert(freeListLock != NULL);
203 
204 #ifdef DEBUG
205     {
206         PRStatus prstat = PR_SUCCESS;
207         SKIP_AFTER_FORK(prstat = PZ_Unlock(freeListLock));
208         PORT_Assert(prstat == PR_SUCCESS);
209     }
210 #else
211     SKIP_AFTER_FORK(PZ_Unlock(freeListLock));
212 #endif
213 }
214 
215 NSSLOWCERTCertificate *
nsslowcert_DupCertificate(NSSLOWCERTCertificate * c)216 nsslowcert_DupCertificate(NSSLOWCERTCertificate *c)
217 {
218     if (c) {
219         nsslowcert_LockCertRefCount(c);
220         ++c->referenceCount;
221         nsslowcert_UnlockCertRefCount(c);
222     }
223     return c;
224 }
225 
226 static int
certdb_Get(DB * db,DBT * key,DBT * data,unsigned int flags)227 certdb_Get(DB *db, DBT *key, DBT *data, unsigned int flags)
228 {
229     int ret;
230 
231     PORT_Assert(dbLock != NULL);
232     PZ_Lock(dbLock);
233 
234     ret = (*db->get)(db, key, data, flags);
235 
236     (void)PZ_Unlock(dbLock);
237 
238     return (ret);
239 }
240 
241 static int
certdb_Put(DB * db,DBT * key,DBT * data,unsigned int flags)242 certdb_Put(DB *db, DBT *key, DBT *data, unsigned int flags)
243 {
244     int ret = 0;
245 
246     PORT_Assert(dbLock != NULL);
247     PZ_Lock(dbLock);
248 
249     ret = (*db->put)(db, key, data, flags);
250 
251     (void)PZ_Unlock(dbLock);
252 
253     return (ret);
254 }
255 
256 static int
certdb_Sync(DB * db,unsigned int flags)257 certdb_Sync(DB *db, unsigned int flags)
258 {
259     int ret;
260 
261     PORT_Assert(dbLock != NULL);
262     PZ_Lock(dbLock);
263 
264     ret = (*db->sync)(db, flags);
265 
266     (void)PZ_Unlock(dbLock);
267 
268     return (ret);
269 }
270 
271 #define DB_NOT_FOUND -30991 /* from DBM 3.2 */
272 static int
certdb_Del(DB * db,DBT * key,unsigned int flags)273 certdb_Del(DB *db, DBT *key, unsigned int flags)
274 {
275     int ret;
276 
277     PORT_Assert(dbLock != NULL);
278     PZ_Lock(dbLock);
279 
280     ret = (*db->del)(db, key, flags);
281 
282     (void)PZ_Unlock(dbLock);
283 
284     /* don't fail if the record is already deleted */
285     if (ret == DB_NOT_FOUND) {
286         ret = 0;
287     }
288 
289     return (ret);
290 }
291 
292 static int
certdb_Seq(DB * db,DBT * key,DBT * data,unsigned int flags)293 certdb_Seq(DB *db, DBT *key, DBT *data, unsigned int flags)
294 {
295     int ret;
296 
297     PORT_Assert(dbLock != NULL);
298     PZ_Lock(dbLock);
299 
300     ret = (*db->seq)(db, key, data, flags);
301 
302     (void)PZ_Unlock(dbLock);
303 
304     return (ret);
305 }
306 
307 static void
certdb_Close(DB * db)308 certdb_Close(DB *db)
309 {
310     PORT_Assert(dbLock != NULL);
311     SKIP_AFTER_FORK(PZ_Lock(dbLock));
312 
313     (*db->close)(db);
314 
315     SKIP_AFTER_FORK(PZ_Unlock(dbLock));
316 
317     return;
318 }
319 
320 void
pkcs11_freeNickname(char * nickname,char * space)321 pkcs11_freeNickname(char *nickname, char *space)
322 {
323     if (nickname && nickname != space) {
324         PORT_Free(nickname);
325     }
326 }
327 
328 char *
pkcs11_copyNickname(char * nickname,char * space,int spaceLen)329 pkcs11_copyNickname(char *nickname, char *space, int spaceLen)
330 {
331     int len;
332     char *copy = NULL;
333 
334     len = PORT_Strlen(nickname) + 1;
335     if (len <= spaceLen) {
336         copy = space;
337         PORT_Memcpy(copy, nickname, len);
338     } else {
339         copy = PORT_Strdup(nickname);
340     }
341 
342     return copy;
343 }
344 
345 void
pkcs11_freeStaticData(unsigned char * data,unsigned char * space)346 pkcs11_freeStaticData(unsigned char *data, unsigned char *space)
347 {
348     if (data && data != space) {
349         PORT_Free(data);
350     }
351 }
352 
353 unsigned char *
pkcs11_allocStaticData(int len,unsigned char * space,int spaceLen)354 pkcs11_allocStaticData(int len, unsigned char *space, int spaceLen)
355 {
356     unsigned char *data = NULL;
357 
358     if (len <= spaceLen) {
359         data = space;
360     } else {
361         data = (unsigned char *)PORT_Alloc(len);
362     }
363 
364     return data;
365 }
366 
367 unsigned char *
pkcs11_copyStaticData(unsigned char * data,int len,unsigned char * space,int spaceLen)368 pkcs11_copyStaticData(unsigned char *data, int len,
369                       unsigned char *space, int spaceLen)
370 {
371     unsigned char *copy = pkcs11_allocStaticData(len, space, spaceLen);
372     if (copy) {
373         PORT_Memcpy(copy, data, len);
374     }
375 
376     return copy;
377 }
378 
379 /*
380  * destroy a database entry
381  */
382 static void
DestroyDBEntry(certDBEntry * entry)383 DestroyDBEntry(certDBEntry *entry)
384 {
385     PLArenaPool *arena = entry->common.arena;
386 
387     /* must be one of our certDBEntry from the free list */
388     if (arena == NULL) {
389         certDBEntryCert *certEntry;
390         if (entry->common.type != certDBEntryTypeCert) {
391             return;
392         }
393         certEntry = (certDBEntryCert *)entry;
394 
395         pkcs11_freeStaticData(certEntry->derCert.data, certEntry->derCertSpace);
396         pkcs11_freeNickname(certEntry->nickname, certEntry->nicknameSpace);
397 
398         nsslowcert_LockFreeList();
399         if (entryListCount > MAX_ENTRY_LIST_COUNT) {
400             PORT_Free(certEntry);
401         } else {
402             entryListCount++;
403             PORT_Memset(certEntry, 0, sizeof(*certEntry));
404             certEntry->next = entryListHead;
405             entryListHead = certEntry;
406         }
407         nsslowcert_UnlockFreeList();
408         return;
409     }
410 
411     /* Zero out the entry struct, so that any further attempts to use it
412      * will cause an exception (e.g. null pointer reference). */
413     PORT_Memset(&entry->common, 0, sizeof entry->common);
414     PORT_FreeArena(arena, PR_FALSE);
415 
416     return;
417 }
418 
419 /* forward references */
420 static void nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert);
421 
422 static SECStatus
DeleteDBEntry(NSSLOWCERTCertDBHandle * handle,certDBEntryType type,SECItem * dbkey)423 DeleteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, SECItem *dbkey)
424 {
425     DBT key;
426     int ret;
427 
428     /* init the database key */
429     key.data = dbkey->data;
430     key.size = dbkey->len;
431 
432     dbkey->data[0] = (unsigned char)type;
433 
434     /* delete entry from database */
435     ret = certdb_Del(handle->permCertDB, &key, 0);
436     if (ret != 0) {
437         PORT_SetError(SEC_ERROR_BAD_DATABASE);
438         goto loser;
439     }
440 
441     ret = certdb_Sync(handle->permCertDB, 0);
442     if (ret) {
443         PORT_SetError(SEC_ERROR_BAD_DATABASE);
444         goto loser;
445     }
446 
447     return (SECSuccess);
448 
449 loser:
450     return (SECFailure);
451 }
452 
453 static SECStatus
ReadDBEntry(NSSLOWCERTCertDBHandle * handle,certDBEntryCommon * entry,SECItem * dbkey,SECItem * dbentry,PLArenaPool * arena)454 ReadDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
455             SECItem *dbkey, SECItem *dbentry, PLArenaPool *arena)
456 {
457     DBT data, key;
458     int ret;
459     unsigned char *buf;
460 
461     /* init the database key */
462     key.data = dbkey->data;
463     key.size = dbkey->len;
464 
465     dbkey->data[0] = (unsigned char)entry->type;
466 
467     /* read entry from database */
468     ret = certdb_Get(handle->permCertDB, &key, &data, 0);
469     if (ret != 0) {
470         PORT_SetError(SEC_ERROR_BAD_DATABASE);
471         goto loser;
472     }
473 
474     /* validate the entry */
475     if (data.size < SEC_DB_ENTRY_HEADER_LEN) {
476         PORT_SetError(SEC_ERROR_BAD_DATABASE);
477         goto loser;
478     }
479     buf = (unsigned char *)data.data;
480     /* version 7 has the same schema, we may be using a v7 db if we openned
481      * the databases readonly. */
482     if (!((buf[0] == (unsigned char)CERT_DB_FILE_VERSION) ||
483           (buf[0] == (unsigned char)CERT_DB_V7_FILE_VERSION))) {
484         PORT_SetError(SEC_ERROR_BAD_DATABASE);
485         goto loser;
486     }
487     if (buf[1] != (unsigned char)entry->type) {
488         PORT_SetError(SEC_ERROR_BAD_DATABASE);
489         goto loser;
490     }
491 
492     /* copy out header information */
493     entry->version = (unsigned int)buf[0];
494     entry->type = (certDBEntryType)buf[1];
495     entry->flags = (unsigned int)buf[2];
496 
497     /* format body of entry for return to caller */
498     dbentry->len = data.size - SEC_DB_ENTRY_HEADER_LEN;
499     if (dbentry->len) {
500         if (arena) {
501             dbentry->data = (unsigned char *)
502                 PORT_ArenaAlloc(arena, dbentry->len);
503             if (dbentry->data == NULL) {
504                 PORT_SetError(SEC_ERROR_NO_MEMORY);
505                 goto loser;
506             }
507 
508             PORT_Memcpy(dbentry->data, &buf[SEC_DB_ENTRY_HEADER_LEN],
509                         dbentry->len);
510         } else {
511             dbentry->data = &buf[SEC_DB_ENTRY_HEADER_LEN];
512         }
513     } else {
514         dbentry->data = NULL;
515     }
516 
517     return (SECSuccess);
518 
519 loser:
520     return (SECFailure);
521 }
522 
523 /**
524  ** Implement low level database access
525  **/
526 static SECStatus
WriteDBEntry(NSSLOWCERTCertDBHandle * handle,certDBEntryCommon * entry,SECItem * dbkey,SECItem * dbentry)527 WriteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
528              SECItem *dbkey, SECItem *dbentry)
529 {
530     int ret;
531     DBT data, key;
532     unsigned char *buf;
533 
534     data.data = dbentry->data;
535     data.size = dbentry->len;
536 
537     buf = (unsigned char *)data.data;
538 
539     buf[0] = (unsigned char)entry->version;
540     buf[1] = (unsigned char)entry->type;
541     buf[2] = (unsigned char)entry->flags;
542 
543     key.data = dbkey->data;
544     key.size = dbkey->len;
545 
546     dbkey->data[0] = (unsigned char)entry->type;
547 
548     /* put the record into the database now */
549     ret = certdb_Put(handle->permCertDB, &key, &data, 0);
550 
551     if (ret != 0) {
552         goto loser;
553     }
554 
555     ret = certdb_Sync(handle->permCertDB, 0);
556 
557     if (ret) {
558         goto loser;
559     }
560 
561     return (SECSuccess);
562 
563 loser:
564     return (SECFailure);
565 }
566 
567 /*
568  * encode a database cert record
569  */
570 static SECStatus
EncodeDBCertEntry(certDBEntryCert * entry,PLArenaPool * arena,SECItem * dbitem)571 EncodeDBCertEntry(certDBEntryCert *entry, PLArenaPool *arena, SECItem *dbitem)
572 {
573     unsigned int nnlen;
574     unsigned char *buf;
575     char *nn;
576     char zbuf = 0;
577 
578     if (entry->nickname) {
579         nn = entry->nickname;
580     } else {
581         nn = &zbuf;
582     }
583     nnlen = PORT_Strlen(nn) + 1;
584 
585     /* allocate space for encoded database record, including space
586      * for low level header
587      */
588     dbitem->len = entry->derCert.len + nnlen + DB_CERT_ENTRY_HEADER_LEN +
589                   SEC_DB_ENTRY_HEADER_LEN;
590 
591     dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
592     if (dbitem->data == NULL) {
593         PORT_SetError(SEC_ERROR_NO_MEMORY);
594         goto loser;
595     }
596 
597     /* fill in database record */
598     buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
599 
600     buf[0] = (PRUint8)(entry->trust.sslFlags >> 8);
601     buf[1] = (PRUint8)(entry->trust.sslFlags);
602     buf[2] = (PRUint8)(entry->trust.emailFlags >> 8);
603     buf[3] = (PRUint8)(entry->trust.emailFlags);
604     buf[4] = (PRUint8)(entry->trust.objectSigningFlags >> 8);
605     buf[5] = (PRUint8)(entry->trust.objectSigningFlags);
606     buf[6] = (PRUint8)(entry->derCert.len >> 8);
607     buf[7] = (PRUint8)(entry->derCert.len);
608     buf[8] = (PRUint8)(nnlen >> 8);
609     buf[9] = (PRUint8)(nnlen);
610 
611     PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN], entry->derCert.data,
612                 entry->derCert.len);
613 
614     PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN + entry->derCert.len],
615                 nn, nnlen);
616 
617     return (SECSuccess);
618 
619 loser:
620     return (SECFailure);
621 }
622 
623 /*
624  * encode a database key for a cert record
625  */
626 static SECStatus
EncodeDBCertKey(const SECItem * certKey,PLArenaPool * arena,SECItem * dbkey)627 EncodeDBCertKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey)
628 {
629     unsigned int len = certKey->len + SEC_DB_KEY_HEADER_LEN;
630     if (len > NSS_MAX_LEGACY_DB_KEY_SIZE)
631         goto loser;
632     if (arena) {
633         dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, len);
634     } else {
635         if (dbkey->len < len) {
636             dbkey->data = (unsigned char *)PORT_Alloc(len);
637         }
638     }
639     dbkey->len = len;
640     if (dbkey->data == NULL) {
641         goto loser;
642     }
643     PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
644                 certKey->data, certKey->len);
645     dbkey->data[0] = certDBEntryTypeCert;
646 
647     return (SECSuccess);
648 loser:
649     return (SECFailure);
650 }
651 
652 static SECStatus
EncodeDBGenericKey(const SECItem * certKey,PLArenaPool * arena,SECItem * dbkey,certDBEntryType entryType)653 EncodeDBGenericKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey,
654                    certDBEntryType entryType)
655 {
656     /*
657      * we only allow _one_ KRL key!
658      */
659     if (entryType == certDBEntryTypeKeyRevocation) {
660         dbkey->len = SEC_DB_KEY_HEADER_LEN;
661         dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
662         if (dbkey->data == NULL) {
663             goto loser;
664         }
665         dbkey->data[0] = (unsigned char)entryType;
666         return (SECSuccess);
667     }
668 
669     dbkey->len = certKey->len + SEC_DB_KEY_HEADER_LEN;
670     if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
671         goto loser;
672     dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
673     if (dbkey->data == NULL) {
674         goto loser;
675     }
676     PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
677                 certKey->data, certKey->len);
678     dbkey->data[0] = (unsigned char)entryType;
679 
680     return (SECSuccess);
681 loser:
682     return (SECFailure);
683 }
684 
685 static SECStatus
DecodeDBCertEntry(certDBEntryCert * entry,SECItem * dbentry)686 DecodeDBCertEntry(certDBEntryCert *entry, SECItem *dbentry)
687 {
688     unsigned int nnlen;
689     unsigned int headerlen;
690     int lenoff;
691 
692     /* allow updates of old versions of the database */
693     switch (entry->common.version) {
694         case 5:
695             headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
696             lenoff = 3;
697             break;
698         case 6:
699             /* should not get here */
700             PORT_Assert(0);
701             headerlen = DB_CERT_V6_ENTRY_HEADER_LEN;
702             lenoff = 3;
703             break;
704         case 7:
705         case 8:
706             headerlen = DB_CERT_ENTRY_HEADER_LEN;
707             lenoff = 6;
708             break;
709         default:
710             /* better not get here */
711             PORT_Assert(0);
712             headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
713             lenoff = 3;
714             break;
715     }
716 
717     /* is record long enough for header? */
718     if (dbentry->len < headerlen) {
719         PORT_SetError(SEC_ERROR_BAD_DATABASE);
720         goto loser;
721     }
722 
723     /* is database entry correct length? */
724     entry->derCert.len = ((dbentry->data[lenoff] << 8) |
725                           dbentry->data[lenoff + 1]);
726     nnlen = ((dbentry->data[lenoff + 2] << 8) | dbentry->data[lenoff + 3]);
727     lenoff = dbentry->len - (entry->derCert.len + nnlen + headerlen);
728     if (lenoff) {
729         if (lenoff < 0 || (lenoff & 0xffff) != 0) {
730             PORT_SetError(SEC_ERROR_BAD_DATABASE);
731             goto loser;
732         }
733         /* The cert size exceeded 64KB.  Reconstruct the correct length. */
734         entry->derCert.len += lenoff;
735     }
736 
737     /* Is data long enough? */
738     if (dbentry->len < headerlen + entry->derCert.len) {
739         PORT_SetError(SEC_ERROR_BAD_DATABASE);
740         goto loser;
741     }
742 
743     /* copy the dercert */
744     entry->derCert.data = pkcs11_copyStaticData(&dbentry->data[headerlen],
745                                                 entry->derCert.len, entry->derCertSpace, sizeof(entry->derCertSpace));
746     if (entry->derCert.data == NULL) {
747         PORT_SetError(SEC_ERROR_NO_MEMORY);
748         goto loser;
749     }
750 
751     /* copy the nickname */
752     if (nnlen > 1) {
753         /* Is data long enough? */
754         if (dbentry->len < headerlen + entry->derCert.len + nnlen) {
755             PORT_SetError(SEC_ERROR_BAD_DATABASE);
756             goto loser;
757         }
758         entry->nickname = (char *)pkcs11_copyStaticData(
759             &dbentry->data[headerlen + entry->derCert.len], nnlen,
760             (unsigned char *)entry->nicknameSpace,
761             sizeof(entry->nicknameSpace));
762         if (entry->nickname == NULL) {
763             PORT_SetError(SEC_ERROR_NO_MEMORY);
764             goto loser;
765         }
766     } else {
767         entry->nickname = NULL;
768     }
769 
770     if (entry->common.version < 7) {
771         /* allow updates of v5 db */
772         entry->trust.sslFlags = dbentry->data[0];
773         entry->trust.emailFlags = dbentry->data[1];
774         entry->trust.objectSigningFlags = dbentry->data[2];
775     } else {
776         entry->trust.sslFlags = (dbentry->data[0] << 8) | dbentry->data[1];
777         entry->trust.emailFlags = (dbentry->data[2] << 8) | dbentry->data[3];
778         entry->trust.objectSigningFlags =
779             (dbentry->data[4] << 8) | dbentry->data[5];
780     }
781 
782     return (SECSuccess);
783 loser:
784     return (SECFailure);
785 }
786 
787 /*
788  * Create a new certDBEntryCert from existing data
789  */
790 static certDBEntryCert *
NewDBCertEntry(SECItem * derCert,char * nickname,NSSLOWCERTCertTrust * trust,int flags)791 NewDBCertEntry(SECItem *derCert, char *nickname,
792                NSSLOWCERTCertTrust *trust, int flags)
793 {
794     certDBEntryCert *entry;
795     PLArenaPool *arena = NULL;
796     int nnlen;
797 
798     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
799 
800     if (!arena) {
801         goto loser;
802     }
803 
804     entry = PORT_ArenaZNew(arena, certDBEntryCert);
805     if (entry == NULL) {
806         goto loser;
807     }
808 
809     /* fill in the dbCert */
810     entry->common.arena = arena;
811     entry->common.type = certDBEntryTypeCert;
812     entry->common.version = CERT_DB_FILE_VERSION;
813     entry->common.flags = flags;
814 
815     if (trust) {
816         entry->trust = *trust;
817     }
818 
819     entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, derCert->len);
820     if (!entry->derCert.data) {
821         goto loser;
822     }
823     entry->derCert.len = derCert->len;
824     PORT_Memcpy(entry->derCert.data, derCert->data, derCert->len);
825 
826     nnlen = (nickname ? strlen(nickname) + 1 : 0);
827 
828     if (nnlen) {
829         entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
830         if (!entry->nickname) {
831             goto loser;
832         }
833         PORT_Memcpy(entry->nickname, nickname, nnlen);
834 
835     } else {
836         entry->nickname = 0;
837     }
838 
839     return (entry);
840 
841 loser:
842 
843     /* allocation error, free arena and return */
844     if (arena) {
845         PORT_FreeArena(arena, PR_FALSE);
846     }
847 
848     PORT_SetError(SEC_ERROR_NO_MEMORY);
849     return (0);
850 }
851 
852 /*
853  * Decode a version 4 DBCert from the byte stream database format
854  * and construct a current database entry struct
855  */
856 static certDBEntryCert *
DecodeV4DBCertEntry(unsigned char * buf,int len)857 DecodeV4DBCertEntry(unsigned char *buf, int len)
858 {
859     certDBEntryCert *entry;
860     int certlen;
861     int nnlen;
862     PLArenaPool *arena;
863 
864     /* make sure length is at least long enough for the header */
865     if (len < DBCERT_V4_HEADER_LEN) {
866         PORT_SetError(SEC_ERROR_BAD_DATABASE);
867         return (0);
868     }
869 
870     /* get other lengths */
871     certlen = buf[3] << 8 | buf[4];
872     nnlen = buf[5] << 8 | buf[6];
873 
874     /* make sure DB entry is the right size */
875     if ((certlen + nnlen + DBCERT_V4_HEADER_LEN) != len) {
876         PORT_SetError(SEC_ERROR_BAD_DATABASE);
877         return (0);
878     }
879 
880     /* allocate arena */
881     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
882 
883     if (!arena) {
884         PORT_SetError(SEC_ERROR_NO_MEMORY);
885         return (0);
886     }
887 
888     /* allocate structure and members */
889     entry = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
890 
891     if (!entry) {
892         goto loser;
893     }
894 
895     entry->common.arena = arena;
896     entry->common.version = CERT_DB_FILE_VERSION;
897     entry->common.type = certDBEntryTypeCert;
898     entry->common.flags = 0;
899     entry->trust.sslFlags = buf[0];
900     entry->trust.emailFlags = buf[1];
901     entry->trust.objectSigningFlags = buf[2];
902 
903     entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, certlen);
904     if (!entry->derCert.data) {
905         goto loser;
906     }
907     entry->derCert.len = certlen;
908     PORT_Memcpy(entry->derCert.data, &buf[DBCERT_V4_HEADER_LEN], certlen);
909 
910     if (nnlen) {
911         entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
912         if (!entry->nickname) {
913             goto loser;
914         }
915         PORT_Memcpy(entry->nickname, &buf[DBCERT_V4_HEADER_LEN + certlen], nnlen);
916 
917         if (PORT_Strcmp(entry->nickname, "Server-Cert") == 0) {
918             entry->trust.sslFlags |= CERTDB_USER;
919         }
920     } else {
921         entry->nickname = 0;
922     }
923 
924     return (entry);
925 
926 loser:
927     PORT_FreeArena(arena, PR_FALSE);
928     PORT_SetError(SEC_ERROR_NO_MEMORY);
929     return (0);
930 }
931 
932 /*
933  * Encode a Certificate database entry into byte stream suitable for
934  * the database
935  */
936 static SECStatus
WriteDBCertEntry(NSSLOWCERTCertDBHandle * handle,certDBEntryCert * entry)937 WriteDBCertEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
938 {
939     SECItem dbitem, dbkey;
940     PLArenaPool *tmparena = NULL;
941     SECItem tmpitem;
942     SECStatus rv;
943 
944     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
945     if (tmparena == NULL) {
946         goto loser;
947     }
948 
949     rv = EncodeDBCertEntry(entry, tmparena, &dbitem);
950     if (rv != SECSuccess) {
951         goto loser;
952     }
953 
954     /* get the database key and format it */
955     rv = nsslowcert_KeyFromDERCert(tmparena, &entry->derCert, &tmpitem);
956     if (rv == SECFailure) {
957         goto loser;
958     }
959 
960     rv = EncodeDBCertKey(&tmpitem, tmparena, &dbkey);
961     if (rv == SECFailure) {
962         goto loser;
963     }
964 
965     /* now write it to the database */
966     rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
967     if (rv != SECSuccess) {
968         goto loser;
969     }
970 
971     PORT_FreeArena(tmparena, PR_FALSE);
972     return (SECSuccess);
973 
974 loser:
975     if (tmparena) {
976         PORT_FreeArena(tmparena, PR_FALSE);
977     }
978     return (SECFailure);
979 }
980 
981 /*
982  * delete a certificate entry
983  */
984 static SECStatus
DeleteDBCertEntry(NSSLOWCERTCertDBHandle * handle,SECItem * certKey)985 DeleteDBCertEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey)
986 {
987     SECItem dbkey;
988     SECStatus rv;
989 
990     dbkey.data = NULL;
991     dbkey.len = 0;
992 
993     rv = EncodeDBCertKey(certKey, NULL, &dbkey);
994     if (rv != SECSuccess) {
995         goto loser;
996     }
997 
998     rv = DeleteDBEntry(handle, certDBEntryTypeCert, &dbkey);
999     if (rv == SECFailure) {
1000         goto loser;
1001     }
1002 
1003     PORT_Free(dbkey.data);
1004 
1005     return (SECSuccess);
1006 
1007 loser:
1008     if (dbkey.data) {
1009         PORT_Free(dbkey.data);
1010     }
1011     return (SECFailure);
1012 }
1013 
1014 static certDBEntryCert *
CreateCertEntry(void)1015 CreateCertEntry(void)
1016 {
1017     certDBEntryCert *entry;
1018 
1019     nsslowcert_LockFreeList();
1020     entry = entryListHead;
1021     if (entry) {
1022         entryListCount--;
1023         entryListHead = entry->next;
1024     }
1025     PORT_Assert(entryListCount >= 0);
1026     nsslowcert_UnlockFreeList();
1027     if (entry) {
1028         return entry;
1029     }
1030 
1031     return PORT_ZNew(certDBEntryCert);
1032 }
1033 
1034 static void
DestroyCertEntryFreeList(void)1035 DestroyCertEntryFreeList(void)
1036 {
1037     certDBEntryCert *entry;
1038 
1039     nsslowcert_LockFreeList();
1040     while (NULL != (entry = entryListHead)) {
1041         entryListCount--;
1042         entryListHead = entry->next;
1043         PORT_Free(entry);
1044     }
1045     PORT_Assert(!entryListCount);
1046     entryListCount = 0;
1047     nsslowcert_UnlockFreeList();
1048 }
1049 
1050 /*
1051  * Read a certificate entry
1052  */
1053 static certDBEntryCert *
ReadDBCertEntry(NSSLOWCERTCertDBHandle * handle,const SECItem * certKey)1054 ReadDBCertEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
1055 {
1056     certDBEntryCert *entry;
1057     SECItem dbkey;
1058     SECItem dbentry;
1059     SECStatus rv;
1060     unsigned char buf[512];
1061 
1062     dbkey.data = buf;
1063     dbkey.len = sizeof(buf);
1064 
1065     entry = CreateCertEntry();
1066     if (entry == NULL) {
1067         PORT_SetError(SEC_ERROR_NO_MEMORY);
1068         goto loser;
1069     }
1070     entry->common.arena = NULL;
1071     entry->common.type = certDBEntryTypeCert;
1072 
1073     rv = EncodeDBCertKey(certKey, NULL, &dbkey);
1074     if (rv != SECSuccess) {
1075         goto loser;
1076     }
1077 
1078     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
1079     if (rv == SECFailure) {
1080         goto loser;
1081     }
1082 
1083     rv = DecodeDBCertEntry(entry, &dbentry);
1084     if (rv != SECSuccess) {
1085         goto loser;
1086     }
1087 
1088     pkcs11_freeStaticData(dbkey.data, buf);
1089     dbkey.data = NULL;
1090     return (entry);
1091 
1092 loser:
1093     pkcs11_freeStaticData(dbkey.data, buf);
1094     dbkey.data = NULL;
1095     if (entry) {
1096         DestroyDBEntry((certDBEntry *)entry);
1097     }
1098 
1099     return (NULL);
1100 }
1101 
1102 /*
1103  * encode a database cert record
1104  */
1105 static SECStatus
EncodeDBCrlEntry(certDBEntryRevocation * entry,PLArenaPool * arena,SECItem * dbitem)1106 EncodeDBCrlEntry(certDBEntryRevocation *entry, PLArenaPool *arena, SECItem *dbitem)
1107 {
1108     unsigned int nnlen = 0;
1109     unsigned char *buf;
1110 
1111     if (entry->url) {
1112         nnlen = PORT_Strlen(entry->url) + 1;
1113     }
1114 
1115     /* allocate space for encoded database record, including space
1116      * for low level header
1117      */
1118     dbitem->len = entry->derCrl.len + nnlen + SEC_DB_ENTRY_HEADER_LEN + DB_CRL_ENTRY_HEADER_LEN;
1119 
1120     dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
1121     if (dbitem->data == NULL) {
1122         PORT_SetError(SEC_ERROR_NO_MEMORY);
1123         goto loser;
1124     }
1125 
1126     /* fill in database record */
1127     buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
1128 
1129     buf[0] = (PRUint8)(entry->derCrl.len >> 8);
1130     buf[1] = (PRUint8)(entry->derCrl.len);
1131     buf[2] = (PRUint8)(nnlen >> 8);
1132     buf[3] = (PRUint8)(nnlen);
1133 
1134     PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN], entry->derCrl.data,
1135                 entry->derCrl.len);
1136 
1137     if (nnlen != 0) {
1138         PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
1139                     entry->url, nnlen);
1140     }
1141 
1142     return (SECSuccess);
1143 
1144 loser:
1145     return (SECFailure);
1146 }
1147 
1148 static SECStatus
DecodeDBCrlEntry(certDBEntryRevocation * entry,SECItem * dbentry)1149 DecodeDBCrlEntry(certDBEntryRevocation *entry, SECItem *dbentry)
1150 {
1151     unsigned int urlLen;
1152     int lenDiff;
1153 
1154     /* is record long enough for header? */
1155     if (dbentry->len < DB_CRL_ENTRY_HEADER_LEN) {
1156         PORT_SetError(SEC_ERROR_BAD_DATABASE);
1157         goto loser;
1158     }
1159 
1160     /* is database entry correct length? */
1161     entry->derCrl.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
1162     urlLen = ((dbentry->data[2] << 8) | dbentry->data[3]);
1163     lenDiff = dbentry->len -
1164               (entry->derCrl.len + urlLen + DB_CRL_ENTRY_HEADER_LEN);
1165     if (lenDiff) {
1166         if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
1167             PORT_SetError(SEC_ERROR_BAD_DATABASE);
1168             goto loser;
1169         }
1170         /* CRL entry is greater than 64 K. Hack to make this continue to work */
1171         entry->derCrl.len += lenDiff;
1172     }
1173 
1174     /* copy the der CRL */
1175     entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
1176                                                           entry->derCrl.len);
1177     if (entry->derCrl.data == NULL) {
1178         PORT_SetError(SEC_ERROR_NO_MEMORY);
1179         goto loser;
1180     }
1181     PORT_Memcpy(entry->derCrl.data, &dbentry->data[DB_CRL_ENTRY_HEADER_LEN],
1182                 entry->derCrl.len);
1183 
1184     /* copy the url */
1185     entry->url = NULL;
1186     if (urlLen != 0) {
1187         entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, urlLen);
1188         if (entry->url == NULL) {
1189             PORT_SetError(SEC_ERROR_NO_MEMORY);
1190             goto loser;
1191         }
1192         PORT_Memcpy(entry->url,
1193                     &dbentry->data[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
1194                     urlLen);
1195     }
1196 
1197     return (SECSuccess);
1198 loser:
1199     return (SECFailure);
1200 }
1201 
1202 /*
1203  * Create a new certDBEntryRevocation from existing data
1204  */
1205 static certDBEntryRevocation *
NewDBCrlEntry(SECItem * derCrl,char * url,certDBEntryType crlType,int flags)1206 NewDBCrlEntry(SECItem *derCrl, char *url, certDBEntryType crlType, int flags)
1207 {
1208     certDBEntryRevocation *entry;
1209     PLArenaPool *arena = NULL;
1210     int nnlen;
1211 
1212     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1213 
1214     if (!arena) {
1215         goto loser;
1216     }
1217 
1218     entry = PORT_ArenaZNew(arena, certDBEntryRevocation);
1219     if (entry == NULL) {
1220         goto loser;
1221     }
1222 
1223     /* fill in the dbRevolcation */
1224     entry->common.arena = arena;
1225     entry->common.type = crlType;
1226     entry->common.version = CERT_DB_FILE_VERSION;
1227     entry->common.flags = flags;
1228 
1229     entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(arena, derCrl->len);
1230     if (!entry->derCrl.data) {
1231         goto loser;
1232     }
1233 
1234     if (url) {
1235         nnlen = PORT_Strlen(url) + 1;
1236         entry->url = (char *)PORT_ArenaAlloc(arena, nnlen);
1237         if (!entry->url) {
1238             goto loser;
1239         }
1240         PORT_Memcpy(entry->url, url, nnlen);
1241     } else {
1242         entry->url = NULL;
1243     }
1244 
1245     entry->derCrl.len = derCrl->len;
1246     PORT_Memcpy(entry->derCrl.data, derCrl->data, derCrl->len);
1247 
1248     return (entry);
1249 
1250 loser:
1251 
1252     /* allocation error, free arena and return */
1253     if (arena) {
1254         PORT_FreeArena(arena, PR_FALSE);
1255     }
1256 
1257     PORT_SetError(SEC_ERROR_NO_MEMORY);
1258     return (0);
1259 }
1260 
1261 static SECStatus
WriteDBCrlEntry(NSSLOWCERTCertDBHandle * handle,certDBEntryRevocation * entry,SECItem * crlKey)1262 WriteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryRevocation *entry,
1263                 SECItem *crlKey)
1264 {
1265     SECItem dbkey;
1266     PLArenaPool *tmparena = NULL;
1267     SECItem encodedEntry;
1268     SECStatus rv;
1269 
1270     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1271     if (tmparena == NULL) {
1272         goto loser;
1273     }
1274 
1275     rv = EncodeDBCrlEntry(entry, tmparena, &encodedEntry);
1276     if (rv == SECFailure) {
1277         goto loser;
1278     }
1279 
1280     rv = EncodeDBGenericKey(crlKey, tmparena, &dbkey, entry->common.type);
1281     if (rv == SECFailure) {
1282         goto loser;
1283     }
1284 
1285     /* now write it to the database */
1286     rv = WriteDBEntry(handle, &entry->common, &dbkey, &encodedEntry);
1287     if (rv != SECSuccess) {
1288         goto loser;
1289     }
1290 
1291     PORT_FreeArena(tmparena, PR_FALSE);
1292     return (SECSuccess);
1293 
1294 loser:
1295     if (tmparena) {
1296         PORT_FreeArena(tmparena, PR_FALSE);
1297     }
1298     return (SECFailure);
1299 }
1300 /*
1301  * delete a crl entry
1302  */
1303 static SECStatus
DeleteDBCrlEntry(NSSLOWCERTCertDBHandle * handle,const SECItem * crlKey,certDBEntryType crlType)1304 DeleteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *crlKey,
1305                  certDBEntryType crlType)
1306 {
1307     SECItem dbkey;
1308     PLArenaPool *arena = NULL;
1309     SECStatus rv;
1310 
1311     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1312     if (arena == NULL) {
1313         goto loser;
1314     }
1315 
1316     rv = EncodeDBGenericKey(crlKey, arena, &dbkey, crlType);
1317     if (rv != SECSuccess) {
1318         goto loser;
1319     }
1320 
1321     rv = DeleteDBEntry(handle, crlType, &dbkey);
1322     if (rv == SECFailure) {
1323         goto loser;
1324     }
1325 
1326     PORT_FreeArena(arena, PR_FALSE);
1327     return (SECSuccess);
1328 
1329 loser:
1330     if (arena) {
1331         PORT_FreeArena(arena, PR_FALSE);
1332     }
1333 
1334     return (SECFailure);
1335 }
1336 
1337 /*
1338  * Read a certificate entry
1339  */
1340 static certDBEntryRevocation *
ReadDBCrlEntry(NSSLOWCERTCertDBHandle * handle,SECItem * certKey,certDBEntryType crlType)1341 ReadDBCrlEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey,
1342                certDBEntryType crlType)
1343 {
1344     PLArenaPool *arena = NULL;
1345     PLArenaPool *tmparena = NULL;
1346     certDBEntryRevocation *entry;
1347     SECItem dbkey;
1348     SECItem dbentry;
1349     SECStatus rv;
1350 
1351     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1352     if (arena == NULL) {
1353         PORT_SetError(SEC_ERROR_NO_MEMORY);
1354         goto loser;
1355     }
1356 
1357     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1358     if (tmparena == NULL) {
1359         PORT_SetError(SEC_ERROR_NO_MEMORY);
1360         goto loser;
1361     }
1362 
1363     entry = (certDBEntryRevocation *)
1364         PORT_ArenaAlloc(arena, sizeof(certDBEntryRevocation));
1365     if (entry == NULL) {
1366         PORT_SetError(SEC_ERROR_NO_MEMORY);
1367         goto loser;
1368     }
1369     entry->common.arena = arena;
1370     entry->common.type = crlType;
1371 
1372     rv = EncodeDBGenericKey(certKey, tmparena, &dbkey, crlType);
1373     if (rv != SECSuccess) {
1374         goto loser;
1375     }
1376 
1377     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
1378     if (rv == SECFailure) {
1379         goto loser;
1380     }
1381 
1382     rv = DecodeDBCrlEntry(entry, &dbentry);
1383     if (rv != SECSuccess) {
1384         goto loser;
1385     }
1386 
1387     PORT_FreeArena(tmparena, PR_FALSE);
1388     return (entry);
1389 
1390 loser:
1391     if (tmparena) {
1392         PORT_FreeArena(tmparena, PR_FALSE);
1393     }
1394     if (arena) {
1395         PORT_FreeArena(arena, PR_FALSE);
1396     }
1397 
1398     return (NULL);
1399 }
1400 
1401 void
nsslowcert_DestroyDBEntry(certDBEntry * entry)1402 nsslowcert_DestroyDBEntry(certDBEntry *entry)
1403 {
1404     DestroyDBEntry(entry);
1405     return;
1406 }
1407 
1408 /*
1409  * Encode a database nickname record
1410  */
1411 static SECStatus
EncodeDBNicknameEntry(certDBEntryNickname * entry,PLArenaPool * arena,SECItem * dbitem)1412 EncodeDBNicknameEntry(certDBEntryNickname *entry, PLArenaPool *arena,
1413                       SECItem *dbitem)
1414 {
1415     unsigned char *buf;
1416 
1417     /* allocate space for encoded database record, including space
1418      * for low level header
1419      */
1420     dbitem->len = entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN +
1421                   SEC_DB_ENTRY_HEADER_LEN;
1422     dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
1423     if (dbitem->data == NULL) {
1424         goto loser;
1425     }
1426 
1427     /* fill in database record */
1428     buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
1429     buf[0] = (PRUint8)(entry->subjectName.len >> 8);
1430     buf[1] = (PRUint8)(entry->subjectName.len);
1431     PORT_Memcpy(&buf[DB_NICKNAME_ENTRY_HEADER_LEN], entry->subjectName.data,
1432                 entry->subjectName.len);
1433 
1434     return (SECSuccess);
1435 
1436 loser:
1437     return (SECFailure);
1438 }
1439 
1440 /*
1441  * Encode a database key for a nickname record
1442  */
1443 static SECStatus
EncodeDBNicknameKey(char * nickname,PLArenaPool * arena,SECItem * dbkey)1444 EncodeDBNicknameKey(char *nickname, PLArenaPool *arena,
1445                     SECItem *dbkey)
1446 {
1447     unsigned int nnlen;
1448 
1449     nnlen = PORT_Strlen(nickname) + 1; /* includes null */
1450 
1451     /* now get the database key and format it */
1452     dbkey->len = nnlen + SEC_DB_KEY_HEADER_LEN;
1453     if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
1454         goto loser;
1455     dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
1456     if (dbkey->data == NULL) {
1457         goto loser;
1458     }
1459     PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], nickname, nnlen);
1460     dbkey->data[0] = certDBEntryTypeNickname;
1461 
1462     return (SECSuccess);
1463 
1464 loser:
1465     return (SECFailure);
1466 }
1467 
1468 static SECStatus
DecodeDBNicknameEntry(certDBEntryNickname * entry,SECItem * dbentry,char * nickname)1469 DecodeDBNicknameEntry(certDBEntryNickname *entry, SECItem *dbentry,
1470                       char *nickname)
1471 {
1472     int lenDiff;
1473 
1474     /* is record long enough for header? */
1475     if (dbentry->len < DB_NICKNAME_ENTRY_HEADER_LEN) {
1476         PORT_SetError(SEC_ERROR_BAD_DATABASE);
1477         goto loser;
1478     }
1479 
1480     /* is database entry correct length? */
1481     entry->subjectName.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
1482     lenDiff = dbentry->len -
1483               (entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN);
1484     if (lenDiff) {
1485         if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
1486             PORT_SetError(SEC_ERROR_BAD_DATABASE);
1487             goto loser;
1488         }
1489         /* The entry size exceeded 64KB.  Reconstruct the correct length. */
1490         entry->subjectName.len += lenDiff;
1491     }
1492 
1493     /* copy the certkey */
1494     entry->subjectName.data =
1495         (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
1496                                          entry->subjectName.len);
1497     if (entry->subjectName.data == NULL) {
1498         PORT_SetError(SEC_ERROR_NO_MEMORY);
1499         goto loser;
1500     }
1501     PORT_Memcpy(entry->subjectName.data,
1502                 &dbentry->data[DB_NICKNAME_ENTRY_HEADER_LEN],
1503                 entry->subjectName.len);
1504     entry->subjectName.type = siBuffer;
1505 
1506     entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena,
1507                                               PORT_Strlen(nickname) + 1);
1508     if (entry->nickname) {
1509         PORT_Strcpy(entry->nickname, nickname);
1510     }
1511 
1512     return (SECSuccess);
1513 
1514 loser:
1515     return (SECFailure);
1516 }
1517 
1518 /*
1519  * create a new nickname entry
1520  */
1521 static certDBEntryNickname *
NewDBNicknameEntry(char * nickname,SECItem * subjectName,unsigned int flags)1522 NewDBNicknameEntry(char *nickname, SECItem *subjectName, unsigned int flags)
1523 {
1524     PLArenaPool *arena = NULL;
1525     certDBEntryNickname *entry;
1526     int nnlen;
1527     SECStatus rv;
1528 
1529     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1530     if (arena == NULL) {
1531         PORT_SetError(SEC_ERROR_NO_MEMORY);
1532         goto loser;
1533     }
1534 
1535     entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
1536                                                    sizeof(certDBEntryNickname));
1537     if (entry == NULL) {
1538         PORT_SetError(SEC_ERROR_NO_MEMORY);
1539         goto loser;
1540     }
1541 
1542     /* init common fields */
1543     entry->common.arena = arena;
1544     entry->common.type = certDBEntryTypeNickname;
1545     entry->common.version = CERT_DB_FILE_VERSION;
1546     entry->common.flags = flags;
1547 
1548     /* copy the nickname */
1549     nnlen = PORT_Strlen(nickname) + 1;
1550 
1551     entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
1552     if (entry->nickname == NULL) {
1553         goto loser;
1554     }
1555 
1556     PORT_Memcpy(entry->nickname, nickname, nnlen);
1557 
1558     rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
1559     if (rv != SECSuccess) {
1560         goto loser;
1561     }
1562 
1563     return (entry);
1564 loser:
1565     if (arena) {
1566         PORT_FreeArena(arena, PR_FALSE);
1567     }
1568 
1569     return (NULL);
1570 }
1571 
1572 /*
1573  * delete a nickname entry
1574  */
1575 static SECStatus
DeleteDBNicknameEntry(NSSLOWCERTCertDBHandle * handle,char * nickname)1576 DeleteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
1577 {
1578     PLArenaPool *arena = NULL;
1579     SECStatus rv;
1580     SECItem dbkey;
1581 
1582     if (nickname == NULL) {
1583         return (SECSuccess);
1584     }
1585 
1586     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1587     if (arena == NULL) {
1588         goto loser;
1589     }
1590 
1591     rv = EncodeDBNicknameKey(nickname, arena, &dbkey);
1592     if (rv != SECSuccess) {
1593         goto loser;
1594     }
1595 
1596     rv = DeleteDBEntry(handle, certDBEntryTypeNickname, &dbkey);
1597     if (rv == SECFailure) {
1598         goto loser;
1599     }
1600 
1601     PORT_FreeArena(arena, PR_FALSE);
1602     return (SECSuccess);
1603 
1604 loser:
1605     if (arena) {
1606         PORT_FreeArena(arena, PR_FALSE);
1607     }
1608 
1609     return (SECFailure);
1610 }
1611 
1612 /*
1613  * Read a nickname entry
1614  */
1615 static certDBEntryNickname *
ReadDBNicknameEntry(NSSLOWCERTCertDBHandle * handle,char * nickname)1616 ReadDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
1617 {
1618     PLArenaPool *arena = NULL;
1619     PLArenaPool *tmparena = NULL;
1620     certDBEntryNickname *entry;
1621     SECItem dbkey;
1622     SECItem dbentry;
1623     SECStatus rv;
1624 
1625     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1626     if (arena == NULL) {
1627         PORT_SetError(SEC_ERROR_NO_MEMORY);
1628         goto loser;
1629     }
1630 
1631     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1632     if (tmparena == NULL) {
1633         PORT_SetError(SEC_ERROR_NO_MEMORY);
1634         goto loser;
1635     }
1636 
1637     entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
1638                                                    sizeof(certDBEntryNickname));
1639     if (entry == NULL) {
1640         PORT_SetError(SEC_ERROR_NO_MEMORY);
1641         goto loser;
1642     }
1643     entry->common.arena = arena;
1644     entry->common.type = certDBEntryTypeNickname;
1645 
1646     rv = EncodeDBNicknameKey(nickname, tmparena, &dbkey);
1647     if (rv != SECSuccess) {
1648         goto loser;
1649     }
1650 
1651     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
1652     if (rv == SECFailure) {
1653         goto loser;
1654     }
1655 
1656     /* is record long enough for header? */
1657     if (dbentry.len < DB_NICKNAME_ENTRY_HEADER_LEN) {
1658         PORT_SetError(SEC_ERROR_BAD_DATABASE);
1659         goto loser;
1660     }
1661 
1662     rv = DecodeDBNicknameEntry(entry, &dbentry, nickname);
1663     if (rv != SECSuccess) {
1664         goto loser;
1665     }
1666 
1667     PORT_FreeArena(tmparena, PR_FALSE);
1668     return (entry);
1669 
1670 loser:
1671     if (tmparena) {
1672         PORT_FreeArena(tmparena, PR_FALSE);
1673     }
1674     if (arena) {
1675         PORT_FreeArena(arena, PR_FALSE);
1676     }
1677 
1678     return (NULL);
1679 }
1680 
1681 /*
1682  * Encode a nickname entry into byte stream suitable for
1683  * the database
1684  */
1685 static SECStatus
WriteDBNicknameEntry(NSSLOWCERTCertDBHandle * handle,certDBEntryNickname * entry)1686 WriteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryNickname *entry)
1687 {
1688     SECItem dbitem, dbkey;
1689     PLArenaPool *tmparena = NULL;
1690     SECStatus rv;
1691 
1692     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1693     if (tmparena == NULL) {
1694         goto loser;
1695     }
1696 
1697     rv = EncodeDBNicknameEntry(entry, tmparena, &dbitem);
1698     if (rv != SECSuccess) {
1699         goto loser;
1700     }
1701 
1702     rv = EncodeDBNicknameKey(entry->nickname, tmparena, &dbkey);
1703     if (rv != SECSuccess) {
1704         goto loser;
1705     }
1706 
1707     /* now write it to the database */
1708     rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
1709     if (rv != SECSuccess) {
1710         goto loser;
1711     }
1712 
1713     PORT_FreeArena(tmparena, PR_FALSE);
1714     return (SECSuccess);
1715 
1716 loser:
1717     if (tmparena) {
1718         PORT_FreeArena(tmparena, PR_FALSE);
1719     }
1720     return (SECFailure);
1721 }
1722 
1723 static SECStatus
EncodeDBSMimeEntry(certDBEntrySMime * entry,PLArenaPool * arena,SECItem * dbitem)1724 EncodeDBSMimeEntry(certDBEntrySMime *entry, PLArenaPool *arena,
1725                    SECItem *dbitem)
1726 {
1727     unsigned char *buf;
1728 
1729     /* allocate space for encoded database record, including space
1730      * for low level header
1731      */
1732     dbitem->len = entry->subjectName.len + entry->smimeOptions.len +
1733                   entry->optionsDate.len +
1734                   DB_SMIME_ENTRY_HEADER_LEN + SEC_DB_ENTRY_HEADER_LEN;
1735 
1736     dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
1737     if (dbitem->data == NULL) {
1738         PORT_SetError(SEC_ERROR_NO_MEMORY);
1739         goto loser;
1740     }
1741 
1742     /* fill in database record */
1743     buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
1744 
1745     buf[0] = (PRUint8)(entry->subjectName.len >> 8);
1746     buf[1] = (PRUint8)(entry->subjectName.len);
1747     buf[2] = (PRUint8)(entry->smimeOptions.len >> 8);
1748     buf[3] = (PRUint8)(entry->smimeOptions.len);
1749     buf[4] = (PRUint8)(entry->optionsDate.len >> 8);
1750     buf[5] = (PRUint8)(entry->optionsDate.len);
1751 
1752     /* if no smime options, then there should not be an options date either */
1753     PORT_Assert(!((entry->smimeOptions.len == 0) &&
1754                   (entry->optionsDate.len != 0)));
1755 
1756     PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN], entry->subjectName.data,
1757                 entry->subjectName.len);
1758     if (entry->smimeOptions.len) {
1759         PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len],
1760                     entry->smimeOptions.data,
1761                     entry->smimeOptions.len);
1762         PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len +
1763                          entry->smimeOptions.len],
1764                     entry->optionsDate.data,
1765                     entry->optionsDate.len);
1766     }
1767 
1768     return (SECSuccess);
1769 
1770 loser:
1771     return (SECFailure);
1772 }
1773 
1774 /*
1775  * Encode a database key for a SMIME record
1776  */
1777 static SECStatus
EncodeDBSMimeKey(char * emailAddr,PLArenaPool * arena,SECItem * dbkey)1778 EncodeDBSMimeKey(char *emailAddr, PLArenaPool *arena,
1779                  SECItem *dbkey)
1780 {
1781     unsigned int addrlen;
1782 
1783     addrlen = PORT_Strlen(emailAddr) + 1; /* includes null */
1784 
1785     /* now get the database key and format it */
1786     dbkey->len = addrlen + SEC_DB_KEY_HEADER_LEN;
1787     if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
1788         goto loser;
1789     dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
1790     if (dbkey->data == NULL) {
1791         goto loser;
1792     }
1793     PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], emailAddr, addrlen);
1794     dbkey->data[0] = certDBEntryTypeSMimeProfile;
1795 
1796     return (SECSuccess);
1797 
1798 loser:
1799     return (SECFailure);
1800 }
1801 
1802 /*
1803  * Decode a database SMIME record
1804  */
1805 static SECStatus
DecodeDBSMimeEntry(certDBEntrySMime * entry,SECItem * dbentry,char * emailAddr)1806 DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
1807 {
1808     int lenDiff;
1809 
1810     /* is record long enough for header? */
1811     if (dbentry->len < DB_SMIME_ENTRY_HEADER_LEN) {
1812         PORT_SetError(SEC_ERROR_BAD_DATABASE);
1813         goto loser;
1814     }
1815 
1816     /* is database entry correct length? */
1817     entry->subjectName.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
1818     entry->smimeOptions.len = ((dbentry->data[2] << 8) | dbentry->data[3]);
1819     entry->optionsDate.len = ((dbentry->data[4] << 8) | dbentry->data[5]);
1820     lenDiff = dbentry->len - (entry->subjectName.len +
1821                               entry->smimeOptions.len +
1822                               entry->optionsDate.len +
1823                               DB_SMIME_ENTRY_HEADER_LEN);
1824     if (lenDiff) {
1825         if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
1826             PORT_SetError(SEC_ERROR_BAD_DATABASE);
1827             goto loser;
1828         }
1829         /* The entry size exceeded 64KB.  Reconstruct the correct length. */
1830         entry->subjectName.len += lenDiff;
1831     }
1832 
1833     /* copy the subject name */
1834     entry->subjectName.data =
1835         (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
1836                                          entry->subjectName.len);
1837     if (entry->subjectName.data == NULL) {
1838         PORT_SetError(SEC_ERROR_NO_MEMORY);
1839         goto loser;
1840     }
1841     PORT_Memcpy(entry->subjectName.data,
1842                 &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN],
1843                 entry->subjectName.len);
1844 
1845     /* copy the smime options */
1846     if (entry->smimeOptions.len) {
1847         entry->smimeOptions.data =
1848             (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
1849                                              entry->smimeOptions.len);
1850         if (entry->smimeOptions.data == NULL) {
1851             PORT_SetError(SEC_ERROR_NO_MEMORY);
1852             goto loser;
1853         }
1854         PORT_Memcpy(entry->smimeOptions.data,
1855                     &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
1856                                    entry->subjectName.len],
1857                     entry->smimeOptions.len);
1858     } else {
1859         entry->smimeOptions.data = NULL;
1860     }
1861     if (entry->optionsDate.len) {
1862         entry->optionsDate.data =
1863             (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
1864                                              entry->optionsDate.len);
1865         if (entry->optionsDate.data == NULL) {
1866             PORT_SetError(SEC_ERROR_NO_MEMORY);
1867             goto loser;
1868         }
1869         PORT_Memcpy(entry->optionsDate.data,
1870                     &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
1871                                    entry->subjectName.len +
1872                                    entry->smimeOptions.len],
1873                     entry->optionsDate.len);
1874     } else {
1875         entry->optionsDate.data = NULL;
1876     }
1877 
1878     /* both options and options date must either exist or not exist */
1879     if (((entry->optionsDate.len == 0) ||
1880          (entry->smimeOptions.len == 0)) &&
1881         entry->smimeOptions.len != entry->optionsDate.len) {
1882         PORT_SetError(SEC_ERROR_BAD_DATABASE);
1883         goto loser;
1884     }
1885 
1886     entry->emailAddr = (char *)PORT_ArenaAlloc(entry->common.arena,
1887                                                PORT_Strlen(emailAddr) + 1);
1888     if (entry->emailAddr) {
1889         PORT_Strcpy(entry->emailAddr, emailAddr);
1890     }
1891 
1892     return (SECSuccess);
1893 
1894 loser:
1895     return (SECFailure);
1896 }
1897 
1898 /*
1899  * create a new SMIME entry
1900  */
1901 static certDBEntrySMime *
NewDBSMimeEntry(char * emailAddr,SECItem * subjectName,SECItem * smimeOptions,SECItem * optionsDate,unsigned int flags)1902 NewDBSMimeEntry(char *emailAddr, SECItem *subjectName, SECItem *smimeOptions,
1903                 SECItem *optionsDate, unsigned int flags)
1904 {
1905     PLArenaPool *arena = NULL;
1906     certDBEntrySMime *entry;
1907     int addrlen;
1908     SECStatus rv;
1909 
1910     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1911     if (arena == NULL) {
1912         PORT_SetError(SEC_ERROR_NO_MEMORY);
1913         goto loser;
1914     }
1915 
1916     entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
1917                                                 sizeof(certDBEntrySMime));
1918     if (entry == NULL) {
1919         PORT_SetError(SEC_ERROR_NO_MEMORY);
1920         goto loser;
1921     }
1922 
1923     /* init common fields */
1924     entry->common.arena = arena;
1925     entry->common.type = certDBEntryTypeSMimeProfile;
1926     entry->common.version = CERT_DB_FILE_VERSION;
1927     entry->common.flags = flags;
1928 
1929     /* copy the email addr */
1930     addrlen = PORT_Strlen(emailAddr) + 1;
1931 
1932     entry->emailAddr = (char *)PORT_ArenaAlloc(arena, addrlen);
1933     if (entry->emailAddr == NULL) {
1934         goto loser;
1935     }
1936 
1937     PORT_Memcpy(entry->emailAddr, emailAddr, addrlen);
1938 
1939     /* copy the subject name */
1940     rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
1941     if (rv != SECSuccess) {
1942         goto loser;
1943     }
1944 
1945     /* copy the smime options */
1946     if (smimeOptions) {
1947         rv = SECITEM_CopyItem(arena, &entry->smimeOptions, smimeOptions);
1948         if (rv != SECSuccess) {
1949             goto loser;
1950         }
1951     } else {
1952         PORT_Assert(optionsDate == NULL);
1953         entry->smimeOptions.data = NULL;
1954         entry->smimeOptions.len = 0;
1955     }
1956 
1957     /* copy the options date */
1958     if (optionsDate) {
1959         rv = SECITEM_CopyItem(arena, &entry->optionsDate, optionsDate);
1960         if (rv != SECSuccess) {
1961             goto loser;
1962         }
1963     } else {
1964         PORT_Assert(smimeOptions == NULL);
1965         entry->optionsDate.data = NULL;
1966         entry->optionsDate.len = 0;
1967     }
1968 
1969     return (entry);
1970 loser:
1971     if (arena) {
1972         PORT_FreeArena(arena, PR_FALSE);
1973     }
1974 
1975     return (NULL);
1976 }
1977 
1978 /*
1979  * delete a SMIME entry
1980  */
1981 static SECStatus
DeleteDBSMimeEntry(NSSLOWCERTCertDBHandle * handle,char * emailAddr)1982 DeleteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
1983 {
1984     PLArenaPool *arena = NULL;
1985     SECStatus rv;
1986     SECItem dbkey;
1987 
1988     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1989     if (arena == NULL) {
1990         goto loser;
1991     }
1992 
1993     rv = EncodeDBSMimeKey(emailAddr, arena, &dbkey);
1994     if (rv != SECSuccess) {
1995         goto loser;
1996     }
1997 
1998     rv = DeleteDBEntry(handle, certDBEntryTypeSMimeProfile, &dbkey);
1999     if (rv == SECFailure) {
2000         goto loser;
2001     }
2002 
2003     PORT_FreeArena(arena, PR_FALSE);
2004     return (SECSuccess);
2005 
2006 loser:
2007     if (arena) {
2008         PORT_FreeArena(arena, PR_FALSE);
2009     }
2010 
2011     return (SECFailure);
2012 }
2013 
2014 /*
2015  * Read a SMIME entry
2016  */
2017 certDBEntrySMime *
nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle * handle,char * emailAddr)2018 nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
2019 {
2020     PLArenaPool *arena = NULL;
2021     PLArenaPool *tmparena = NULL;
2022     certDBEntrySMime *entry = NULL;
2023     SECItem dbkey;
2024     SECItem dbentry;
2025     SECStatus rv;
2026 
2027     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2028     if (arena == NULL) {
2029         PORT_SetError(SEC_ERROR_NO_MEMORY);
2030         goto loser;
2031     }
2032 
2033     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2034     if (tmparena == NULL) {
2035         PORT_SetError(SEC_ERROR_NO_MEMORY);
2036         goto loser;
2037     }
2038 
2039     entry = (certDBEntrySMime *)PORT_ArenaZAlloc(arena,
2040                                                  sizeof(certDBEntrySMime));
2041     if (entry == NULL) {
2042         PORT_SetError(SEC_ERROR_NO_MEMORY);
2043         goto loser;
2044     }
2045     entry->common.arena = arena;
2046     entry->common.type = certDBEntryTypeSMimeProfile;
2047 
2048     rv = EncodeDBSMimeKey(emailAddr, tmparena, &dbkey);
2049     if (rv != SECSuccess) {
2050         goto loser;
2051     }
2052 
2053     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
2054     if (rv == SECFailure) {
2055         goto loser;
2056     }
2057 
2058     /* is record long enough for header? */
2059     if (dbentry.len < DB_SMIME_ENTRY_HEADER_LEN) {
2060         PORT_SetError(SEC_ERROR_BAD_DATABASE);
2061         goto loser;
2062     }
2063 
2064     rv = DecodeDBSMimeEntry(entry, &dbentry, emailAddr);
2065     if (rv != SECSuccess) {
2066         goto loser;
2067     }
2068 
2069     PORT_FreeArena(tmparena, PR_FALSE);
2070     return (entry);
2071 
2072 loser:
2073     if (tmparena) {
2074         PORT_FreeArena(tmparena, PR_FALSE);
2075     }
2076     if (arena) {
2077         PORT_FreeArena(arena, PR_FALSE);
2078     }
2079 
2080     return (NULL);
2081 }
2082 
2083 /*
2084  * Encode a SMIME entry into byte stream suitable for
2085  * the database
2086  */
2087 static SECStatus
WriteDBSMimeEntry(NSSLOWCERTCertDBHandle * handle,certDBEntrySMime * entry)2088 WriteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySMime *entry)
2089 {
2090     SECItem dbitem, dbkey;
2091     PLArenaPool *tmparena = NULL;
2092     SECStatus rv;
2093 
2094     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2095     if (tmparena == NULL) {
2096         goto loser;
2097     }
2098 
2099     rv = EncodeDBSMimeEntry(entry, tmparena, &dbitem);
2100     if (rv != SECSuccess) {
2101         goto loser;
2102     }
2103 
2104     rv = EncodeDBSMimeKey(entry->emailAddr, tmparena, &dbkey);
2105     if (rv != SECSuccess) {
2106         goto loser;
2107     }
2108 
2109     /* now write it to the database */
2110     rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
2111     if (rv != SECSuccess) {
2112         goto loser;
2113     }
2114 
2115     PORT_FreeArena(tmparena, PR_FALSE);
2116     return (SECSuccess);
2117 
2118 loser:
2119     if (tmparena) {
2120         PORT_FreeArena(tmparena, PR_FALSE);
2121     }
2122     return (SECFailure);
2123 }
2124 
2125 /*
2126  * Encode a database subject record
2127  */
2128 static SECStatus
EncodeDBSubjectEntry(certDBEntrySubject * entry,PLArenaPool * arena,SECItem * dbitem)2129 EncodeDBSubjectEntry(certDBEntrySubject *entry, PLArenaPool *arena,
2130                      SECItem *dbitem)
2131 {
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;
2142     ;
2143 
2144     if (entry->nickname) {
2145         nnlen = PORT_Strlen(entry->nickname) + 1;
2146     }
2147     if (entry->emailAddrs) {
2148         eaddrslen = 2;
2149         for (i = 0; i < entry->nemailAddrs; i++) {
2150             eaddrslen += PORT_Strlen(entry->emailAddrs[i]) + 1 + 2;
2151         }
2152     }
2153 
2154     ncerts = entry->ncerts;
2155 
2156     /* compute the length of the entry */
2157     keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen;
2158     len = keyidoff + (4 * ncerts) + eaddrslen;
2159     for (i = 0; i < ncerts; i++) {
2160         if (keyIDs[i].len > 0xffff ||
2161             (certKeys[i].len > 0xffff)) {
2162             PORT_SetError(SEC_ERROR_INPUT_LEN);
2163             goto loser;
2164         }
2165         len += certKeys[i].len;
2166         len += keyIDs[i].len;
2167     }
2168 
2169     /* allocate space for encoded database record, including space
2170      * for low level header
2171      */
2172     dbitem->len = len + SEC_DB_ENTRY_HEADER_LEN;
2173 
2174     dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
2175     if (dbitem->data == NULL) {
2176         PORT_SetError(SEC_ERROR_NO_MEMORY);
2177         goto loser;
2178     }
2179 
2180     /* fill in database record */
2181     buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
2182 
2183     buf[0] = (PRUint8)(ncerts >> 8);
2184     buf[1] = (PRUint8)(ncerts);
2185     buf[2] = (PRUint8)(nnlen >> 8);
2186     buf[3] = (PRUint8)(nnlen);
2187     /* v7 email field is NULL in v8 */
2188     buf[4] = 0;
2189     buf[5] = 0;
2190 
2191     PORT_Assert(DB_SUBJECT_ENTRY_HEADER_LEN == 6);
2192 
2193     if (entry->nickname) {
2194         PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen);
2195     }
2196     tmpbuf = &buf[keyidoff];
2197     for (i = 0; i < ncerts; i++) {
2198         tmpbuf[0] = (PRUint8)(certKeys[i].len >> 8);
2199         tmpbuf[1] = (PRUint8)(certKeys[i].len);
2200         tmpbuf += 2;
2201     }
2202     for (i = 0; i < ncerts; i++) {
2203         tmpbuf[0] = (PRUint8)(keyIDs[i].len >> 8);
2204         tmpbuf[1] = (PRUint8)(keyIDs[i].len);
2205         tmpbuf += 2;
2206     }
2207 
2208     for (i = 0; i < ncerts; i++) {
2209         PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
2210         tmpbuf += certKeys[i].len;
2211     }
2212     for (i = 0; i < ncerts; i++) {
2213         if (keyIDs[i].len) {
2214             PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
2215             tmpbuf += keyIDs[i].len;
2216         }
2217     }
2218 
2219     if (entry->emailAddrs) {
2220         tmpbuf[0] = (PRUint8)(entry->nemailAddrs >> 8);
2221         tmpbuf[1] = (PRUint8)(entry->nemailAddrs);
2222         tmpbuf += 2;
2223         for (i = 0; i < entry->nemailAddrs; i++) {
2224             int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1;
2225             tmpbuf[0] = (PRUint8)(nameLen >> 8);
2226             tmpbuf[1] = (PRUint8)(nameLen);
2227             tmpbuf += 2;
2228             PORT_Memcpy(tmpbuf, entry->emailAddrs[i], nameLen);
2229             tmpbuf += nameLen;
2230         }
2231     }
2232 
2233     PORT_Assert(tmpbuf == &buf[len]);
2234 
2235     return (SECSuccess);
2236 
2237 loser:
2238     return (SECFailure);
2239 }
2240 
2241 /*
2242  * Encode a database key for a subject record
2243  */
2244 static SECStatus
EncodeDBSubjectKey(SECItem * derSubject,PLArenaPool * arena,SECItem * dbkey)2245 EncodeDBSubjectKey(SECItem *derSubject, PLArenaPool *arena,
2246                    SECItem *dbkey)
2247 {
2248     dbkey->len = derSubject->len + SEC_DB_KEY_HEADER_LEN;
2249     if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
2250         goto loser;
2251     dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
2252     if (dbkey->data == NULL) {
2253         goto loser;
2254     }
2255     PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], derSubject->data,
2256                 derSubject->len);
2257     dbkey->data[0] = certDBEntryTypeSubject;
2258 
2259     return (SECSuccess);
2260 
2261 loser:
2262     return (SECFailure);
2263 }
2264 
2265 static SECStatus
DecodeDBSubjectEntry(certDBEntrySubject * entry,SECItem * dbentry,const SECItem * derSubject)2266 DecodeDBSubjectEntry(certDBEntrySubject *entry, SECItem *dbentry,
2267                      const SECItem *derSubject)
2268 {
2269     PLArenaPool *arena = entry->common.arena;
2270     unsigned char *tmpbuf;
2271     unsigned char *end;
2272     void *mark = PORT_ArenaMark(arena);
2273     unsigned int eaddrlen;
2274     unsigned int i;
2275     unsigned int keyidoff;
2276     unsigned int len;
2277     unsigned int ncerts = 0;
2278     unsigned int nnlen;
2279     SECStatus rv;
2280 
2281     rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
2282     if (rv != SECSuccess) {
2283         goto loser;
2284     }
2285 
2286     /* is record long enough for header? */
2287     if (dbentry->len < DB_SUBJECT_ENTRY_HEADER_LEN) {
2288         PORT_SetError(SEC_ERROR_BAD_DATABASE);
2289         goto loser;
2290     }
2291 
2292     entry->ncerts = ncerts = ((dbentry->data[0] << 8) | dbentry->data[1]);
2293     nnlen = ((dbentry->data[2] << 8) | dbentry->data[3]);
2294     eaddrlen = ((dbentry->data[4] << 8) | dbentry->data[5]);
2295     keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
2296     len = keyidoff + (4 * ncerts);
2297     if (dbentry->len < len) {
2298         PORT_SetError(SEC_ERROR_BAD_DATABASE);
2299         goto loser;
2300     }
2301 
2302     entry->certKeys = PORT_ArenaNewArray(arena, SECItem, ncerts);
2303     entry->keyIDs = PORT_ArenaNewArray(arena, SECItem, ncerts);
2304     if ((entry->certKeys == NULL) || (entry->keyIDs == NULL)) {
2305         PORT_SetError(SEC_ERROR_NO_MEMORY);
2306         goto loser;
2307     }
2308 
2309     if (nnlen > 1) { /* null terminator is stored */
2310         entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
2311         if (entry->nickname == NULL) {
2312             PORT_SetError(SEC_ERROR_NO_MEMORY);
2313             goto loser;
2314         }
2315         PORT_Memcpy(entry->nickname,
2316                     &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN],
2317                     nnlen);
2318     } else {
2319         entry->nickname = NULL;
2320     }
2321 
2322     /* if we have an old style email entry, there is only one */
2323     entry->nemailAddrs = 0;
2324     if (eaddrlen > 1) { /* null terminator is stored */
2325         entry->emailAddrs = PORT_ArenaNewArray(arena, char *, 2);
2326         if (entry->emailAddrs == NULL) {
2327             PORT_SetError(SEC_ERROR_NO_MEMORY);
2328             goto loser;
2329         }
2330         entry->emailAddrs[0] = (char *)PORT_ArenaAlloc(arena, eaddrlen);
2331         if (entry->emailAddrs[0] == NULL) {
2332             PORT_SetError(SEC_ERROR_NO_MEMORY);
2333             goto loser;
2334         }
2335         PORT_Memcpy(entry->emailAddrs[0],
2336                     &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN + nnlen],
2337                     eaddrlen);
2338         entry->nemailAddrs = 1;
2339     } else {
2340         entry->emailAddrs = NULL;
2341     }
2342 
2343     /* collect the lengths of the certKeys and keyIDs, and total the
2344      * overall length.
2345      */
2346     tmpbuf = &dbentry->data[keyidoff];
2347     for (i = 0; i < ncerts; i++) {
2348         unsigned int itemlen = (tmpbuf[0] << 8) | tmpbuf[1];
2349         entry->certKeys[i].len = itemlen;
2350         len += itemlen;
2351         tmpbuf += 2;
2352     }
2353     for (i = 0; i < ncerts; i++) {
2354         unsigned int itemlen = (tmpbuf[0] << 8) | tmpbuf[1];
2355         entry->keyIDs[i].len = itemlen;
2356         len += itemlen;
2357         tmpbuf += 2;
2358     }
2359 
2360     /* is encoded entry large enough ? */
2361     if (len > dbentry->len) {
2362         PORT_SetError(SEC_ERROR_BAD_DATABASE);
2363         goto loser;
2364     }
2365 
2366     for (i = 0; i < ncerts; i++) {
2367         unsigned int kLen = entry->certKeys[i].len;
2368         entry->certKeys[i].data = (unsigned char *)PORT_ArenaAlloc(arena, kLen);
2369         if (entry->certKeys[i].data == NULL) {
2370             PORT_SetError(SEC_ERROR_NO_MEMORY);
2371             goto loser;
2372         }
2373         PORT_Memcpy(entry->certKeys[i].data, tmpbuf, kLen);
2374         tmpbuf += kLen;
2375     }
2376     for (i = 0; i < ncerts; i++) {
2377         unsigned int iLen = entry->keyIDs[i].len;
2378         entry->keyIDs[i].data = (unsigned char *)PORT_ArenaAlloc(arena, iLen);
2379         if (entry->keyIDs[i].data == NULL) {
2380             PORT_SetError(SEC_ERROR_NO_MEMORY);
2381             goto loser;
2382         }
2383         PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, iLen);
2384         tmpbuf += iLen;
2385     }
2386 
2387     end = dbentry->data + dbentry->len;
2388     if ((eaddrlen == 0) && (end - tmpbuf > 1)) {
2389         /* read in the additional email addresses */
2390         entry->nemailAddrs = (((unsigned int)tmpbuf[0]) << 8) | tmpbuf[1];
2391         tmpbuf += 2;
2392         if (end - tmpbuf < 2 * (int)entry->nemailAddrs)
2393             goto loser;
2394         entry->emailAddrs = PORT_ArenaNewArray(arena, char *, entry->nemailAddrs);
2395         if (entry->emailAddrs == NULL) {
2396             PORT_SetError(SEC_ERROR_NO_MEMORY);
2397             goto loser;
2398         }
2399         for (i = 0; i < entry->nemailAddrs; i++) {
2400             int nameLen;
2401             if (end - tmpbuf < 2) {
2402                 goto loser;
2403             }
2404             nameLen = (((int)tmpbuf[0]) << 8) | tmpbuf[1];
2405             tmpbuf += 2;
2406             if (end - tmpbuf < nameLen) {
2407                 goto loser;
2408             }
2409             entry->emailAddrs[i] = PORT_ArenaAlloc(arena, nameLen);
2410             if (entry->emailAddrs == NULL) {
2411                 PORT_SetError(SEC_ERROR_NO_MEMORY);
2412                 goto loser;
2413             }
2414             PORT_Memcpy(entry->emailAddrs[i], tmpbuf, nameLen);
2415             tmpbuf += nameLen;
2416         }
2417         if (tmpbuf != end)
2418             goto loser;
2419     }
2420     PORT_ArenaUnmark(arena, mark);
2421     return (SECSuccess);
2422 
2423 loser:
2424     PORT_ArenaRelease(arena, mark); /* discard above allocations */
2425     return (SECFailure);
2426 }
2427 
2428 /*
2429  * create a new subject entry with a single cert
2430  */
2431 static certDBEntrySubject *
NewDBSubjectEntry(SECItem * derSubject,SECItem * certKey,SECItem * keyID,char * nickname,char * emailAddr,unsigned int flags)2432 NewDBSubjectEntry(SECItem *derSubject, SECItem *certKey,
2433                   SECItem *keyID, char *nickname, char *emailAddr,
2434                   unsigned int flags)
2435 {
2436     PLArenaPool *arena = NULL;
2437     certDBEntrySubject *entry;
2438     SECStatus rv;
2439     unsigned int nnlen;
2440 
2441     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2442     if (arena == NULL) {
2443         PORT_SetError(SEC_ERROR_NO_MEMORY);
2444         goto loser;
2445     }
2446 
2447     entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
2448                                                   sizeof(certDBEntrySubject));
2449     if (entry == NULL) {
2450         PORT_SetError(SEC_ERROR_NO_MEMORY);
2451         goto loser;
2452     }
2453 
2454     /* init common fields */
2455     entry->common.arena = arena;
2456     entry->common.type = certDBEntryTypeSubject;
2457     entry->common.version = CERT_DB_FILE_VERSION;
2458     entry->common.flags = flags;
2459 
2460     /* copy the subject */
2461     rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
2462     if (rv != SECSuccess) {
2463         goto loser;
2464     }
2465 
2466     entry->ncerts = 1;
2467     entry->nemailAddrs = 0;
2468     /* copy nickname */
2469     if (nickname && (*nickname != '\0')) {
2470         nnlen = PORT_Strlen(nickname) + 1;
2471         entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
2472         if (entry->nickname == NULL) {
2473             goto loser;
2474         }
2475 
2476         PORT_Memcpy(entry->nickname, nickname, nnlen);
2477     } else {
2478         entry->nickname = NULL;
2479     }
2480 
2481     /* copy email addr */
2482     if (emailAddr && (*emailAddr != '\0')) {
2483         emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
2484         if (emailAddr == NULL) {
2485             entry->emailAddrs = NULL;
2486             goto loser;
2487         }
2488 
2489         entry->emailAddrs = (char **)PORT_ArenaAlloc(arena, sizeof(char *));
2490         if (entry->emailAddrs == NULL) {
2491             PORT_Free(emailAddr);
2492             goto loser;
2493         }
2494         entry->emailAddrs[0] = PORT_ArenaStrdup(arena, emailAddr);
2495         if (entry->emailAddrs[0]) {
2496             entry->nemailAddrs = 1;
2497         }
2498 
2499         PORT_Free(emailAddr);
2500     } else {
2501         entry->emailAddrs = NULL;
2502     }
2503 
2504     /* allocate space for certKeys and keyIDs */
2505     entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
2506     entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
2507     if ((entry->certKeys == NULL) || (entry->keyIDs == NULL)) {
2508         goto loser;
2509     }
2510 
2511     /* copy the certKey and keyID */
2512     rv = SECITEM_CopyItem(arena, &entry->certKeys[0], certKey);
2513     if (rv != SECSuccess) {
2514         goto loser;
2515     }
2516     rv = SECITEM_CopyItem(arena, &entry->keyIDs[0], keyID);
2517     if (rv != SECSuccess) {
2518         goto loser;
2519     }
2520 
2521     return (entry);
2522 loser:
2523     if (arena) {
2524         PORT_FreeArena(arena, PR_FALSE);
2525     }
2526 
2527     return (NULL);
2528 }
2529 
2530 /*
2531  * delete a subject entry
2532  */
2533 static SECStatus
DeleteDBSubjectEntry(NSSLOWCERTCertDBHandle * handle,SECItem * derSubject)2534 DeleteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
2535 {
2536     SECItem dbkey;
2537     PLArenaPool *arena = NULL;
2538     SECStatus rv;
2539 
2540     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2541     if (arena == NULL) {
2542         goto loser;
2543     }
2544 
2545     rv = EncodeDBSubjectKey(derSubject, arena, &dbkey);
2546     if (rv != SECSuccess) {
2547         goto loser;
2548     }
2549 
2550     rv = DeleteDBEntry(handle, certDBEntryTypeSubject, &dbkey);
2551     if (rv == SECFailure) {
2552         goto loser;
2553     }
2554 
2555     PORT_FreeArena(arena, PR_FALSE);
2556     return (SECSuccess);
2557 
2558 loser:
2559     if (arena) {
2560         PORT_FreeArena(arena, PR_FALSE);
2561     }
2562 
2563     return (SECFailure);
2564 }
2565 
2566 /*
2567  * Read the subject entry
2568  */
2569 static certDBEntrySubject *
ReadDBSubjectEntry(NSSLOWCERTCertDBHandle * handle,SECItem * derSubject)2570 ReadDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
2571 {
2572     /* |arena| isn't function-bounded, so cannot be a PORTCheapArenaPool. */
2573     PLArenaPool *arena = NULL;
2574     PORTCheapArenaPool tmpArena;
2575 
2576     certDBEntrySubject *entry;
2577     SECItem dbkey;
2578     SECItem dbentry;
2579     SECStatus rv;
2580 
2581     PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
2582     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2583     if (arena == NULL) {
2584         PORT_SetError(SEC_ERROR_NO_MEMORY);
2585         goto loser;
2586     }
2587 
2588     entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
2589                                                   sizeof(certDBEntrySubject));
2590     if (entry == NULL) {
2591         PORT_SetError(SEC_ERROR_NO_MEMORY);
2592         goto loser;
2593     }
2594     entry->common.arena = arena;
2595     entry->common.type = certDBEntryTypeSubject;
2596 
2597     rv = EncodeDBSubjectKey(derSubject, &tmpArena.arena, &dbkey);
2598     if (rv != SECSuccess) {
2599         goto loser;
2600     }
2601 
2602     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, &tmpArena.arena);
2603     if (rv == SECFailure) {
2604         goto loser;
2605     }
2606 
2607     rv = DecodeDBSubjectEntry(entry, &dbentry, derSubject);
2608     if (rv == SECFailure) {
2609         goto loser;
2610     }
2611 
2612     PORT_DestroyCheapArena(&tmpArena);
2613     return (entry);
2614 
2615 loser:
2616     PORT_DestroyCheapArena(&tmpArena);
2617     if (arena) {
2618         PORT_FreeArena(arena, PR_FALSE);
2619     }
2620 
2621     return (NULL);
2622 }
2623 
2624 /*
2625  * Encode a subject name entry into byte stream suitable for
2626  * the database
2627  */
2628 static SECStatus
WriteDBSubjectEntry(NSSLOWCERTCertDBHandle * handle,certDBEntrySubject * entry)2629 WriteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySubject *entry)
2630 {
2631     SECItem dbitem, dbkey;
2632     PLArenaPool *tmparena = NULL;
2633     SECStatus rv;
2634 
2635     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2636     if (tmparena == NULL) {
2637         goto loser;
2638     }
2639 
2640     rv = EncodeDBSubjectEntry(entry, tmparena, &dbitem);
2641     if (rv != SECSuccess) {
2642         goto loser;
2643     }
2644 
2645     rv = EncodeDBSubjectKey(&entry->derSubject, tmparena, &dbkey);
2646     if (rv != SECSuccess) {
2647         goto loser;
2648     }
2649 
2650     /* now write it to the database */
2651     rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
2652     if (rv != SECSuccess) {
2653         goto loser;
2654     }
2655 
2656     PORT_FreeArena(tmparena, PR_FALSE);
2657     return (SECSuccess);
2658 
2659 loser:
2660     if (tmparena) {
2661         PORT_FreeArena(tmparena, PR_FALSE);
2662     }
2663     return (SECFailure);
2664 }
2665 
2666 typedef enum { nsslowcert_remove,
2667                nsslowcert_add } nsslowcertUpdateType;
2668 
2669 static SECStatus
nsslowcert_UpdateSubjectEmailAddr(NSSLOWCERTCertDBHandle * dbhandle,SECItem * derSubject,char * emailAddr,nsslowcertUpdateType updateType)2670 nsslowcert_UpdateSubjectEmailAddr(NSSLOWCERTCertDBHandle *dbhandle,
2671                                   SECItem *derSubject, char *emailAddr, nsslowcertUpdateType updateType)
2672 {
2673     certDBEntrySubject *entry = NULL;
2674     int index = -1, i;
2675     SECStatus rv;
2676 
2677     if (emailAddr) {
2678         emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
2679         if (emailAddr == NULL) {
2680             return SECFailure;
2681         }
2682     } else {
2683         return SECSuccess;
2684     }
2685 
2686     entry = ReadDBSubjectEntry(dbhandle, derSubject);
2687     if (entry == NULL) {
2688         rv = SECFailure;
2689         goto done;
2690     }
2691 
2692     for (i = 0; i < (int)(entry->nemailAddrs); i++) {
2693         if (PORT_Strcmp(entry->emailAddrs[i], emailAddr) == 0) {
2694             index = i;
2695         }
2696     }
2697 
2698     if (updateType == nsslowcert_remove) {
2699         if (index == -1) {
2700             rv = SECSuccess;
2701             goto done;
2702         }
2703         entry->nemailAddrs--;
2704         for (i = index; i < (int)(entry->nemailAddrs); i++) {
2705             entry->emailAddrs[i] = entry->emailAddrs[i + 1];
2706         }
2707     } else {
2708         char **newAddrs = NULL;
2709 
2710         if (index != -1) {
2711             rv = SECSuccess;
2712             goto done;
2713         }
2714         newAddrs = (char **)PORT_ArenaAlloc(entry->common.arena,
2715                                             (entry->nemailAddrs + 1) * sizeof(char *));
2716         if (!newAddrs) {
2717             rv = SECFailure;
2718             goto done;
2719         }
2720         for (i = 0; i < (int)(entry->nemailAddrs); i++) {
2721             newAddrs[i] = entry->emailAddrs[i];
2722         }
2723         newAddrs[entry->nemailAddrs] =
2724             PORT_ArenaStrdup(entry->common.arena, emailAddr);
2725         if (!newAddrs[entry->nemailAddrs]) {
2726             rv = SECFailure;
2727             goto done;
2728         }
2729         entry->emailAddrs = newAddrs;
2730         entry->nemailAddrs++;
2731     }
2732 
2733     /* delete the subject entry */
2734     DeleteDBSubjectEntry(dbhandle, derSubject);
2735 
2736     /* write the new one */
2737     rv = WriteDBSubjectEntry(dbhandle, entry);
2738 
2739 done:
2740     if (entry)
2741         DestroyDBEntry((certDBEntry *)entry);
2742     if (emailAddr)
2743         PORT_Free(emailAddr);
2744     return rv;
2745 }
2746 
2747 /*
2748  * writes a nickname to an existing subject entry that does not currently
2749  * have one
2750  */
2751 static SECStatus
AddNicknameToSubject(NSSLOWCERTCertDBHandle * dbhandle,NSSLOWCERTCertificate * cert,char * nickname)2752 AddNicknameToSubject(NSSLOWCERTCertDBHandle *dbhandle,
2753                      NSSLOWCERTCertificate *cert, char *nickname)
2754 {
2755     certDBEntrySubject *entry;
2756     SECStatus rv;
2757 
2758     if (nickname == NULL) {
2759         return (SECFailure);
2760     }
2761 
2762     entry = ReadDBSubjectEntry(dbhandle, &cert->derSubject);
2763     PORT_Assert(entry != NULL);
2764     if (entry == NULL) {
2765         goto loser;
2766     }
2767 
2768     PORT_Assert(entry->nickname == NULL);
2769     if (entry->nickname != NULL) {
2770         goto loser;
2771     }
2772 
2773     entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
2774 
2775     if (entry->nickname == NULL) {
2776         goto loser;
2777     }
2778 
2779     /* delete the subject entry */
2780     DeleteDBSubjectEntry(dbhandle, &cert->derSubject);
2781 
2782     /* write the new one */
2783     rv = WriteDBSubjectEntry(dbhandle, entry);
2784     if (rv != SECSuccess) {
2785         goto loser;
2786     }
2787 
2788     DestroyDBEntry((certDBEntry *)entry);
2789     return (SECSuccess);
2790 
2791 loser:
2792     DestroyDBEntry((certDBEntry *)entry);
2793     return (SECFailure);
2794 }
2795 
2796 /*
2797  * create a new version entry
2798  */
2799 static certDBEntryVersion *
NewDBVersionEntry(unsigned int flags)2800 NewDBVersionEntry(unsigned int flags)
2801 {
2802     PLArenaPool *arena = NULL;
2803     certDBEntryVersion *entry;
2804 
2805     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2806     if (arena == NULL) {
2807         PORT_SetError(SEC_ERROR_NO_MEMORY);
2808         goto loser;
2809     }
2810 
2811     entry = (certDBEntryVersion *)PORT_ArenaAlloc(arena,
2812                                                   sizeof(certDBEntryVersion));
2813     if (entry == NULL) {
2814         PORT_SetError(SEC_ERROR_NO_MEMORY);
2815         goto loser;
2816     }
2817     entry->common.arena = arena;
2818     entry->common.type = certDBEntryTypeVersion;
2819     entry->common.version = CERT_DB_FILE_VERSION;
2820     entry->common.flags = flags;
2821 
2822     return (entry);
2823 loser:
2824     if (arena) {
2825         PORT_FreeArena(arena, PR_FALSE);
2826     }
2827 
2828     return (NULL);
2829 }
2830 
2831 /*
2832  * Read the version entry
2833  */
2834 static certDBEntryVersion *
ReadDBVersionEntry(NSSLOWCERTCertDBHandle * handle)2835 ReadDBVersionEntry(NSSLOWCERTCertDBHandle *handle)
2836 {
2837     PLArenaPool *arena = NULL;
2838     PLArenaPool *tmparena = NULL;
2839     certDBEntryVersion *entry;
2840     SECItem dbkey;
2841     SECItem dbentry;
2842     SECStatus rv;
2843 
2844     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2845     if (arena == NULL) {
2846         PORT_SetError(SEC_ERROR_NO_MEMORY);
2847         goto loser;
2848     }
2849 
2850     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2851     if (tmparena == NULL) {
2852         PORT_SetError(SEC_ERROR_NO_MEMORY);
2853         goto loser;
2854     }
2855 
2856     entry = PORT_ArenaZNew(arena, certDBEntryVersion);
2857     if (entry == NULL) {
2858         PORT_SetError(SEC_ERROR_NO_MEMORY);
2859         goto loser;
2860     }
2861     entry->common.arena = arena;
2862     entry->common.type = certDBEntryTypeVersion;
2863 
2864     /* now get the database key and format it */
2865     dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
2866     dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
2867     if (dbkey.data == NULL) {
2868         goto loser;
2869     }
2870     PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
2871                 SEC_DB_VERSION_KEY_LEN);
2872 
2873     rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
2874     if (rv != SECSuccess) {
2875         goto loser;
2876     }
2877 
2878     PORT_FreeArena(tmparena, PR_FALSE);
2879     return (entry);
2880 
2881 loser:
2882     if (tmparena) {
2883         PORT_FreeArena(tmparena, PR_FALSE);
2884     }
2885     if (arena) {
2886         PORT_FreeArena(arena, PR_FALSE);
2887     }
2888 
2889     return (NULL);
2890 }
2891 
2892 /*
2893  * Encode a version entry into byte stream suitable for
2894  * the database
2895  */
2896 static SECStatus
WriteDBVersionEntry(NSSLOWCERTCertDBHandle * handle,certDBEntryVersion * entry)2897 WriteDBVersionEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryVersion *entry)
2898 {
2899     SECItem dbitem, dbkey;
2900     PLArenaPool *tmparena = NULL;
2901     SECStatus rv;
2902 
2903     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2904     if (tmparena == NULL) {
2905         goto loser;
2906     }
2907 
2908     /* allocate space for encoded database record, including space
2909      * for low level header
2910      */
2911     dbitem.len = SEC_DB_ENTRY_HEADER_LEN;
2912 
2913     dbitem.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbitem.len);
2914     if (dbitem.data == NULL) {
2915         PORT_SetError(SEC_ERROR_NO_MEMORY);
2916         goto loser;
2917     }
2918 
2919     /* now get the database key and format it */
2920     dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
2921     dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
2922     if (dbkey.data == NULL) {
2923         goto loser;
2924     }
2925     PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
2926                 SEC_DB_VERSION_KEY_LEN);
2927 
2928     /* now write it to the database */
2929     rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
2930     if (rv != SECSuccess) {
2931         goto loser;
2932     }
2933 
2934     PORT_FreeArena(tmparena, PR_FALSE);
2935     return (SECSuccess);
2936 
2937 loser:
2938     if (tmparena) {
2939         PORT_FreeArena(tmparena, PR_FALSE);
2940     }
2941     return (SECFailure);
2942 }
2943 
2944 /*
2945  * cert is no longer a perm cert, but will remain a temp cert
2946  */
2947 static SECStatus
RemovePermSubjectNode(NSSLOWCERTCertificate * cert)2948 RemovePermSubjectNode(NSSLOWCERTCertificate *cert)
2949 {
2950     certDBEntrySubject *entry;
2951     unsigned int i;
2952     SECStatus rv;
2953 
2954     entry = ReadDBSubjectEntry(cert->dbhandle, &cert->derSubject);
2955     if (entry == NULL) {
2956         return (SECFailure);
2957     }
2958 
2959     PORT_Assert(entry->ncerts);
2960     rv = SECFailure;
2961 
2962     if (entry->ncerts > 1) {
2963         for (i = 0; i < entry->ncerts; i++) {
2964             if (SECITEM_CompareItem(&entry->certKeys[i], &cert->certKey) ==
2965                 SECEqual) {
2966                 /* copy rest of list forward one entry */
2967                 for (i = i + 1; i < entry->ncerts; i++) {
2968                     entry->certKeys[i - 1] = entry->certKeys[i];
2969                     entry->keyIDs[i - 1] = entry->keyIDs[i];
2970                 }
2971                 entry->ncerts--;
2972                 DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
2973                 rv = WriteDBSubjectEntry(cert->dbhandle, entry);
2974                 break;
2975             }
2976         }
2977     } else {
2978         /* no entries left, delete the perm entry in the DB */
2979         if (entry->emailAddrs) {
2980             /* if the subject had an email record, then delete it too */
2981             for (i = 0; i < entry->nemailAddrs; i++) {
2982                 DeleteDBSMimeEntry(cert->dbhandle, entry->emailAddrs[i]);
2983             }
2984         }
2985         if (entry->nickname) {
2986             DeleteDBNicknameEntry(cert->dbhandle, entry->nickname);
2987         }
2988 
2989         DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
2990     }
2991     DestroyDBEntry((certDBEntry *)entry);
2992 
2993     return (rv);
2994 }
2995 
2996 /*
2997  * add a cert to the perm subject list
2998  */
2999 static SECStatus
AddPermSubjectNode(certDBEntrySubject * entry,NSSLOWCERTCertificate * cert,char * nickname)3000 AddPermSubjectNode(certDBEntrySubject *entry, NSSLOWCERTCertificate *cert,
3001                    char *nickname)
3002 {
3003     SECItem *newCertKeys, *newKeyIDs;
3004     unsigned int i, new_i;
3005     SECStatus rv;
3006     unsigned int ncerts;
3007 
3008     PORT_Assert(entry);
3009     ncerts = entry->ncerts;
3010 
3011     if (nickname && entry->nickname) {
3012         /* nicknames must be the same */
3013         PORT_Assert(PORT_Strcmp(nickname, entry->nickname) == 0);
3014     }
3015 
3016     if ((entry->nickname == NULL) && (nickname != NULL)) {
3017         /* copy nickname into the entry */
3018         entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
3019         if (entry->nickname == NULL) {
3020             return (SECFailure);
3021         }
3022     }
3023 
3024     /* a DB entry already exists, so add this cert */
3025     newCertKeys = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
3026     newKeyIDs = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
3027 
3028     if ((newCertKeys == NULL) || (newKeyIDs == NULL)) {
3029         return (SECFailure);
3030     }
3031 
3032     /* Step 1: copy certs older than "cert" into new entry. */
3033     for (i = 0, new_i = 0; i < ncerts; i++) {
3034         NSSLOWCERTCertificate *cmpcert;
3035         PRBool isNewer;
3036         cmpcert = nsslowcert_FindCertByKey(cert->dbhandle,
3037                                            &entry->certKeys[i]);
3038         /* The entry has been corrupted, remove it from the list */
3039         if (!cmpcert) {
3040             continue;
3041         }
3042 
3043         isNewer = nsslowcert_IsNewer(cert, cmpcert);
3044         nsslowcert_DestroyCertificate(cmpcert);
3045         if (isNewer)
3046             break;
3047         /* copy this cert entry */
3048         newCertKeys[new_i] = entry->certKeys[i];
3049         newKeyIDs[new_i] = entry->keyIDs[i];
3050         new_i++;
3051     }
3052 
3053     /* Step 2: Add "cert" to the entry. */
3054     rv = SECITEM_CopyItem(entry->common.arena, &newCertKeys[new_i],
3055                           &cert->certKey);
3056     if (rv != SECSuccess) {
3057         return (SECFailure);
3058     }
3059     rv = SECITEM_CopyItem(entry->common.arena, &newKeyIDs[new_i],
3060                           &cert->subjectKeyID);
3061     if (rv != SECSuccess) {
3062         return (SECFailure);
3063     }
3064     new_i++;
3065 
3066     /* Step 3: copy remaining certs (if any) from old entry to new. */
3067     for (; i < ncerts; i++, new_i++) {
3068         newCertKeys[new_i] = entry->certKeys[i];
3069         newKeyIDs[new_i] = entry->keyIDs[i];
3070     }
3071 
3072     /* update certKeys and keyIDs */
3073     entry->certKeys = newCertKeys;
3074     entry->keyIDs = newKeyIDs;
3075 
3076     /* set new count value */
3077     entry->ncerts = new_i;
3078 
3079     DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
3080     rv = WriteDBSubjectEntry(cert->dbhandle, entry);
3081     return (rv);
3082 }
3083 
3084 SECStatus
nsslowcert_TraversePermCertsForSubject(NSSLOWCERTCertDBHandle * handle,SECItem * derSubject,NSSLOWCERTCertCallback cb,void * cbarg)3085 nsslowcert_TraversePermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
3086                                        SECItem *derSubject,
3087                                        NSSLOWCERTCertCallback cb, void *cbarg)
3088 {
3089     certDBEntrySubject *entry;
3090     unsigned int i;
3091     NSSLOWCERTCertificate *cert;
3092     SECStatus rv = SECSuccess;
3093 
3094     entry = ReadDBSubjectEntry(handle, derSubject);
3095 
3096     if (entry == NULL) {
3097         return (SECFailure);
3098     }
3099 
3100     for (i = 0; i < entry->ncerts; i++) {
3101         cert = nsslowcert_FindCertByKey(handle, &entry->certKeys[i]);
3102         if (!cert) {
3103             continue;
3104         }
3105         rv = (*cb)(cert, cbarg);
3106         nsslowcert_DestroyCertificate(cert);
3107         if (rv == SECFailure) {
3108             break;
3109         }
3110     }
3111 
3112     DestroyDBEntry((certDBEntry *)entry);
3113 
3114     return (rv);
3115 }
3116 
3117 int
nsslowcert_NumPermCertsForSubject(NSSLOWCERTCertDBHandle * handle,SECItem * derSubject)3118 nsslowcert_NumPermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
3119                                   SECItem *derSubject)
3120 {
3121     certDBEntrySubject *entry;
3122     int ret;
3123 
3124     entry = ReadDBSubjectEntry(handle, derSubject);
3125 
3126     if (entry == NULL) {
3127         return (SECFailure);
3128     }
3129 
3130     ret = entry->ncerts;
3131 
3132     DestroyDBEntry((certDBEntry *)entry);
3133 
3134     return (ret);
3135 }
3136 
3137 SECStatus
nsslowcert_TraversePermCertsForNickname(NSSLOWCERTCertDBHandle * handle,char * nickname,NSSLOWCERTCertCallback cb,void * cbarg)3138 nsslowcert_TraversePermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
3139                                         char *nickname, NSSLOWCERTCertCallback cb, void *cbarg)
3140 {
3141     certDBEntryNickname *nnentry = NULL;
3142     certDBEntrySMime *smentry = NULL;
3143     SECStatus rv;
3144     SECItem *derSubject = NULL;
3145 
3146     nnentry = ReadDBNicknameEntry(handle, nickname);
3147     if (nnentry) {
3148         derSubject = &nnentry->subjectName;
3149     } else {
3150         smentry = nsslowcert_ReadDBSMimeEntry(handle, nickname);
3151         if (smentry) {
3152             derSubject = &smentry->subjectName;
3153         }
3154     }
3155 
3156     if (derSubject) {
3157         rv = nsslowcert_TraversePermCertsForSubject(handle, derSubject,
3158                                                     cb, cbarg);
3159     } else {
3160         rv = SECFailure;
3161     }
3162 
3163     if (nnentry) {
3164         DestroyDBEntry((certDBEntry *)nnentry);
3165     }
3166     if (smentry) {
3167         DestroyDBEntry((certDBEntry *)smentry);
3168     }
3169 
3170     return (rv);
3171 }
3172 
3173 int
nsslowcert_NumPermCertsForNickname(NSSLOWCERTCertDBHandle * handle,char * nickname)3174 nsslowcert_NumPermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
3175                                    char *nickname)
3176 {
3177     certDBEntryNickname *entry;
3178     int ret;
3179 
3180     entry = ReadDBNicknameEntry(handle, nickname);
3181 
3182     if (entry) {
3183         ret = nsslowcert_NumPermCertsForSubject(handle, &entry->subjectName);
3184         DestroyDBEntry((certDBEntry *)entry);
3185     } else {
3186         ret = 0;
3187     }
3188     return (ret);
3189 }
3190 
3191 /*
3192  * add a nickname to a cert that doesn't have one
3193  */
3194 static SECStatus
AddNicknameToPermCert(NSSLOWCERTCertDBHandle * dbhandle,NSSLOWCERTCertificate * cert,char * nickname)3195 AddNicknameToPermCert(NSSLOWCERTCertDBHandle *dbhandle,
3196                       NSSLOWCERTCertificate *cert, char *nickname)
3197 {
3198     certDBEntryCert *entry;
3199     int rv;
3200 
3201     entry = cert->dbEntry;
3202     PORT_Assert(entry != NULL);
3203     if (entry == NULL) {
3204         goto loser;
3205     }
3206 
3207     pkcs11_freeNickname(entry->nickname, entry->nicknameSpace);
3208     entry->nickname = NULL;
3209     entry->nickname = pkcs11_copyNickname(nickname, entry->nicknameSpace,
3210                                           sizeof(entry->nicknameSpace));
3211 
3212     rv = WriteDBCertEntry(dbhandle, entry);
3213     if (rv) {
3214         goto loser;
3215     }
3216 
3217     pkcs11_freeNickname(cert->nickname, cert->nicknameSpace);
3218     cert->nickname = NULL;
3219     cert->nickname = pkcs11_copyNickname(nickname, cert->nicknameSpace,
3220                                          sizeof(cert->nicknameSpace));
3221 
3222     return (SECSuccess);
3223 
3224 loser:
3225     return (SECFailure);
3226 }
3227 
3228 /*
3229  * add a nickname to a cert that is already in the perm database, but doesn't
3230  * have one yet (it is probably an e-mail cert).
3231  */
3232 SECStatus
nsslowcert_AddPermNickname(NSSLOWCERTCertDBHandle * dbhandle,NSSLOWCERTCertificate * cert,char * nickname)3233 nsslowcert_AddPermNickname(NSSLOWCERTCertDBHandle *dbhandle,
3234                            NSSLOWCERTCertificate *cert, char *nickname)
3235 {
3236     SECStatus rv = SECFailure;
3237     certDBEntrySubject *entry = NULL;
3238     certDBEntryNickname *nicknameEntry = NULL;
3239 
3240     nsslowcert_LockDB(dbhandle);
3241 
3242     entry = ReadDBSubjectEntry(dbhandle, &cert->derSubject);
3243     if (entry == NULL)
3244         goto loser;
3245 
3246     if (entry->nickname == NULL) {
3247 
3248         /* no nickname for subject */
3249         rv = AddNicknameToSubject(dbhandle, cert, nickname);
3250         if (rv != SECSuccess) {
3251             goto loser;
3252         }
3253         rv = AddNicknameToPermCert(dbhandle, cert, nickname);
3254         if (rv != SECSuccess) {
3255             goto loser;
3256         }
3257         nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
3258         if (nicknameEntry == NULL) {
3259             goto loser;
3260         }
3261 
3262         rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
3263         if (rv != SECSuccess) {
3264             goto loser;
3265         }
3266     } else {
3267         /* subject already has a nickname */
3268         rv = AddNicknameToPermCert(dbhandle, cert, entry->nickname);
3269         if (rv != SECSuccess) {
3270             goto loser;
3271         }
3272         /* make sure nickname entry exists. If the database was corrupted,
3273          * we may have lost the nickname entry. Add it back now  */
3274         nicknameEntry = ReadDBNicknameEntry(dbhandle, entry->nickname);
3275         if (nicknameEntry == NULL) {
3276             nicknameEntry = NewDBNicknameEntry(entry->nickname,
3277                                                &cert->derSubject, 0);
3278             if (nicknameEntry == NULL) {
3279                 goto loser;
3280             }
3281 
3282             rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
3283             if (rv != SECSuccess) {
3284                 goto loser;
3285             }
3286         }
3287     }
3288     rv = SECSuccess;
3289 
3290 loser:
3291     if (entry) {
3292         DestroyDBEntry((certDBEntry *)entry);
3293     }
3294     if (nicknameEntry) {
3295         DestroyDBEntry((certDBEntry *)nicknameEntry);
3296     }
3297     nsslowcert_UnlockDB(dbhandle);
3298     return (rv);
3299 }
3300 
3301 static certDBEntryCert *
AddCertToPermDB(NSSLOWCERTCertDBHandle * handle,NSSLOWCERTCertificate * cert,char * nickname,NSSLOWCERTCertTrust * trust)3302 AddCertToPermDB(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTCertificate *cert,
3303                 char *nickname, NSSLOWCERTCertTrust *trust)
3304 {
3305     certDBEntryCert *certEntry = NULL;
3306     certDBEntryNickname *nicknameEntry = NULL;
3307     certDBEntrySubject *subjectEntry = NULL;
3308     int state = 0;
3309     SECStatus rv;
3310     PRBool donnentry = PR_FALSE;
3311 
3312     if (nickname) {
3313         donnentry = PR_TRUE;
3314     }
3315 
3316     subjectEntry = ReadDBSubjectEntry(handle, &cert->derSubject);
3317 
3318     if (subjectEntry && subjectEntry->nickname) {
3319         donnentry = PR_FALSE;
3320         nickname = subjectEntry->nickname;
3321     }
3322 
3323     certEntry = NewDBCertEntry(&cert->derCert, nickname, trust, 0);
3324     if (certEntry == NULL) {
3325         goto loser;
3326     }
3327 
3328     if (donnentry) {
3329         nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
3330         if (nicknameEntry == NULL) {
3331             goto loser;
3332         }
3333     }
3334 
3335     rv = WriteDBCertEntry(handle, certEntry);
3336     if (rv != SECSuccess) {
3337         goto loser;
3338     }
3339     state = 1;
3340 
3341     if (nicknameEntry) {
3342         rv = WriteDBNicknameEntry(handle, nicknameEntry);
3343         if (rv != SECSuccess) {
3344             goto loser;
3345         }
3346     }
3347 
3348     state = 2;
3349 
3350     /* "Change" handles if necessary */
3351     cert->dbhandle = handle;
3352 
3353     /* add to or create new subject entry */
3354     if (subjectEntry) {
3355         /* REWRITE BASED ON SUBJECT ENTRY */
3356         rv = AddPermSubjectNode(subjectEntry, cert, nickname);
3357         if (rv != SECSuccess) {
3358             goto loser;
3359         }
3360     } else {
3361         /* make a new subject entry - this case is only used when updating
3362          * an old version of the database.  This is OK because the oldnickname
3363          * db format didn't allow multiple certs with the same subject.
3364          */
3365         /* where does subjectKeyID and certKey come from? */
3366         subjectEntry = NewDBSubjectEntry(&cert->derSubject, &cert->certKey,
3367                                          &cert->subjectKeyID, nickname,
3368                                          NULL, 0);
3369         if (subjectEntry == NULL) {
3370             goto loser;
3371         }
3372         rv = WriteDBSubjectEntry(handle, subjectEntry);
3373         if (rv != SECSuccess) {
3374             goto loser;
3375         }
3376     }
3377 
3378     state = 3;
3379 
3380     if (nicknameEntry) {
3381         DestroyDBEntry((certDBEntry *)nicknameEntry);
3382     }
3383 
3384     if (subjectEntry) {
3385         DestroyDBEntry((certDBEntry *)subjectEntry);
3386     }
3387 
3388     return (certEntry);
3389 
3390 loser:
3391     /* don't leave partial entry in the database */
3392     if (state > 0) {
3393         DeleteDBCertEntry(handle, &cert->certKey);
3394     }
3395     if ((state > 1) && donnentry) {
3396         DeleteDBNicknameEntry(handle, nickname);
3397     }
3398     if (certEntry) {
3399         DestroyDBEntry((certDBEntry *)certEntry);
3400     }
3401     if (nicknameEntry) {
3402         DestroyDBEntry((certDBEntry *)nicknameEntry);
3403     }
3404     if (subjectEntry) {
3405         DestroyDBEntry((certDBEntry *)subjectEntry);
3406     }
3407 
3408     return (NULL);
3409 }
3410 
3411 /* forward declaration */
3412 static SECStatus
3413 UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb);
3414 
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  */
3421 
3422 static SECStatus
UpdateV8DB(NSSLOWCERTCertDBHandle * handle,DB * updatedb)3423 UpdateV8DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
3424 {
3425     return UpdateV7DB(handle, updatedb);
3426 }
3427 
3428 /*
3429  * we could just blindly sequence through reading key data pairs and writing
3430  * them back out, but some cert.db's have gotten quite large and may have some
3431  * subtle corruption problems, so instead we cycle through the certs and
3432  * CRL's and S/MIME profiles and rebuild our subject lists from those records.
3433  */
3434 static SECStatus
UpdateV7DB(NSSLOWCERTCertDBHandle * handle,DB * updatedb)3435 UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
3436 {
3437     DBT key, data;
3438     int ret;
3439     NSSLOWCERTCertificate *cert;
3440     PRBool isKRL = PR_FALSE;
3441     certDBEntryType entryType;
3442     SECItem dbEntry, dbKey;
3443     certDBEntryRevocation crlEntry;
3444     certDBEntryCert certEntry;
3445     certDBEntrySMime smimeEntry;
3446     SECStatus rv;
3447 
3448     ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST);
3449 
3450     if (ret) {
3451         return (SECFailure);
3452     }
3453 
3454     do {
3455         unsigned char *dataBuf = (unsigned char *)data.data;
3456         unsigned char *keyBuf = (unsigned char *)key.data;
3457         dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
3458         dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
3459         entryType = (certDBEntryType)keyBuf[0];
3460         dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
3461         dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
3462         if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
3463             continue;
3464         }
3465 
3466         switch (entryType) {
3467             /* these entries will get regenerated as we read the
3468              * rest of the data from the database */
3469             case certDBEntryTypeVersion:
3470             case certDBEntryTypeSubject:
3471             case certDBEntryTypeContentVersion:
3472             case certDBEntryTypeNickname:
3473             /* smime profiles need entries created after the certs have
3474              * been imported, loop over them in a second run */
3475             case certDBEntryTypeSMimeProfile:
3476                 break;
3477 
3478             case certDBEntryTypeCert:
3479                 /* decode Entry */
3480                 certEntry.common.version = (unsigned int)dataBuf[0];
3481                 certEntry.common.type = entryType;
3482                 certEntry.common.flags = (unsigned int)dataBuf[2];
3483                 rv = DecodeDBCertEntry(&certEntry, &dbEntry);
3484                 if (rv != SECSuccess) {
3485                     break;
3486                 }
3487                 /* should we check for existing duplicates? */
3488                 cert = nsslowcert_DecodeDERCertificate(&certEntry.derCert,
3489                                                        certEntry.nickname);
3490                 if (cert) {
3491                     nsslowcert_UpdatePermCert(handle, cert, certEntry.nickname,
3492                                               &certEntry.trust);
3493                     nsslowcert_DestroyCertificate(cert);
3494                 }
3495                 /* free any data the decode may have allocated. */
3496                 pkcs11_freeStaticData(certEntry.derCert.data,
3497                                       certEntry.derCertSpace);
3498                 pkcs11_freeNickname(certEntry.nickname, certEntry.nicknameSpace);
3499                 break;
3500 
3501             case certDBEntryTypeKeyRevocation:
3502                 isKRL = PR_TRUE;
3503             /* fall through */
3504             case certDBEntryTypeRevocation:
3505                 crlEntry.common.version = (unsigned int)dataBuf[0];
3506                 crlEntry.common.type = entryType;
3507                 crlEntry.common.flags = (unsigned int)dataBuf[2];
3508                 crlEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
3509                 if (crlEntry.common.arena == NULL) {
3510                     break;
3511                 }
3512                 rv = DecodeDBCrlEntry(&crlEntry, &dbEntry);
3513                 if (rv != SECSuccess) {
3514                     break;
3515                 }
3516                 nsslowcert_UpdateCrl(handle, &crlEntry.derCrl, &dbKey,
3517                                      crlEntry.url, isKRL);
3518                 /* free data allocated by the decode */
3519                 PORT_FreeArena(crlEntry.common.arena, PR_FALSE);
3520                 crlEntry.common.arena = NULL;
3521                 break;
3522 
3523             default:
3524                 break;
3525         }
3526     } while ((*updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0);
3527 
3528     /* now loop again updating just the SMimeProfile. */
3529     ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST);
3530 
3531     if (ret) {
3532         return (SECFailure);
3533     }
3534 
3535     do {
3536         unsigned char *dataBuf = (unsigned char *)data.data;
3537         unsigned char *keyBuf = (unsigned char *)key.data;
3538         dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
3539         dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
3540         entryType = (certDBEntryType)keyBuf[0];
3541         if (entryType != certDBEntryTypeSMimeProfile) {
3542             continue;
3543         }
3544         dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
3545         dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
3546         if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
3547             continue;
3548         }
3549         smimeEntry.common.version = (unsigned int)dataBuf[0];
3550         smimeEntry.common.type = entryType;
3551         smimeEntry.common.flags = (unsigned int)dataBuf[2];
3552         smimeEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
3553         /* decode entry */
3554         rv = DecodeDBSMimeEntry(&smimeEntry, &dbEntry, (char *)dbKey.data);
3555         if (rv == SECSuccess) {
3556             nsslowcert_UpdateSMimeProfile(handle, smimeEntry.emailAddr,
3557                                           &smimeEntry.subjectName, &smimeEntry.smimeOptions,
3558                                           &smimeEntry.optionsDate);
3559         }
3560         PORT_FreeArena(smimeEntry.common.arena, PR_FALSE);
3561         smimeEntry.common.arena = NULL;
3562     } while ((*updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0);
3563 
3564     (*updatedb->close)(updatedb);
3565 
3566     /* a database update is a good time to go back and verify the integrity of
3567      * the keys and certs */
3568     handle->dbVerify = PR_TRUE;
3569     return (SECSuccess);
3570 }
3571 
3572 /*
3573  * NOTE - Version 6 DB did not go out to the real world in a release,
3574  * so we can remove this function in a later release.
3575  */
3576 static SECStatus
UpdateV6DB(NSSLOWCERTCertDBHandle * handle,DB * updatedb)3577 UpdateV6DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
3578 {
3579     int ret;
3580     DBT key, data;
3581     unsigned char *buf, *tmpbuf = NULL;
3582     certDBEntryType type;
3583     certDBEntryNickname *nnEntry = NULL;
3584     certDBEntrySubject *subjectEntry = NULL;
3585     certDBEntrySMime *emailEntry = NULL;
3586     char *nickname;
3587     char *emailAddr;
3588 
3589     /*
3590      * Sequence through the old database and copy all of the entries
3591      * to the new database.  Subject name entries will have the new
3592      * fields inserted into them (with zero length).
3593      */
3594     ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST);
3595     if (ret) {
3596         return (SECFailure);
3597     }
3598 
3599     do {
3600         buf = (unsigned char *)data.data;
3601 
3602         if (data.size >= 3) {
3603             if (buf[0] == 6) { /* version number */
3604                 type = (certDBEntryType)buf[1];
3605                 if (type == certDBEntryTypeSubject) {
3606                     /* expando subjecto entrieo */
3607                     tmpbuf = (unsigned char *)PORT_Alloc(data.size + 4);
3608                     if (tmpbuf) {
3609                         /* copy header stuff */
3610                         PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN + 2);
3611                         /* insert 4 more bytes of zero'd header */
3612                         PORT_Memset(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 2],
3613                                     0, 4);
3614                         /* copy rest of the data */
3615                         PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
3616                                     &buf[SEC_DB_ENTRY_HEADER_LEN + 2],
3617                                     data.size - (SEC_DB_ENTRY_HEADER_LEN + 2));
3618 
3619                         data.data = (void *)tmpbuf;
3620                         data.size += 4;
3621                         buf = tmpbuf;
3622                     }
3623                 } else if (type == certDBEntryTypeCert) {
3624                     /* expando certo entrieo */
3625                     tmpbuf = (unsigned char *)PORT_Alloc(data.size + 3);
3626                     if (tmpbuf) {
3627                         /* copy header stuff */
3628                         PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN);
3629 
3630                         /* copy trust flage, setting msb's to 0 */
3631                         tmpbuf[SEC_DB_ENTRY_HEADER_LEN] = 0;
3632                         tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 1] =
3633                             buf[SEC_DB_ENTRY_HEADER_LEN];
3634                         tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 2] = 0;
3635                         tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 3] =
3636                             buf[SEC_DB_ENTRY_HEADER_LEN + 1];
3637                         tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 4] = 0;
3638                         tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 5] =
3639                             buf[SEC_DB_ENTRY_HEADER_LEN + 2];
3640 
3641                         /* copy rest of the data */
3642                         PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
3643                                     &buf[SEC_DB_ENTRY_HEADER_LEN + 3],
3644                                     data.size - (SEC_DB_ENTRY_HEADER_LEN + 3));
3645 
3646                         data.data = (void *)tmpbuf;
3647                         data.size += 3;
3648                         buf = tmpbuf;
3649                     }
3650                 }
3651 
3652                 /* update the record version number */
3653                 buf[0] = CERT_DB_FILE_VERSION;
3654 
3655                 /* copy to the new database */
3656                 ret = certdb_Put(handle->permCertDB, &key, &data, 0);
3657                 if (tmpbuf) {
3658                     PORT_Free(tmpbuf);
3659                     tmpbuf = NULL;
3660                 }
3661                 if (ret) {
3662                     return SECFailure;
3663                 }
3664             }
3665         }
3666     } while ((*updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0);
3667 
3668     ret = certdb_Sync(handle->permCertDB, 0);
3669     if (ret) {
3670         return SECFailure;
3671     }
3672 
3673     ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST);
3674     if (ret) {
3675         return (SECFailure);
3676     }
3677 
3678     do {
3679         buf = (unsigned char *)data.data;
3680 
3681         if (data.size >= 3) {
3682             if (buf[0] == CERT_DB_FILE_VERSION) { /* version number */
3683                 type = (certDBEntryType)buf[1];
3684                 if (type == certDBEntryTypeNickname) {
3685                     nickname = &((char *)key.data)[1];
3686 
3687                     /* get the matching nickname entry in the new DB */
3688                     nnEntry = ReadDBNicknameEntry(handle, nickname);
3689                     if (nnEntry == NULL) {
3690                         goto endloop;
3691                     }
3692 
3693                     /* find the subject entry pointed to by nickname */
3694                     subjectEntry = ReadDBSubjectEntry(handle,
3695                                                       &nnEntry->subjectName);
3696                     if (subjectEntry == NULL) {
3697                         goto endloop;
3698                     }
3699 
3700                     subjectEntry->nickname =
3701                         (char *)PORT_ArenaAlloc(subjectEntry->common.arena,
3702                                                 key.size - 1);
3703                     if (subjectEntry->nickname) {
3704                         PORT_Memcpy(subjectEntry->nickname, nickname,
3705                                     key.size - 1);
3706                         (void)WriteDBSubjectEntry(handle, subjectEntry);
3707                     }
3708                 } else if (type == certDBEntryTypeSMimeProfile) {
3709                     emailAddr = &((char *)key.data)[1];
3710 
3711                     /* get the matching smime entry in the new DB */
3712                     emailEntry = nsslowcert_ReadDBSMimeEntry(handle, emailAddr);
3713                     if (emailEntry == NULL) {
3714                         goto endloop;
3715                     }
3716 
3717                     /* find the subject entry pointed to by nickname */
3718                     subjectEntry = ReadDBSubjectEntry(handle,
3719                                                       &emailEntry->subjectName);
3720                     if (subjectEntry == NULL) {
3721                         goto endloop;
3722                     }
3723 
3724                     subjectEntry->emailAddrs = (char **)
3725                         PORT_ArenaAlloc(subjectEntry->common.arena,
3726                                         sizeof(char *));
3727                     if (subjectEntry->emailAddrs) {
3728                         subjectEntry->emailAddrs[0] =
3729                             (char *)PORT_ArenaAlloc(subjectEntry->common.arena,
3730                                                     key.size - 1);
3731                         if (subjectEntry->emailAddrs[0]) {
3732                             PORT_Memcpy(subjectEntry->emailAddrs[0], emailAddr,
3733                                         key.size - 1);
3734                             subjectEntry->nemailAddrs = 1;
3735                             (void)WriteDBSubjectEntry(handle, subjectEntry);
3736                         }
3737                     }
3738                 }
3739 
3740             endloop:
3741                 if (subjectEntry) {
3742                     DestroyDBEntry((certDBEntry *)subjectEntry);
3743                     subjectEntry = NULL;
3744                 }
3745                 if (nnEntry) {
3746                     DestroyDBEntry((certDBEntry *)nnEntry);
3747                     nnEntry = NULL;
3748                 }
3749                 if (emailEntry) {
3750                     DestroyDBEntry((certDBEntry *)emailEntry);
3751                     emailEntry = NULL;
3752                 }
3753             }
3754         }
3755     } while ((*updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0);
3756 
3757     ret = certdb_Sync(handle->permCertDB, 0);
3758     if (ret) {
3759         return SECFailure;
3760     }
3761 
3762     (*updatedb->close)(updatedb);
3763     return (SECSuccess);
3764 }
3765 
3766 static SECStatus
updateV5Callback(NSSLOWCERTCertificate * cert,SECItem * k,void * pdata)3767 updateV5Callback(NSSLOWCERTCertificate *cert, SECItem *k, void *pdata)
3768 {
3769     NSSLOWCERTCertDBHandle *handle;
3770     certDBEntryCert *entry;
3771     NSSLOWCERTCertTrust *trust;
3772 
3773     handle = (NSSLOWCERTCertDBHandle *)pdata;
3774     trust = &cert->dbEntry->trust;
3775 
3776     /* SSL user certs can be used for email if they have an email addr */
3777     if (cert->emailAddr && (trust->sslFlags & CERTDB_USER) &&
3778         (trust->emailFlags == 0)) {
3779         trust->emailFlags = CERTDB_USER;
3780     }
3781     /* servers didn't set the user flags on the server cert.. */
3782     if (PORT_Strcmp(cert->dbEntry->nickname, "Server-Cert") == 0) {
3783         trust->sslFlags |= CERTDB_USER;
3784     }
3785 
3786     entry = AddCertToPermDB(handle, cert, cert->dbEntry->nickname,
3787                             &cert->dbEntry->trust);
3788     if (entry) {
3789         DestroyDBEntry((certDBEntry *)entry);
3790     }
3791 
3792     return (SECSuccess);
3793 }
3794 
3795 static SECStatus
UpdateV5DB(NSSLOWCERTCertDBHandle * handle,DB * updatedb)3796 UpdateV5DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
3797 {
3798     NSSLOWCERTCertDBHandle updatehandle;
3799 
3800     updatehandle.permCertDB = updatedb;
3801     updatehandle.dbMon = PZ_NewMonitor(nssILockCertDB);
3802     updatehandle.dbVerify = 0;
3803     updatehandle.ref = 1; /* prevent premature close */
3804 
3805     (void)nsslowcert_TraversePermCerts(&updatehandle, updateV5Callback,
3806                                        (void *)handle);
3807 
3808     PZ_DestroyMonitor(updatehandle.dbMon);
3809 
3810     (*updatedb->close)(updatedb);
3811     return (SECSuccess);
3812 }
3813 
3814 static PRBool
isV4DB(DB * db)3815 isV4DB(DB *db)
3816 {
3817     DBT key, data;
3818     int ret;
3819 
3820     key.data = "Version";
3821     key.size = 7;
3822 
3823     ret = (*db->get)(db, &key, &data, 0);
3824     if (ret) {
3825         return PR_FALSE;
3826     }
3827 
3828     if ((data.size == 1) && (*(unsigned char *)data.data <= 4)) {
3829         return PR_TRUE;
3830     }
3831 
3832     return PR_FALSE;
3833 }
3834 
3835 static SECStatus
UpdateV4DB(NSSLOWCERTCertDBHandle * handle,DB * updatedb)3836 UpdateV4DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
3837 {
3838     DBT key, data;
3839     certDBEntryCert *entry, *entry2;
3840     int ret;
3841     NSSLOWCERTCertificate *cert;
3842 
3843     ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST);
3844 
3845     if (ret) {
3846         return (SECFailure);
3847     }
3848 
3849     do {
3850         if (data.size != 1) { /* skip version number */
3851 
3852             /* decode the old DB entry */
3853             entry = (certDBEntryCert *)
3854                 DecodeV4DBCertEntry((unsigned char *)data.data, data.size);
3855 
3856             if (entry) {
3857                 cert = nsslowcert_DecodeDERCertificate(&entry->derCert,
3858                                                        entry->nickname);
3859 
3860                 if (cert != NULL) {
3861                     /* add to new database */
3862                     entry2 = AddCertToPermDB(handle, cert, entry->nickname,
3863                                              &entry->trust);
3864 
3865                     nsslowcert_DestroyCertificate(cert);
3866                     if (entry2) {
3867                         DestroyDBEntry((certDBEntry *)entry2);
3868                     }
3869                 }
3870                 DestroyDBEntry((certDBEntry *)entry);
3871             }
3872         }
3873     } while ((*updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0);
3874 
3875     (*updatedb->close)(updatedb);
3876     return (SECSuccess);
3877 }
3878 
3879 /*
3880  * return true if a database key conflict exists
3881  */
3882 PRBool
nsslowcert_CertDBKeyConflict(SECItem * derCert,NSSLOWCERTCertDBHandle * handle)3883 nsslowcert_CertDBKeyConflict(SECItem *derCert, NSSLOWCERTCertDBHandle *handle)
3884 {
3885     SECStatus rv;
3886     DBT tmpdata;
3887     DBT namekey;
3888     int ret;
3889     SECItem keyitem;
3890     PLArenaPool *arena = NULL;
3891     SECItem derKey;
3892 
3893     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
3894     if (arena == NULL) {
3895         goto loser;
3896     }
3897 
3898     /* get the db key of the cert */
3899     rv = nsslowcert_KeyFromDERCert(arena, derCert, &derKey);
3900     if (rv != SECSuccess) {
3901         goto loser;
3902     }
3903 
3904     rv = EncodeDBCertKey(&derKey, arena, &keyitem);
3905     if (rv != SECSuccess) {
3906         goto loser;
3907     }
3908 
3909     namekey.data = keyitem.data;
3910     namekey.size = keyitem.len;
3911 
3912     ret = certdb_Get(handle->permCertDB, &namekey, &tmpdata, 0);
3913     if (ret == 0) {
3914         goto loser;
3915     }
3916 
3917     PORT_FreeArena(arena, PR_FALSE);
3918 
3919     return (PR_FALSE);
3920 loser:
3921     if (arena) {
3922         PORT_FreeArena(arena, PR_FALSE);
3923     }
3924 
3925     return (PR_TRUE);
3926 }
3927 
3928 /*
3929  * return true if a nickname conflict exists
3930  * NOTE: caller must have already made sure that this exact cert
3931  * doesn't exist in the DB
3932  */
3933 static PRBool
nsslowcert_CertNicknameConflict(char * nickname,SECItem * derSubject,NSSLOWCERTCertDBHandle * handle)3934 nsslowcert_CertNicknameConflict(char *nickname, SECItem *derSubject,
3935                                 NSSLOWCERTCertDBHandle *handle)
3936 {
3937     PRBool rv;
3938     certDBEntryNickname *entry;
3939 
3940     if (nickname == NULL) {
3941         return (PR_FALSE);
3942     }
3943 
3944     entry = ReadDBNicknameEntry(handle, nickname);
3945 
3946     if (entry == NULL) {
3947         /* no entry for this nickname, so no conflict */
3948         return (PR_FALSE);
3949     }
3950 
3951     rv = PR_TRUE;
3952     if (SECITEM_CompareItem(derSubject, &entry->subjectName) == SECEqual) {
3953         /* if subject names are the same, then no conflict */
3954         rv = PR_FALSE;
3955     }
3956 
3957     DestroyDBEntry((certDBEntry *)entry);
3958     return (rv);
3959 }
3960 
3961 #ifdef DBM_USING_NSPR
3962 #define NO_RDONLY PR_RDONLY
3963 #define NO_RDWR PR_RDWR
3964 #define NO_CREATE (PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE)
3965 #else
3966 #define NO_RDONLY O_RDONLY
3967 #define NO_RDWR O_RDWR
3968 #define NO_CREATE (O_RDWR | O_CREAT | O_TRUNC)
3969 #endif
3970 
3971 /*
3972  * open an old database that needs to be updated
3973  */
3974 static DB *
nsslowcert_openolddb(NSSLOWCERTDBNameFunc namecb,void * cbarg,int version)3975 nsslowcert_openolddb(NSSLOWCERTDBNameFunc namecb, void *cbarg, int version)
3976 {
3977     char *tmpname;
3978     DB *updatedb = NULL;
3979 
3980     tmpname = (*namecb)(cbarg, version); /* get v6 db name */
3981     if (tmpname) {
3982         updatedb = dbopen(tmpname, NO_RDONLY, 0600, DB_HASH, 0);
3983         PORT_Free(tmpname);
3984     }
3985     return updatedb;
3986 }
3987 
3988 static SECStatus
openNewCertDB(const char * appName,const char * prefix,const char * certdbname,NSSLOWCERTCertDBHandle * handle,NSSLOWCERTDBNameFunc namecb,void * cbarg)3989 openNewCertDB(const char *appName, const char *prefix, const char *certdbname,
3990               NSSLOWCERTCertDBHandle *handle, NSSLOWCERTDBNameFunc namecb, void *cbarg)
3991 {
3992     SECStatus rv;
3993     certDBEntryVersion *versionEntry = NULL;
3994     DB *updatedb = NULL;
3995     int status = RDB_FAIL;
3996 
3997     if (appName) {
3998         handle->permCertDB = rdbopen(appName, prefix, "cert", NO_CREATE, &status);
3999     } else {
4000         handle->permCertDB = dbsopen(certdbname, NO_CREATE, 0600, DB_HASH, 0);
4001     }
4002 
4003     /* if create fails then we lose */
4004     if (handle->permCertDB == 0) {
4005         return status == RDB_RETRY ? SECWouldBlock : SECFailure;
4006     }
4007 
4008     /* Verify version number; */
4009     versionEntry = NewDBVersionEntry(0);
4010     if (versionEntry == NULL) {
4011         rv = SECFailure;
4012         goto loser;
4013     }
4014 
4015     rv = WriteDBVersionEntry(handle, versionEntry);
4016 
4017     DestroyDBEntry((certDBEntry *)versionEntry);
4018 
4019     if (rv != SECSuccess) {
4020         goto loser;
4021     }
4022 
4023     /* rv must already be Success here because of previous if statement */
4024     /* try to upgrade old db here */
4025     if (appName &&
4026         (updatedb = dbsopen(certdbname, NO_RDONLY, 0600, DB_HASH, 0)) != NULL) {
4027         rv = UpdateV8DB(handle, updatedb);
4028     } else if ((updatedb = nsslowcert_openolddb(namecb, cbarg, 7)) != NULL) {
4029         rv = UpdateV7DB(handle, updatedb);
4030     } else if ((updatedb = nsslowcert_openolddb(namecb, cbarg, 6)) != NULL) {
4031         rv = UpdateV6DB(handle, updatedb);
4032     } else if ((updatedb = nsslowcert_openolddb(namecb, cbarg, 5)) != NULL) {
4033         rv = UpdateV5DB(handle, updatedb);
4034     } else if ((updatedb = nsslowcert_openolddb(namecb, cbarg, 4)) != NULL) {
4035         /* NES has v5 format db's with v4 db names! */
4036         if (isV4DB(updatedb)) {
4037             rv = UpdateV4DB(handle, updatedb);
4038         } else {
4039             rv = UpdateV5DB(handle, updatedb);
4040         }
4041     }
4042 
4043 loser:
4044     db_InitComplete(handle->permCertDB);
4045     return rv;
4046 }
4047 
4048 static int
nsslowcert_GetVersionNumber(NSSLOWCERTCertDBHandle * handle)4049 nsslowcert_GetVersionNumber(NSSLOWCERTCertDBHandle *handle)
4050 {
4051     certDBEntryVersion *versionEntry = NULL;
4052     int version = 0;
4053 
4054     versionEntry = ReadDBVersionEntry(handle);
4055     if (versionEntry == NULL) {
4056         return 0;
4057     }
4058     version = versionEntry->common.version;
4059     DestroyDBEntry((certDBEntry *)versionEntry);
4060     return version;
4061 }
4062 
4063 /*
4064  * Open the certificate database and index databases.  Create them if
4065  * they are not there or bad.
4066  */
4067 static SECStatus
nsslowcert_OpenPermCertDB(NSSLOWCERTCertDBHandle * handle,PRBool readOnly,const char * appName,const char * prefix,NSSLOWCERTDBNameFunc namecb,void * cbarg)4068 nsslowcert_OpenPermCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
4069                           const char *appName, const char *prefix,
4070                           NSSLOWCERTDBNameFunc namecb, void *cbarg)
4071 {
4072     SECStatus rv;
4073     int openflags;
4074     char *certdbname;
4075     int version = 0;
4076 
4077     certdbname = (*namecb)(cbarg, CERT_DB_FILE_VERSION);
4078     if (certdbname == NULL) {
4079         return (SECFailure);
4080     }
4081 
4082     openflags = readOnly ? NO_RDONLY : NO_RDWR;
4083 
4084     /*
4085      * first open the permanent file based database.
4086      */
4087     if (appName) {
4088         handle->permCertDB = rdbopen(appName, prefix, "cert", openflags, NULL);
4089     } else {
4090         handle->permCertDB = dbsopen(certdbname, openflags, 0600, DB_HASH, 0);
4091     }
4092 
4093     /* check for correct version number */
4094     if (handle->permCertDB) {
4095         version = nsslowcert_GetVersionNumber(handle);
4096         if ((version != CERT_DB_FILE_VERSION) &&
4097             !(appName && version == CERT_DB_V7_FILE_VERSION)) {
4098             goto loser;
4099         }
4100     } else if (readOnly) {
4101         /* don't create if readonly */
4102         /* Try openning a version 7 database */
4103         handle->permCertDB = nsslowcert_openolddb(namecb, cbarg, 7);
4104         if (!handle->permCertDB) {
4105             goto loser;
4106         }
4107         if (nsslowcert_GetVersionNumber(handle) != 7) {
4108             goto loser;
4109         }
4110     } else {
4111         /* if first open fails, try to create a new DB */
4112         rv = openNewCertDB(appName, prefix, certdbname, handle, namecb, cbarg);
4113         if (rv == SECWouldBlock) {
4114             /* only the rdb version can fail with wouldblock */
4115             handle->permCertDB =
4116                 rdbopen(appName, prefix, "cert", openflags, NULL);
4117 
4118             /* check for correct version number */
4119             if (!handle->permCertDB) {
4120                 goto loser;
4121             }
4122             version = nsslowcert_GetVersionNumber(handle);
4123             if ((version != CERT_DB_FILE_VERSION) &&
4124                 !(appName && version == CERT_DB_V7_FILE_VERSION)) {
4125                 goto loser;
4126             }
4127         } else if (rv != SECSuccess) {
4128             goto loser;
4129         }
4130     }
4131 
4132     PORT_Free(certdbname);
4133 
4134     return (SECSuccess);
4135 
4136 loser:
4137 
4138     PORT_SetError(SEC_ERROR_BAD_DATABASE);
4139 
4140     if (handle->permCertDB) {
4141         certdb_Close(handle->permCertDB);
4142         handle->permCertDB = 0;
4143     }
4144 
4145     PORT_Free(certdbname);
4146 
4147     return (SECFailure);
4148 }
4149 
4150 /*
4151  * delete all DB records associated with a particular certificate
4152  */
4153 static SECStatus
DeletePermCert(NSSLOWCERTCertificate * cert)4154 DeletePermCert(NSSLOWCERTCertificate *cert)
4155 {
4156     SECStatus rv;
4157     SECStatus ret;
4158 
4159     ret = SECSuccess;
4160 
4161     rv = DeleteDBCertEntry(cert->dbhandle, &cert->certKey);
4162     if (rv != SECSuccess) {
4163         ret = SECFailure;
4164     }
4165 
4166     rv = RemovePermSubjectNode(cert);
4167 
4168     return (ret);
4169 }
4170 
4171 /*
4172  * Delete a certificate from the permanent database.
4173  */
4174 SECStatus
nsslowcert_DeletePermCertificate(NSSLOWCERTCertificate * cert)4175 nsslowcert_DeletePermCertificate(NSSLOWCERTCertificate *cert)
4176 {
4177     SECStatus rv;
4178 
4179     nsslowcert_LockDB(cert->dbhandle);
4180 
4181     /* delete the records from the permanent database */
4182     rv = DeletePermCert(cert);
4183 
4184     /* get rid of dbcert and stuff pointing to it */
4185     DestroyDBEntry((certDBEntry *)cert->dbEntry);
4186     cert->dbEntry = NULL;
4187     cert->trust = NULL;
4188 
4189     nsslowcert_UnlockDB(cert->dbhandle);
4190     return (rv);
4191 }
4192 
4193 /*
4194  * Traverse all of the entries in the database of a particular type
4195  * call the given function for each one.
4196  */
4197 SECStatus
nsslowcert_TraverseDBEntries(NSSLOWCERTCertDBHandle * handle,certDBEntryType type,SECStatus (* callback)(SECItem * data,SECItem * key,certDBEntryType type,void * pdata),void * udata)4198 nsslowcert_TraverseDBEntries(NSSLOWCERTCertDBHandle *handle,
4199                              certDBEntryType type,
4200                              SECStatus (*callback)(SECItem *data, SECItem *key,
4201                                                    certDBEntryType type, void *pdata),
4202                              void *udata)
4203 {
4204     DBT data;
4205     DBT key;
4206     SECStatus rv = SECSuccess;
4207     int ret;
4208     SECItem dataitem;
4209     SECItem keyitem;
4210     unsigned char *buf;
4211     unsigned char *keybuf;
4212 
4213     ret = certdb_Seq(handle->permCertDB, &key, &data, R_FIRST);
4214     if (ret) {
4215         return (SECFailure);
4216     }
4217     /* here, ret is zero and rv is SECSuccess.
4218      * Below here, ret is a count of successful calls to the callback function.
4219      */
4220     do {
4221         buf = (unsigned char *)data.data;
4222 
4223         if (buf[1] == (unsigned char)type) {
4224             dataitem.len = data.size;
4225             dataitem.data = buf;
4226             dataitem.type = siBuffer;
4227             keyitem.len = key.size - SEC_DB_KEY_HEADER_LEN;
4228             keybuf = (unsigned char *)key.data;
4229             keyitem.data = &keybuf[SEC_DB_KEY_HEADER_LEN];
4230             keyitem.type = siBuffer;
4231             /* type should equal keybuf[0].  */
4232 
4233             rv = (*callback)(&dataitem, &keyitem, type, udata);
4234             if (rv == SECSuccess) {
4235                 ++ret;
4236             }
4237         }
4238     } while (certdb_Seq(handle->permCertDB, &key, &data, R_NEXT) == 0);
4239     /* If any callbacks succeeded, or no calls to callbacks were made,
4240      * then report success.  Otherwise, report failure.
4241      */
4242     return (ret ? SECSuccess : rv);
4243 }
4244 /*
4245  * Decode a certificate and enter it into the temporary certificate database.
4246  * Deal with nicknames correctly
4247  *
4248  * This is the private entry point.
4249  */
4250 static NSSLOWCERTCertificate *
DecodeACert(NSSLOWCERTCertDBHandle * handle,certDBEntryCert * entry)4251 DecodeACert(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
4252 {
4253     NSSLOWCERTCertificate *cert = NULL;
4254 
4255     cert = nsslowcert_DecodeDERCertificate(&entry->derCert, entry->nickname);
4256 
4257     if (cert == NULL) {
4258         goto loser;
4259     }
4260 
4261     cert->dbhandle = handle;
4262     cert->dbEntry = entry;
4263     cert->trust = &entry->trust;
4264 
4265     return (cert);
4266 
4267 loser:
4268     return (0);
4269 }
4270 
4271 static NSSLOWCERTTrust *
CreateTrust(void)4272 CreateTrust(void)
4273 {
4274     NSSLOWCERTTrust *trust = NULL;
4275 
4276     nsslowcert_LockFreeList();
4277     trust = trustListHead;
4278     if (trust) {
4279         trustListCount--;
4280         trustListHead = trust->next;
4281     }
4282     PORT_Assert(trustListCount >= 0);
4283     nsslowcert_UnlockFreeList();
4284     if (trust) {
4285         return trust;
4286     }
4287 
4288     return PORT_ZNew(NSSLOWCERTTrust);
4289 }
4290 
4291 static void
DestroyTrustFreeList(void)4292 DestroyTrustFreeList(void)
4293 {
4294     NSSLOWCERTTrust *trust;
4295 
4296     nsslowcert_LockFreeList();
4297     while (NULL != (trust = trustListHead)) {
4298         trustListCount--;
4299         trustListHead = trust->next;
4300         PORT_Free(trust);
4301     }
4302     PORT_Assert(!trustListCount);
4303     trustListCount = 0;
4304     nsslowcert_UnlockFreeList();
4305 }
4306 
4307 static NSSLOWCERTTrust *
DecodeTrustEntry(NSSLOWCERTCertDBHandle * handle,certDBEntryCert * entry,const SECItem * dbKey)4308 DecodeTrustEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry,
4309                  const SECItem *dbKey)
4310 {
4311     NSSLOWCERTTrust *trust = CreateTrust();
4312     if (trust == NULL) {
4313         return trust;
4314     }
4315     trust->dbhandle = handle;
4316     trust->dbEntry = entry;
4317     trust->dbKey.data = pkcs11_copyStaticData(dbKey->data, dbKey->len,
4318                                               trust->dbKeySpace, sizeof(trust->dbKeySpace));
4319     if (!trust->dbKey.data) {
4320         PORT_Free(trust);
4321         return NULL;
4322     }
4323     trust->dbKey.len = dbKey->len;
4324 
4325     trust->trust = &entry->trust;
4326     trust->derCert = &entry->derCert;
4327 
4328     return (trust);
4329 }
4330 
4331 typedef struct {
4332     PermCertCallback certfunc;
4333     NSSLOWCERTCertDBHandle *handle;
4334     void *data;
4335 } PermCertCallbackState;
4336 
4337 /*
4338  * traversal callback to decode certs and call callers callback
4339  */
4340 static SECStatus
certcallback(SECItem * dbdata,SECItem * dbkey,certDBEntryType type,void * data)4341 certcallback(SECItem *dbdata, SECItem *dbkey, certDBEntryType type, void *data)
4342 {
4343     PermCertCallbackState *mystate;
4344     SECStatus rv;
4345     certDBEntryCert *entry;
4346     SECItem entryitem;
4347     NSSLOWCERTCertificate *cert;
4348     PLArenaPool *arena = NULL;
4349 
4350     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
4351     if (arena == NULL) {
4352         goto loser;
4353     }
4354 
4355     entry = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
4356     if (!entry) {
4357         PORT_SetError(SEC_ERROR_NO_MEMORY);
4358         goto loser;
4359     }
4360     mystate = (PermCertCallbackState *)data;
4361     entry->common.version = (unsigned int)dbdata->data[0];
4362     entry->common.type = (certDBEntryType)dbdata->data[1];
4363     entry->common.flags = (unsigned int)dbdata->data[2];
4364     entry->common.arena = arena;
4365 
4366     entryitem.len = dbdata->len - SEC_DB_ENTRY_HEADER_LEN;
4367     entryitem.data = &dbdata->data[SEC_DB_ENTRY_HEADER_LEN];
4368 
4369     rv = DecodeDBCertEntry(entry, &entryitem);
4370     if (rv != SECSuccess) {
4371         goto loser;
4372     }
4373     entry->derCert.type = siBuffer;
4374 
4375     /* note: Entry is 'inheritted'.  */
4376     cert = DecodeACert(mystate->handle, entry);
4377 
4378     rv = (*mystate->certfunc)(cert, dbkey, mystate->data);
4379 
4380     /* arena stored in entry destroyed by nsslowcert_DestroyCertificate */
4381     nsslowcert_DestroyCertificateNoLocking(cert);
4382 
4383     return (rv);
4384 
4385 loser:
4386     if (arena) {
4387         PORT_FreeArena(arena, PR_FALSE);
4388     }
4389     return (SECFailure);
4390 }
4391 
4392 /*
4393  * Traverse all of the certificates in the permanent database and
4394  * call the given function for each one; expect the caller to have lock.
4395  */
4396 static SECStatus
TraversePermCertsNoLocking(NSSLOWCERTCertDBHandle * handle,SECStatus (* certfunc)(NSSLOWCERTCertificate * cert,SECItem * k,void * pdata),void * udata)4397 TraversePermCertsNoLocking(NSSLOWCERTCertDBHandle *handle,
4398                            SECStatus (*certfunc)(NSSLOWCERTCertificate *cert,
4399                                                  SECItem *k,
4400                                                  void *pdata),
4401                            void *udata)
4402 {
4403     SECStatus rv;
4404     PermCertCallbackState mystate;
4405 
4406     mystate.certfunc = certfunc;
4407     mystate.handle = handle;
4408     mystate.data = udata;
4409     rv = nsslowcert_TraverseDBEntries(handle, certDBEntryTypeCert, certcallback,
4410                                       (void *)&mystate);
4411 
4412     return (rv);
4413 }
4414 
4415 /*
4416  * Traverse all of the certificates in the permanent database and
4417  * call the given function for each one.
4418  */
4419 SECStatus
nsslowcert_TraversePermCerts(NSSLOWCERTCertDBHandle * handle,SECStatus (* certfunc)(NSSLOWCERTCertificate * cert,SECItem * k,void * pdata),void * udata)4420 nsslowcert_TraversePermCerts(NSSLOWCERTCertDBHandle *handle,
4421                              SECStatus (*certfunc)(NSSLOWCERTCertificate *cert, SECItem *k,
4422                                                    void *pdata),
4423                              void *udata)
4424 {
4425     SECStatus rv;
4426 
4427     nsslowcert_LockDB(handle);
4428     rv = TraversePermCertsNoLocking(handle, certfunc, udata);
4429     nsslowcert_UnlockDB(handle);
4430 
4431     return (rv);
4432 }
4433 
4434 /*
4435  * Close the database
4436  */
4437 void
nsslowcert_ClosePermCertDB(NSSLOWCERTCertDBHandle * handle)4438 nsslowcert_ClosePermCertDB(NSSLOWCERTCertDBHandle *handle)
4439 {
4440     if (handle) {
4441         if (handle->permCertDB) {
4442             certdb_Close(handle->permCertDB);
4443             handle->permCertDB = NULL;
4444         }
4445         if (handle->dbMon) {
4446             PZ_DestroyMonitor(handle->dbMon);
4447             handle->dbMon = NULL;
4448         }
4449         PORT_Free(handle);
4450     }
4451     return;
4452 }
4453 
4454 /*
4455  * Get the trust attributes from a certificate
4456  */
4457 SECStatus
nsslowcert_GetCertTrust(NSSLOWCERTCertificate * cert,NSSLOWCERTCertTrust * trust)4458 nsslowcert_GetCertTrust(NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
4459 {
4460     SECStatus rv;
4461 
4462     nsslowcert_LockCertTrust(cert);
4463 
4464     if (cert->trust == NULL) {
4465         rv = SECFailure;
4466     } else {
4467         *trust = *cert->trust;
4468         rv = SECSuccess;
4469     }
4470 
4471     nsslowcert_UnlockCertTrust(cert);
4472     return (rv);
4473 }
4474 
4475 /*
4476  * Change the trust attributes of a certificate and make them permanent
4477  * in the database.
4478  */
4479 SECStatus
nsslowcert_ChangeCertTrust(NSSLOWCERTCertDBHandle * handle,NSSLOWCERTCertificate * cert,NSSLOWCERTCertTrust * trust)4480 nsslowcert_ChangeCertTrust(NSSLOWCERTCertDBHandle *handle,
4481                            NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
4482 {
4483     certDBEntryCert *entry;
4484     int rv;
4485     SECStatus ret;
4486 
4487     nsslowcert_LockDB(handle);
4488     nsslowcert_LockCertTrust(cert);
4489     /* only set the trust on permanent certs */
4490     if (cert->trust == NULL) {
4491         ret = SECFailure;
4492         goto done;
4493     }
4494 
4495     *cert->trust = *trust;
4496     if (cert->dbEntry == NULL) {
4497         ret = SECSuccess; /* not in permanent database */
4498         goto done;
4499     }
4500 
4501     entry = cert->dbEntry;
4502     entry->trust = *trust;
4503 
4504     rv = WriteDBCertEntry(handle, entry);
4505     if (rv) {
4506         ret = SECFailure;
4507         goto done;
4508     }
4509 
4510     ret = SECSuccess;
4511 
4512 done:
4513     nsslowcert_UnlockCertTrust(cert);
4514     nsslowcert_UnlockDB(handle);
4515     return (ret);
4516 }
4517 
4518 static SECStatus
nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle * dbhandle,NSSLOWCERTCertificate * cert,char * nickname,NSSLOWCERTCertTrust * trust)4519 nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
4520                           NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
4521 {
4522     char *oldnn;
4523     certDBEntryCert *entry;
4524     PRBool conflict;
4525     SECStatus ret;
4526 
4527     PORT_Assert(!cert->dbEntry);
4528 
4529     /* don't add a conflicting nickname */
4530     conflict = nsslowcert_CertNicknameConflict(nickname, &cert->derSubject,
4531                                                dbhandle);
4532     if (conflict) {
4533         ret = SECFailure;
4534         goto done;
4535     }
4536 
4537     /* save old nickname so that we can delete it */
4538     oldnn = cert->nickname;
4539 
4540     entry = AddCertToPermDB(dbhandle, cert, nickname, trust);
4541 
4542     if (entry == NULL) {
4543         ret = SECFailure;
4544         goto done;
4545     }
4546 
4547     pkcs11_freeNickname(oldnn, cert->nicknameSpace);
4548 
4549     cert->nickname = (entry->nickname) ? pkcs11_copyNickname(entry->nickname,
4550                                                              cert->nicknameSpace, sizeof(cert->nicknameSpace))
4551                                        : NULL;
4552     cert->trust = &entry->trust;
4553     cert->dbEntry = entry;
4554 
4555     ret = SECSuccess;
4556 done:
4557     return (ret);
4558 }
4559 
4560 SECStatus
nsslowcert_AddPermCert(NSSLOWCERTCertDBHandle * dbhandle,NSSLOWCERTCertificate * cert,char * nickname,NSSLOWCERTCertTrust * trust)4561 nsslowcert_AddPermCert(NSSLOWCERTCertDBHandle *dbhandle,
4562                        NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
4563 {
4564     SECStatus ret;
4565 
4566     nsslowcert_LockDB(dbhandle);
4567 
4568     ret = nsslowcert_UpdatePermCert(dbhandle, cert, nickname, trust);
4569 
4570     nsslowcert_UnlockDB(dbhandle);
4571     return (ret);
4572 }
4573 
4574 /*
4575  * Open the certificate database and index databases.  Create them if
4576  * they are not there or bad.
4577  */
4578 SECStatus
nsslowcert_OpenCertDB(NSSLOWCERTCertDBHandle * handle,PRBool readOnly,const char * appName,const char * prefix,NSSLOWCERTDBNameFunc namecb,void * cbarg,PRBool openVolatile)4579 nsslowcert_OpenCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
4580                       const char *appName, const char *prefix,
4581                       NSSLOWCERTDBNameFunc namecb, void *cbarg, PRBool openVolatile)
4582 {
4583     int rv;
4584 
4585     certdb_InitDBLock(handle);
4586 
4587     handle->dbMon = PZ_NewMonitor(nssILockCertDB);
4588     PORT_Assert(handle->dbMon != NULL);
4589     handle->dbVerify = PR_FALSE;
4590 
4591     rv = nsslowcert_OpenPermCertDB(handle, readOnly, appName, prefix,
4592                                    namecb, cbarg);
4593     if (rv) {
4594         goto loser;
4595     }
4596 
4597     return (SECSuccess);
4598 
4599 loser:
4600     if (handle->dbMon) {
4601         PZ_DestroyMonitor(handle->dbMon);
4602         handle->dbMon = NULL;
4603     }
4604     PORT_SetError(SEC_ERROR_BAD_DATABASE);
4605     return (SECFailure);
4606 }
4607 
4608 PRBool
nsslowcert_needDBVerify(NSSLOWCERTCertDBHandle * handle)4609 nsslowcert_needDBVerify(NSSLOWCERTCertDBHandle *handle)
4610 {
4611     if (!handle)
4612         return PR_FALSE;
4613     return handle->dbVerify;
4614 }
4615 
4616 void
nsslowcert_setDBVerify(NSSLOWCERTCertDBHandle * handle,PRBool value)4617 nsslowcert_setDBVerify(NSSLOWCERTCertDBHandle *handle, PRBool value)
4618 {
4619     handle->dbVerify = value;
4620 }
4621 
4622 /*
4623  * Lookup a certificate in the databases.
4624  */
4625 static NSSLOWCERTCertificate *
FindCertByKey(NSSLOWCERTCertDBHandle * handle,const SECItem * certKey,PRBool lockdb)4626 FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
4627 {
4628     NSSLOWCERTCertificate *cert = NULL;
4629     certDBEntryCert *entry;
4630     PRBool locked = PR_FALSE;
4631 
4632     if (lockdb) {
4633         locked = PR_TRUE;
4634         nsslowcert_LockDB(handle);
4635     }
4636 
4637     /* find in perm database */
4638     entry = ReadDBCertEntry(handle, certKey);
4639 
4640     if (entry == NULL) {
4641         goto loser;
4642     }
4643 
4644     /* inherit entry */
4645     cert = DecodeACert(handle, entry);
4646 
4647 loser:
4648     if (cert == NULL) {
4649         if (entry) {
4650             DestroyDBEntry((certDBEntry *)entry);
4651         }
4652     }
4653 
4654     if (locked) {
4655         nsslowcert_UnlockDB(handle);
4656     }
4657 
4658     return (cert);
4659 }
4660 
4661 /*
4662  * Lookup a certificate in the databases.
4663  */
4664 static NSSLOWCERTTrust *
FindTrustByKey(NSSLOWCERTCertDBHandle * handle,const SECItem * certKey,PRBool lockdb)4665 FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
4666 {
4667     NSSLOWCERTTrust *trust = NULL;
4668     certDBEntryCert *entry;
4669     PRBool locked = PR_FALSE;
4670 
4671     if (lockdb) {
4672         locked = PR_TRUE;
4673         nsslowcert_LockDB(handle);
4674     }
4675 
4676     /* find in perm database */
4677     entry = ReadDBCertEntry(handle, certKey);
4678 
4679     if (entry == NULL) {
4680         goto loser;
4681     }
4682 
4683     if (!nsslowcert_hasTrust(&entry->trust)) {
4684         goto loser;
4685     }
4686 
4687     /* inherit entry */
4688     trust = DecodeTrustEntry(handle, entry, certKey);
4689 
4690 loser:
4691     if (trust == NULL) {
4692         if (entry) {
4693             DestroyDBEntry((certDBEntry *)entry);
4694         }
4695     }
4696 
4697     if (locked) {
4698         nsslowcert_UnlockDB(handle);
4699     }
4700 
4701     return (trust);
4702 }
4703 
4704 /*
4705  * Lookup a certificate in the databases without locking
4706  */
4707 NSSLOWCERTCertificate *
nsslowcert_FindCertByKey(NSSLOWCERTCertDBHandle * handle,const SECItem * certKey)4708 nsslowcert_FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
4709 {
4710     return (FindCertByKey(handle, certKey, PR_FALSE));
4711 }
4712 
4713 /*
4714  * Lookup a trust object in the databases without locking
4715  */
4716 NSSLOWCERTTrust *
nsslowcert_FindTrustByKey(NSSLOWCERTCertDBHandle * handle,const SECItem * certKey)4717 nsslowcert_FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
4718 {
4719     return (FindTrustByKey(handle, certKey, PR_FALSE));
4720 }
4721 
4722 /*
4723  * Generate a key from an issuerAndSerialNumber, and find the
4724  * associated cert in the database.
4725  */
4726 NSSLOWCERTCertificate *
nsslowcert_FindCertByIssuerAndSN(NSSLOWCERTCertDBHandle * handle,NSSLOWCERTIssuerAndSN * issuerAndSN)4727 nsslowcert_FindCertByIssuerAndSN(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTIssuerAndSN *issuerAndSN)
4728 {
4729     SECItem certKey;
4730     SECItem *sn = &issuerAndSN->serialNumber;
4731     SECItem *issuer = &issuerAndSN->derIssuer;
4732     NSSLOWCERTCertificate *cert;
4733     int data_len = sn->len;
4734     int index = 0;
4735 
4736     /* automatically detect DER encoded serial numbers and remove the der
4737      * encoding since the database expects unencoded data.
4738      * if it's DER encoded, there must be at least 3 bytes, tag, len, data */
4739     if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
4740         /* remove the der encoding of the serial number before generating the
4741          * key.. */
4742         int data_left = sn->len - 2;
4743         data_len = sn->data[1];
4744         index = 2;
4745 
4746         /* extended length ? (not very likely for a serial number) */
4747         if (data_len & 0x80) {
4748             int len_count = data_len & 0x7f;
4749 
4750             data_len = 0;
4751             data_left -= len_count;
4752             if (data_left > 0) {
4753                 while (len_count--) {
4754                     data_len = (data_len << 8) | sn->data[index++];
4755                 }
4756             }
4757         }
4758         /* XXX leaving any leading zeros on the serial number for backwards
4759          * compatibility
4760          */
4761         /* not a valid der, must be just an unlucky serial number value */
4762         if (data_len != data_left) {
4763             data_len = sn->len;
4764             index = 0;
4765         }
4766     }
4767 
4768     certKey.type = 0;
4769     certKey.data = (unsigned char *)PORT_Alloc(sn->len + issuer->len);
4770     certKey.len = data_len + issuer->len;
4771 
4772     if (certKey.data == NULL) {
4773         return (0);
4774     }
4775 
4776     /* first try the serial number as hand-decoded above*/
4777     /* copy the serialNumber */
4778     PORT_Memcpy(certKey.data, &sn->data[index], data_len);
4779 
4780     /* copy the issuer */
4781     PORT_Memcpy(&certKey.data[data_len], issuer->data, issuer->len);
4782 
4783     cert = nsslowcert_FindCertByKey(handle, &certKey);
4784     if (cert) {
4785         PORT_Free(certKey.data);
4786         return (cert);
4787     }
4788 
4789     /* didn't find it, try by der encoded serial number */
4790     /* copy the serialNumber */
4791     PORT_Memcpy(certKey.data, sn->data, sn->len);
4792 
4793     /* copy the issuer */
4794     PORT_Memcpy(&certKey.data[sn->len], issuer->data, issuer->len);
4795     certKey.len = sn->len + issuer->len;
4796 
4797     cert = nsslowcert_FindCertByKey(handle, &certKey);
4798 
4799     PORT_Free(certKey.data);
4800 
4801     return (cert);
4802 }
4803 
4804 /*
4805  * Generate a key from an issuerAndSerialNumber, and find the
4806  * associated cert in the database.
4807  */
4808 NSSLOWCERTTrust *
nsslowcert_FindTrustByIssuerAndSN(NSSLOWCERTCertDBHandle * handle,NSSLOWCERTIssuerAndSN * issuerAndSN)4809 nsslowcert_FindTrustByIssuerAndSN(NSSLOWCERTCertDBHandle *handle,
4810                                   NSSLOWCERTIssuerAndSN *issuerAndSN)
4811 {
4812     SECItem certKey;
4813     SECItem *sn = &issuerAndSN->serialNumber;
4814     SECItem *issuer = &issuerAndSN->derIssuer;
4815     NSSLOWCERTTrust *trust;
4816     unsigned char keyBuf[512];
4817     int data_len = sn->len;
4818     int index = 0;
4819     int len;
4820 
4821     /* automatically detect DER encoded serial numbers and remove the der
4822      * encoding since the database expects unencoded data.
4823      * if it's DER encoded, there must be at least 3 bytes, tag, len, data */
4824     if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
4825         /* remove the der encoding of the serial number before generating the
4826          * key.. */
4827         int data_left = sn->len - 2;
4828         data_len = sn->data[1];
4829         index = 2;
4830 
4831         /* extended length ? (not very likely for a serial number) */
4832         if (data_len & 0x80) {
4833             int len_count = data_len & 0x7f;
4834 
4835             data_len = 0;
4836             data_left -= len_count;
4837             if (data_left > 0) {
4838                 while (len_count--) {
4839                     data_len = (data_len << 8) | sn->data[index++];
4840                 }
4841             }
4842         }
4843         /* XXX leaving any leading zeros on the serial number for backwards
4844          * compatibility
4845          */
4846         /* not a valid der, must be just an unlucky serial number value */
4847         if (data_len != data_left) {
4848             data_len = sn->len;
4849             index = 0;
4850         }
4851     }
4852 
4853     certKey.type = 0;
4854     certKey.len = data_len + issuer->len;
4855     len = sn->len + issuer->len;
4856     if (len > sizeof(keyBuf)) {
4857         certKey.data = (unsigned char *)PORT_Alloc(len);
4858     } else {
4859         certKey.data = keyBuf;
4860     }
4861 
4862     if (certKey.data == NULL) {
4863         return (0);
4864     }
4865 
4866     /* first try the serial number as hand-decoded above*/
4867     /* copy the serialNumber */
4868     PORT_Memcpy(certKey.data, &sn->data[index], data_len);
4869 
4870     /* copy the issuer */
4871     PORT_Memcpy(&certKey.data[data_len], issuer->data, issuer->len);
4872 
4873     trust = nsslowcert_FindTrustByKey(handle, &certKey);
4874     if (trust) {
4875         pkcs11_freeStaticData(certKey.data, keyBuf);
4876         return (trust);
4877     }
4878 
4879     if (index == 0) {
4880         pkcs11_freeStaticData(certKey.data, keyBuf);
4881         return NULL;
4882     }
4883 
4884     /* didn't find it, try by der encoded serial number */
4885     /* copy the serialNumber */
4886     PORT_Memcpy(certKey.data, sn->data, sn->len);
4887 
4888     /* copy the issuer */
4889     PORT_Memcpy(&certKey.data[sn->len], issuer->data, issuer->len);
4890     certKey.len = sn->len + issuer->len;
4891 
4892     trust = nsslowcert_FindTrustByKey(handle, &certKey);
4893 
4894     pkcs11_freeStaticData(certKey.data, keyBuf);
4895 
4896     return (trust);
4897 }
4898 
4899 /*
4900  * look for the given DER certificate in the database
4901  */
4902 NSSLOWCERTCertificate *
nsslowcert_FindCertByDERCert(NSSLOWCERTCertDBHandle * handle,SECItem * derCert)4903 nsslowcert_FindCertByDERCert(NSSLOWCERTCertDBHandle *handle, SECItem *derCert)
4904 {
4905     PLArenaPool *arena;
4906     SECItem certKey;
4907     SECStatus rv;
4908     NSSLOWCERTCertificate *cert = NULL;
4909 
4910     /* create a scratch arena */
4911     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
4912     if (arena == NULL) {
4913         return (NULL);
4914     }
4915 
4916     /* extract the database key from the cert */
4917     rv = nsslowcert_KeyFromDERCert(arena, derCert, &certKey);
4918     if (rv != SECSuccess) {
4919         goto loser;
4920     }
4921 
4922     /* find the certificate */
4923     cert = nsslowcert_FindCertByKey(handle, &certKey);
4924 
4925 loser:
4926     PORT_FreeArena(arena, PR_FALSE);
4927     return (cert);
4928 }
4929 
4930 static void
DestroyCertificate(NSSLOWCERTCertificate * cert,PRBool lockdb)4931 DestroyCertificate(NSSLOWCERTCertificate *cert, PRBool lockdb)
4932 {
4933     int refCount;
4934     NSSLOWCERTCertDBHandle *handle;
4935 
4936     if (cert) {
4937 
4938         handle = cert->dbhandle;
4939 
4940         /*
4941          * handle may be NULL, for example if the cert was created with
4942          * nsslowcert_DecodeDERCertificate.
4943          */
4944         if (lockdb && handle) {
4945             nsslowcert_LockDB(handle);
4946         }
4947 
4948         nsslowcert_LockCertRefCount(cert);
4949         PORT_Assert(cert->referenceCount > 0);
4950         refCount = --cert->referenceCount;
4951         nsslowcert_UnlockCertRefCount(cert);
4952 
4953         if (refCount == 0) {
4954             certDBEntryCert *entry = cert->dbEntry;
4955 
4956             if (entry) {
4957                 DestroyDBEntry((certDBEntry *)entry);
4958             }
4959 
4960             pkcs11_freeNickname(cert->nickname, cert->nicknameSpace);
4961             pkcs11_freeNickname(cert->emailAddr, cert->emailAddrSpace);
4962             pkcs11_freeStaticData(cert->certKey.data, cert->certKeySpace);
4963             cert->certKey.data = NULL;
4964             cert->nickname = NULL;
4965 
4966             /* zero cert before freeing. Any stale references to this cert
4967              * after this point will probably cause an exception.  */
4968             PORT_Memset(cert, 0, sizeof *cert);
4969 
4970             /* use reflock to protect the free list */
4971             nsslowcert_LockFreeList();
4972             if (certListCount > MAX_CERT_LIST_COUNT) {
4973                 PORT_Free(cert);
4974             } else {
4975                 certListCount++;
4976                 cert->next = certListHead;
4977                 certListHead = cert;
4978             }
4979             nsslowcert_UnlockFreeList();
4980             cert = NULL;
4981         }
4982         if (lockdb && handle) {
4983             nsslowcert_UnlockDB(handle);
4984         }
4985     }
4986 
4987     return;
4988 }
4989 
4990 NSSLOWCERTCertificate *
nsslowcert_CreateCert(void)4991 nsslowcert_CreateCert(void)
4992 {
4993     NSSLOWCERTCertificate *cert;
4994     nsslowcert_LockFreeList();
4995     cert = certListHead;
4996     if (cert) {
4997         certListHead = cert->next;
4998         certListCount--;
4999     }
5000     PORT_Assert(certListCount >= 0);
5001     nsslowcert_UnlockFreeList();
5002     if (cert) {
5003         return cert;
5004     }
5005     return PORT_ZNew(NSSLOWCERTCertificate);
5006 }
5007 
5008 static void
DestroyCertFreeList(void)5009 DestroyCertFreeList(void)
5010 {
5011     NSSLOWCERTCertificate *cert;
5012 
5013     nsslowcert_LockFreeList();
5014     while (NULL != (cert = certListHead)) {
5015         certListCount--;
5016         certListHead = cert->next;
5017         PORT_Free(cert);
5018     }
5019     PORT_Assert(!certListCount);
5020     certListCount = 0;
5021     nsslowcert_UnlockFreeList();
5022 }
5023 
5024 void
nsslowcert_DestroyTrust(NSSLOWCERTTrust * trust)5025 nsslowcert_DestroyTrust(NSSLOWCERTTrust *trust)
5026 {
5027     certDBEntryCert *entry = trust->dbEntry;
5028 
5029     if (entry) {
5030         DestroyDBEntry((certDBEntry *)entry);
5031     }
5032     pkcs11_freeStaticData(trust->dbKey.data, trust->dbKeySpace);
5033     PORT_Memset(trust, 0, sizeof(*trust));
5034 
5035     nsslowcert_LockFreeList();
5036     if (trustListCount > MAX_TRUST_LIST_COUNT) {
5037         PORT_Free(trust);
5038     } else {
5039         trustListCount++;
5040         trust->next = trustListHead;
5041         trustListHead = trust;
5042     }
5043     nsslowcert_UnlockFreeList();
5044 
5045     return;
5046 }
5047 
5048 void
nsslowcert_DestroyCertificate(NSSLOWCERTCertificate * cert)5049 nsslowcert_DestroyCertificate(NSSLOWCERTCertificate *cert)
5050 {
5051     DestroyCertificate(cert, PR_TRUE);
5052     return;
5053 }
5054 
5055 static void
nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate * cert)5056 nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert)
5057 {
5058     DestroyCertificate(cert, PR_FALSE);
5059     return;
5060 }
5061 
5062 /*
5063  * Lookup a CRL in the databases. We mirror the same fast caching data base
5064  *  caching stuff used by certificates....?
5065  */
5066 certDBEntryRevocation *
nsslowcert_FindCrlByKey(NSSLOWCERTCertDBHandle * handle,SECItem * crlKey,PRBool isKRL)5067 nsslowcert_FindCrlByKey(NSSLOWCERTCertDBHandle *handle,
5068                         SECItem *crlKey, PRBool isKRL)
5069 {
5070     SECItem keyitem;
5071     SECStatus rv;
5072     PLArenaPool *arena = NULL;
5073     certDBEntryRevocation *entry = NULL;
5074     certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
5075                                     : certDBEntryTypeRevocation;
5076 
5077     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
5078     if (arena == NULL) {
5079         goto loser;
5080     }
5081 
5082     rv = EncodeDBGenericKey(crlKey, arena, &keyitem, crlType);
5083     if (rv != SECSuccess) {
5084         goto loser;
5085     }
5086 
5087     /* find in perm database */
5088     entry = ReadDBCrlEntry(handle, crlKey, crlType);
5089 
5090     if (entry == NULL) {
5091         goto loser;
5092     }
5093 
5094 loser:
5095     if (arena) {
5096         PORT_FreeArena(arena, PR_FALSE);
5097     }
5098 
5099     return entry;
5100 }
5101 
5102 /*
5103  * replace the existing URL in the data base with a new one
5104  */
5105 static SECStatus
nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle * handle,SECItem * derCrl,SECItem * crlKey,char * url,PRBool isKRL)5106 nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
5107                      SECItem *crlKey, char *url, PRBool isKRL)
5108 {
5109     SECStatus rv = SECFailure;
5110     certDBEntryRevocation *entry = NULL;
5111     certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
5112                                     : certDBEntryTypeRevocation;
5113     DeleteDBCrlEntry(handle, crlKey, crlType);
5114 
5115     /* Write the new entry into the data base */
5116     entry = NewDBCrlEntry(derCrl, url, crlType, 0);
5117     if (entry == NULL)
5118         goto done;
5119 
5120     rv = WriteDBCrlEntry(handle, entry, crlKey);
5121     if (rv != SECSuccess)
5122         goto done;
5123 
5124 done:
5125     if (entry) {
5126         DestroyDBEntry((certDBEntry *)entry);
5127     }
5128     return rv;
5129 }
5130 
5131 SECStatus
nsslowcert_AddCrl(NSSLOWCERTCertDBHandle * handle,SECItem * derCrl,SECItem * crlKey,char * url,PRBool isKRL)5132 nsslowcert_AddCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
5133                   SECItem *crlKey, char *url, PRBool isKRL)
5134 {
5135     SECStatus rv;
5136 
5137     rv = nsslowcert_UpdateCrl(handle, derCrl, crlKey, url, isKRL);
5138 
5139     return rv;
5140 }
5141 
5142 SECStatus
nsslowcert_DeletePermCRL(NSSLOWCERTCertDBHandle * handle,const SECItem * derName,PRBool isKRL)5143 nsslowcert_DeletePermCRL(NSSLOWCERTCertDBHandle *handle, const SECItem *derName,
5144                          PRBool isKRL)
5145 {
5146     SECStatus rv;
5147     certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
5148                                     : certDBEntryTypeRevocation;
5149 
5150     rv = DeleteDBCrlEntry(handle, derName, crlType);
5151     if (rv != SECSuccess)
5152         goto done;
5153 
5154 done:
5155     return rv;
5156 }
5157 
5158 PRBool
nsslowcert_hasTrust(NSSLOWCERTCertTrust * trust)5159 nsslowcert_hasTrust(NSSLOWCERTCertTrust *trust)
5160 {
5161     if (trust == NULL) {
5162         return PR_FALSE;
5163     }
5164     return !((trust->sslFlags & CERTDB_TRUSTED_UNKNOWN) &&
5165              (trust->emailFlags & CERTDB_TRUSTED_UNKNOWN) &&
5166              (trust->objectSigningFlags & CERTDB_TRUSTED_UNKNOWN));
5167 }
5168 
5169 /*
5170  * This function has the logic that decides if another person's cert and
5171  * email profile from an S/MIME message should be saved.  It can deal with
5172  * the case when there is no profile.
5173  */
5174 static SECStatus
nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle * dbhandle,char * emailAddr,SECItem * derSubject,SECItem * emailProfile,SECItem * profileTime)5175 nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
5176                               char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
5177                               SECItem *profileTime)
5178 {
5179     certDBEntrySMime *entry = NULL;
5180     SECStatus rv = SECFailure;
5181     ;
5182 
5183     /* find our existing entry */
5184     entry = nsslowcert_ReadDBSMimeEntry(dbhandle, emailAddr);
5185 
5186     if (entry) {
5187         /* keep our old db entry consistant for old applications. */
5188         if (!SECITEM_ItemsAreEqual(derSubject, &entry->subjectName)) {
5189             nsslowcert_UpdateSubjectEmailAddr(dbhandle, &entry->subjectName,
5190                                               emailAddr, nsslowcert_remove);
5191         }
5192         DestroyDBEntry((certDBEntry *)entry);
5193         entry = NULL;
5194     }
5195 
5196     /* now save the entry */
5197     entry = NewDBSMimeEntry(emailAddr, derSubject, emailProfile,
5198                             profileTime, 0);
5199     if (entry == NULL) {
5200         rv = SECFailure;
5201         goto loser;
5202     }
5203 
5204     nsslowcert_LockDB(dbhandle);
5205 
5206     rv = DeleteDBSMimeEntry(dbhandle, emailAddr);
5207     /* if delete fails, try to write new entry anyway... */
5208 
5209     /* link subject entry back here */
5210     rv = nsslowcert_UpdateSubjectEmailAddr(dbhandle, derSubject, emailAddr,
5211                                            nsslowcert_add);
5212     if (rv != SECSuccess) {
5213         nsslowcert_UnlockDB(dbhandle);
5214         goto loser;
5215     }
5216 
5217     rv = WriteDBSMimeEntry(dbhandle, entry);
5218     if (rv != SECSuccess) {
5219         nsslowcert_UnlockDB(dbhandle);
5220         goto loser;
5221     }
5222 
5223     nsslowcert_UnlockDB(dbhandle);
5224 
5225     rv = SECSuccess;
5226 
5227 loser:
5228     if (entry) {
5229         DestroyDBEntry((certDBEntry *)entry);
5230     }
5231     return (rv);
5232 }
5233 
5234 SECStatus
nsslowcert_SaveSMimeProfile(NSSLOWCERTCertDBHandle * dbhandle,char * emailAddr,SECItem * derSubject,SECItem * emailProfile,SECItem * profileTime)5235 nsslowcert_SaveSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle, char *emailAddr,
5236                             SECItem *derSubject, SECItem *emailProfile, SECItem *profileTime)
5237 {
5238     SECStatus rv = SECFailure;
5239     ;
5240 
5241     rv = nsslowcert_UpdateSMimeProfile(dbhandle, emailAddr,
5242                                        derSubject, emailProfile, profileTime);
5243 
5244     return (rv);
5245 }
5246 
5247 void
nsslowcert_DestroyFreeLists(void)5248 nsslowcert_DestroyFreeLists(void)
5249 {
5250     if (freeListLock == NULL) {
5251         return;
5252     }
5253     DestroyCertEntryFreeList();
5254     DestroyTrustFreeList();
5255     DestroyCertFreeList();
5256     SKIP_AFTER_FORK(PZ_DestroyLock(freeListLock));
5257     freeListLock = NULL;
5258 }
5259 
5260 void
nsslowcert_DestroyGlobalLocks(void)5261 nsslowcert_DestroyGlobalLocks(void)
5262 {
5263     if (dbLock) {
5264         SKIP_AFTER_FORK(PZ_DestroyLock(dbLock));
5265         dbLock = NULL;
5266     }
5267     if (certRefCountLock) {
5268         SKIP_AFTER_FORK(PZ_DestroyLock(certRefCountLock));
5269         certRefCountLock = NULL;
5270     }
5271     if (certTrustLock) {
5272         SKIP_AFTER_FORK(PZ_DestroyLock(certTrustLock));
5273         certTrustLock = NULL;
5274     }
5275 }
5276 
5277 certDBEntry *
nsslowcert_DecodeAnyDBEntry(SECItem * dbData,const SECItem * dbKey,certDBEntryType entryType,void * pdata)5278 nsslowcert_DecodeAnyDBEntry(SECItem *dbData, const SECItem *dbKey,
5279                             certDBEntryType entryType, void *pdata)
5280 {
5281     PLArenaPool *arena = NULL;
5282     certDBEntry *entry;
5283     SECStatus rv;
5284     SECItem dbEntry;
5285 
5286     if ((dbData->len < SEC_DB_ENTRY_HEADER_LEN) || (dbKey->len == 0)) {
5287         PORT_SetError(SEC_ERROR_INVALID_ARGS);
5288         goto loser;
5289     }
5290     dbEntry.data = &dbData->data[SEC_DB_ENTRY_HEADER_LEN];
5291     dbEntry.len = dbData->len - SEC_DB_ENTRY_HEADER_LEN;
5292 
5293     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
5294     if (arena == NULL) {
5295         goto loser;
5296     }
5297     entry = PORT_ArenaZNew(arena, certDBEntry);
5298     if (!entry)
5299         goto loser;
5300 
5301     entry->common.version = (unsigned int)dbData->data[0];
5302     entry->common.flags = (unsigned int)dbData->data[2];
5303     entry->common.type = entryType;
5304     entry->common.arena = arena;
5305 
5306     switch (entryType) {
5307         case certDBEntryTypeContentVersion: /* This type appears to be unused */
5308         case certDBEntryTypeVersion:        /* This type has only the common hdr */
5309             rv = SECSuccess;
5310             break;
5311 
5312         case certDBEntryTypeSubject:
5313             rv = DecodeDBSubjectEntry(&entry->subject, &dbEntry, dbKey);
5314             break;
5315 
5316         case certDBEntryTypeNickname:
5317             rv = DecodeDBNicknameEntry(&entry->nickname, &dbEntry,
5318                                        (char *)dbKey->data);
5319             break;
5320 
5321         /* smime profiles need entries created after the certs have
5322          * been imported, loop over them in a second run */
5323         case certDBEntryTypeSMimeProfile:
5324             rv = DecodeDBSMimeEntry(&entry->smime, &dbEntry, (char *)dbKey->data);
5325             break;
5326 
5327         case certDBEntryTypeCert:
5328             rv = DecodeDBCertEntry(&entry->cert, &dbEntry);
5329             break;
5330 
5331         case certDBEntryTypeKeyRevocation:
5332         case certDBEntryTypeRevocation:
5333             rv = DecodeDBCrlEntry(&entry->revocation, &dbEntry);
5334             break;
5335 
5336         default:
5337             PORT_SetError(SEC_ERROR_INVALID_ARGS);
5338             rv = SECFailure;
5339     }
5340 
5341     if (rv == SECSuccess)
5342         return entry;
5343 
5344 loser:
5345     if (arena)
5346         PORT_FreeArena(arena, PR_FALSE);
5347     return NULL;
5348 }
5349