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