dbck.c
上传用户:lyxiangda
上传日期:2007-01-12
资源大小:3042k
文件大小:61k
源码类别:

CA认证

开发平台:

WINDOWS

  1. /*
  2.  * The contents of this file are subject to the Mozilla Public
  3.  * License Version 1.1 (the "License"); you may not use this file
  4.  * except in compliance with the License. You may obtain a copy of
  5.  * the License at http://www.mozilla.org/MPL/
  6.  * 
  7.  * Software distributed under the License is distributed on an "AS
  8.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9.  * implied. See the License for the specific language governing
  10.  * rights and limitations under the License.
  11.  * 
  12.  * The Original Code is the Netscape security libraries.
  13.  * 
  14.  * The Initial Developer of the Original Code is Netscape
  15.  * Communications Corporation.  Portions created by Netscape are 
  16.  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
  17.  * Rights Reserved.
  18.  * 
  19.  * Contributor(s):
  20.  * 
  21.  * Alternatively, the contents of this file may be used under the
  22.  * terms of the GNU General Public License Version 2 or later (the
  23.  * "GPL"), in which case the provisions of the GPL are applicable 
  24.  * instead of those above.  If you wish to allow use of your 
  25.  * version of this file only under the terms of the GPL and not to
  26.  * allow others to use your version of this file under the MPL,
  27.  * indicate your decision by deleting the provisions above and
  28.  * replace them with the notice and other provisions required by
  29.  * the GPL.  If you do not delete the provisions above, a recipient
  30.  * may use your version of this file under either the MPL or the
  31.  * GPL.
  32.  */
  33. /*
  34. ** dbck.c
  35. **
  36. ** utility for fixing corrupt cert databases
  37. **
  38. */
  39. #include <stdio.h>
  40. #include <string.h>
  41. #include "secutil.h"
  42. #include "cdbhdl.h"
  43. #include "certdb.h"
  44. #include "cert.h"
  45. #include "nspr.h"
  46. #include "prtypes.h"
  47. #include "prtime.h"
  48. #include "prlong.h"
  49. static char *progName;
  50. /* placeholders for pointer error types */
  51. static void *WrongEntry;
  52. static void *NoNickname;
  53. static void *NoSMime;
  54. enum {
  55.     GOBOTH = 0,
  56.     GORIGHT,
  57.     GOLEFT
  58. };
  59. typedef struct
  60. {
  61.     PRBool verbose;
  62.     PRBool dograph;
  63.     PRFileDesc *out;
  64.     PRFileDesc *graphfile;
  65.     int dbErrors[10];
  66. } dbDebugInfo;
  67. /*
  68.  * A list node for a cert db entry.  The index is a unique identifier
  69.  * to use for creating generic maps of a db.  This struct handles
  70.  * the cert, nickname, and smime db entry types, as all three have a
  71.  * single handle to a subject entry.
  72.  * This structure is pointed to by certDBEntryListNode->appData.
  73.  */
  74. typedef struct 
  75. {
  76.     PRArenaPool *arena;
  77.     int index;
  78.     certDBEntryListNode *pSubject;
  79. } certDBEntryMap;
  80. /*
  81.  * Subject entry is special case, it has bidirectional handles.  One
  82.  * subject entry can point to several certs (using the same DN), and
  83.  * a nickname and/or smime entry.
  84.  * This structure is pointed to by certDBEntryListNode->appData.
  85.  */
  86. typedef struct
  87. {
  88.     PRArenaPool *arena;
  89.     int index;
  90.     int numCerts;
  91.     certDBEntryListNode **pCerts;
  92.     certDBEntryListNode *pNickname;
  93.     certDBEntryListNode *pSMime;
  94. } certDBSubjectEntryMap;
  95. /*
  96.  * A map of a certdb.
  97.  */
  98. typedef struct
  99. {
  100.     int numCerts;
  101.     int numSubjects;
  102.     int numNicknames;
  103.     int numSMime;
  104.     certDBEntryListNode certs;      /* pointer to head of cert list */
  105.     certDBEntryListNode subjects;   /* pointer to head of subject list */
  106.     certDBEntryListNode nicknames;  /* pointer to head of nickname list */
  107.     certDBEntryListNode smime;      /* pointer to head of smime list */
  108. } certDBArray;
  109. /* Cast list to the base element, a certDBEntryListNode. */
  110. #define LISTNODE_CAST(node) 
  111.     ((certDBEntryListNode *)(node))
  112. static void 
  113. Usage(char *progName)
  114. {
  115. #define FPS fprintf(stderr, 
  116.     FPS "Type %s -H for more detailed descriptionsn", progName);
  117.     FPS "Usage:  %s -D [-d certdir] [-i dbname] [-m] [-v  [-f dumpfile]]n", 
  118. progName);
  119.     FPS "        %s -R -o newdbname [-d certdir] [-i dbname] [-aprsx] [-v [-f dumpfile]]n", 
  120. progName);
  121.     exit(-1);
  122. }
  123. static void
  124. LongUsage(char *progName)
  125. {
  126.     FPS "%-15s Display this help message.n",
  127. "-H");
  128.     FPS "%-15s Dump analysis.  No changes will be made to the database.n",
  129. "-D");
  130.     FPS "%-15s Cert database directory (default is ~/.netscape)n",
  131. "   -d certdir");
  132.     FPS "%-15s Input cert database name (default is cert7.db)n",
  133. "   -i dbname");
  134.     FPS "%-15s Mail a graph of the database to certdb@netscape.com.n",
  135. "   -m");
  136.     FPS "%-15s This will produce an index graph of your cert db and sendn",
  137. "");
  138.     FPS "%-15s it to Netscape for analysis.  Personal info will be removed.n",
  139. "");
  140.     FPS "%-15s Verbose mode.  Dumps the entire contents of your cert7.db.n",
  141. "   -v");
  142.     FPS "%-15s File to dump verbose output into.n",
  143. "   -f dumpfile");
  144.     FPS "%-15s Repair the database.  The program will look for brokenn",
  145. "-R");
  146.     FPS "%-15s dependencies between subject entries and certificates,n",
  147.         "");
  148.     FPS "%-15s between nickname entries and subjects, and between SMIMEn",
  149.         "");
  150.     FPS "%-15s profiles and subjects.  Any duplicate entries will ben",
  151.         "");
  152.     FPS "%-15s removed, any missing entries will be created.n",
  153.         "");
  154.     FPS "%-15s File to store new database in (default is new_cert7.db)n",
  155. "   -o newdbname");
  156.     FPS "%-15s Cert database directory (default is ~/.netscape)n",
  157. "   -d certdir");
  158.     FPS "%-15s Input cert database name (default is cert7.db)n",
  159. "   -i dbname");
  160.     FPS "%-15s Prompt before removing any certificates.n",
  161.         "   -p");
  162.     FPS "%-15s Keep all possible certificates.  Only remove certificatesn",
  163. "   -a");
  164.     FPS "%-15s which prevent creation of a consistent database.  Thus anyn",
  165. "");
  166.     FPS "%-15s expired or redundant entries will be kept.n",
  167. "");
  168.     FPS "%-15s Keep redundant nickname/email entries.  It is possiblen",
  169. "   -r");
  170.     FPS "%-15s only one such entry will be usable.n",
  171. "");
  172.     FPS "%-15s Don't require an S/MIME profile in order to keep an S/MIMEn",
  173. "   -s");
  174.     FPS "%-15s cert.  An empty profile will be created.n",
  175. "");
  176.     FPS "%-15s Keep expired certificates.n",
  177. "   -x");
  178.     FPS "%-15s Verbose mode - report all activity while recovering db.n",
  179. "   -v");
  180.     FPS "%-15s File to dump verbose output into.n",
  181. "   -f dumpfile");
  182.     FPS "n");
  183.     exit(-1);
  184. #undef FPS
  185. }
  186. /*******************************************************************
  187.  *
  188.  *  Functions for dbck.
  189.  *
  190.  ******************************************************************/
  191. void
  192. printHexString(PRFileDesc *out, SECItem *hexval)
  193. {
  194.     int i;
  195.     for (i = 0; i < hexval->len; i++) {
  196. if (i != hexval->len - 1) {
  197.     PR_fprintf(out, "%02x:", hexval->data[i]);
  198. } else {
  199.     PR_fprintf(out, "%02x", hexval->data[i]);
  200. }
  201.     }
  202.     PR_fprintf(out, "n");
  203. }
  204. typedef enum {
  205. /* 0*/ NoSubjectForCert = 0,
  206. /* 1*/ SubjectHasNoKeyForCert,
  207. /* 2*/ NoNicknameOrSMimeForSubject,
  208. /* 3*/ WrongNicknameForSubject,
  209. /* 4*/ NoNicknameEntry,
  210. /* 5*/ WrongSMimeForSubject,
  211. /* 6*/ NoSMimeEntry,
  212. /* 7*/ NoSubjectForNickname,
  213. /* 8*/ NoSubjectForSMime,
  214. /* 9*/ NicknameAndSMimeEntry
  215. } dbErrorType;
  216. static char *dbErrorString[] = {
  217. /* 0*/ "<CERT ENTRY>nDid not find a subject entry for this certificate.",
  218. /* 1*/ "<SUBJECT ENTRY>nSubject has certKey which is not in db.",
  219. /* 2*/ "<SUBJECT ENTRY>nSubject does not have a nickname or email address.",
  220. /* 3*/ "<SUBJECT ENTRY>nUsing this subject's nickname, found a nickname entry for a different subject.",
  221. /* 4*/ "<SUBJECT ENTRY>nDid not find a nickname entry for this subject.",
  222. /* 5*/ "<SUBJECT ENTRY>nUsing this subject's email, found an S/MIME entry for a different subject.",
  223. /* 6*/ "<SUBJECT ENTRY>nDid not find an S/MIME entry for this subject.",
  224. /* 7*/ "<NICKNAME ENTRY>nDid not find a subject entry for this nickname.",
  225. /* 8*/ "<S/MIME ENTRY>nDid not find a subject entry for this S/MIME profile.",
  226. };
  227. SECStatus
  228. dumpCertificate(CERTCertificate *cert, int num, PRFileDesc *outfile)
  229. {
  230.     int userCert = 0;
  231.     CERTCertTrust *trust = cert->trust;
  232.     userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
  233.                (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
  234.                (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
  235.     if (num >= 0) {
  236. PR_fprintf(outfile, "Certificate: %3dn", num);
  237.     } else {
  238. PR_fprintf(outfile, "Certificate:n");
  239.     }
  240.     PR_fprintf(outfile, "----------------n");
  241.     if (userCert)
  242. PR_fprintf(outfile, "(User Cert)n");
  243.     PR_fprintf(outfile, "## SUBJECT:  %sn", cert->subjectName);
  244.     PR_fprintf(outfile, "## ISSUER:  %sn", cert->issuerName);
  245.     PR_fprintf(outfile, "## SERIAL NUMBER:  ");
  246.     printHexString(outfile, &cert->serialNumber);
  247.     {  /*  XXX should be separate function.  */
  248. int64 timeBefore, timeAfter;
  249. PRExplodedTime beforePrintable, afterPrintable;
  250. char *beforestr, *afterstr;
  251. DER_UTCTimeToTime(&timeBefore, &cert->validity.notBefore);
  252. DER_UTCTimeToTime(&timeAfter, &cert->validity.notAfter);
  253. PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable);
  254. PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable);
  255. beforestr = PORT_Alloc(100);
  256. afterstr = PORT_Alloc(100);
  257. PR_FormatTime(beforestr, 100, "%a %b %d %H:%M:%S %Y", &beforePrintable);
  258. PR_FormatTime(afterstr, 100, "%a %b %d %H:%M:%S %Y", &afterPrintable);
  259. PR_fprintf(outfile, "## VALIDITY:  %s to %sn", beforestr, afterstr);
  260.     }
  261.     PR_fprintf(outfile, "n");
  262.     return SECSuccess;
  263. }
  264. SECStatus
  265. dumpCertEntry(certDBEntryCert *entry, int num, PRFileDesc *outfile)
  266. {
  267.     CERTCertificate *cert;
  268.     cert = CERT_DecodeDERCertificate(&entry->derCert, PR_FALSE, NULL);
  269.     if (!cert) {
  270. fprintf(stderr, "Failed to decode certificate.n");
  271. return SECFailure;
  272.     }
  273.     cert->trust = &entry->trust;
  274.     dumpCertificate(cert, num, outfile);
  275.     CERT_DestroyCertificate(cert);
  276.     return SECSuccess;
  277. }
  278. SECStatus
  279. dumpSubjectEntry(certDBEntrySubject *entry, int num, PRFileDesc *outfile)
  280. {
  281.     char *subjectName;
  282.     subjectName = CERT_DerNameToAscii(&entry->derSubject);
  283.     PR_fprintf(outfile, "Subject: %3dn", num);
  284.     PR_fprintf(outfile, "------------n");
  285.     PR_fprintf(outfile, "## %sn", subjectName);
  286.     if (entry->nickname)
  287. PR_fprintf(outfile, "## Subject nickname:  %sn", entry->nickname);
  288.     if (entry->emailAddr)
  289. PR_fprintf(outfile, "## Subject email address:  %sn", 
  290.            entry->emailAddr);
  291.     PR_fprintf(outfile, "## This subject has %d cert(s).n", entry->ncerts);
  292.     PR_fprintf(outfile, "n");
  293.     PORT_Free(subjectName);
  294.     return SECSuccess;
  295. }
  296. SECStatus
  297. dumpNicknameEntry(certDBEntryNickname *entry, int num, PRFileDesc *outfile)
  298. {
  299.     PR_fprintf(outfile, "Nickname: %3dn", num);
  300.     PR_fprintf(outfile, "-------------n");
  301.     PR_fprintf(outfile, "##  "%s"nn", entry->nickname);
  302.     return SECSuccess;
  303. }
  304. SECStatus
  305. dumpSMimeEntry(certDBEntrySMime *entry, int num, PRFileDesc *outfile)
  306. {
  307.     PR_fprintf(outfile, "S/MIME Profile: %3dn", num);
  308.     PR_fprintf(outfile, "-------------------n");
  309.     PR_fprintf(outfile, "##  "%s"n", entry->emailAddr);
  310.     PR_fprintf(outfile, "##  OPTIONS:  ");
  311.     printHexString(outfile, &entry->smimeOptions);
  312.     PR_fprintf(outfile, "##  TIMESTAMP:  ");
  313.     printHexString(outfile, &entry->optionsDate);
  314.     PR_fprintf(outfile, "n");
  315.     return SECSuccess;
  316. }
  317. SECStatus
  318. mapCertEntries(certDBArray *dbArray)
  319. {
  320.     certDBEntryCert *certEntry;
  321.     certDBEntrySubject *subjectEntry;
  322.     certDBEntryListNode *certNode, *subjNode;
  323.     certDBSubjectEntryMap *smap;
  324.     certDBEntryMap *map;
  325.     PRArenaPool *tmparena;
  326.     SECItem derSubject;
  327.     SECItem certKey;
  328.     PRCList *cElem, *sElem;
  329.     int i;
  330.     /* Arena for decoded entries */
  331.     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  332.     if (tmparena == NULL) {
  333. PORT_SetError(SEC_ERROR_NO_MEMORY);
  334. return SECFailure;
  335.     }
  336.     /* Iterate over cert entries and map them to subject entries. 
  337.      * NOTE: mapSubjectEntries must be called first to alloc memory
  338.      * for array of subject->cert map.
  339.      */
  340.     for (cElem = PR_LIST_HEAD(&dbArray->certs.link); 
  341.          cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
  342. certNode = LISTNODE_CAST(cElem);
  343. certEntry = (certDBEntryCert *)&certNode->entry;
  344. map = (certDBEntryMap *)certNode->appData;
  345. CERT_NameFromDERCert(&certEntry->derCert, &derSubject);
  346. CERT_KeyFromDERCert(tmparena, &certEntry->derCert, &certKey);
  347. /*  Loop over found subjects for cert's DN.  */
  348. for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
  349.      sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
  350.     subjNode = LISTNODE_CAST(sElem);
  351.     subjectEntry = (certDBEntrySubject *)&subjNode->entry;
  352.     if (SECITEM_ItemsAreEqual(&derSubject, &subjectEntry->derSubject)) {
  353. /*  Found matching subject name, create link.  */
  354. map->pSubject = subjNode;
  355. /*  Make sure subject entry has cert's key.  */
  356. for (i=0; i<subjectEntry->ncerts; i++) {
  357.     if (SECITEM_ItemsAreEqual(&certKey,
  358.                               &subjectEntry->certKeys[i])) {
  359. /*  Found matching cert key.  */
  360. smap = (certDBSubjectEntryMap *)subjNode->appData;
  361. smap->pCerts[i] = certNode;
  362. break;
  363.     }
  364. }
  365.     }
  366. }
  367.     }
  368.     PORT_FreeArena(tmparena, PR_FALSE);
  369.     return SECSuccess;
  370. }
  371. SECStatus
  372. mapSubjectEntries(certDBArray *dbArray)
  373. {
  374.     certDBEntrySubject *subjectEntry;
  375.     certDBEntryNickname *nicknameEntry;
  376.     certDBEntrySMime *smimeEntry;
  377.     certDBEntryListNode *subjNode, *nickNode, *smimeNode;
  378.     certDBSubjectEntryMap *subjMap;
  379.     certDBEntryMap *nickMap, *smimeMap;
  380.     PRCList *sElem, *nElem, *mElem;
  381.     for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
  382.          sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
  383. /* Iterate over subject entries and map subjects to nickname
  384.  * and smime entries.  The cert<->subject map will be handled
  385.  * by a subsequent call to mapCertEntries.
  386.  */
  387. subjNode = LISTNODE_CAST(sElem);
  388. subjectEntry = (certDBEntrySubject *)&subjNode->entry;
  389. subjMap = (certDBSubjectEntryMap *)subjNode->appData;
  390. /* need to alloc memory here for array of matching certs. */
  391. subjMap->pCerts = PORT_ArenaAlloc(subjMap->arena, 
  392.                                   subjectEntry->ncerts*sizeof(int));
  393. subjMap->numCerts = subjectEntry->ncerts;
  394. if (subjectEntry->nickname) {
  395.     /* Subject should have a nickname entry, so create a link. */
  396.     for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
  397.          nElem != &dbArray->nicknames.link; 
  398.          nElem = PR_NEXT_LINK(nElem)) {
  399. /*  Look for subject's nickname in nickname entries.  */
  400. nickNode = LISTNODE_CAST(nElem);
  401. nicknameEntry = (certDBEntryNickname *)&nickNode->entry;
  402. nickMap = (certDBEntryMap *)nickNode->appData;
  403. if (PL_strcmp(subjectEntry->nickname, 
  404.               nicknameEntry->nickname) == 0) {
  405.     /*  Found a nickname entry for subject's nickname.  */
  406.     if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
  407.                               &nicknameEntry->subjectName)) {
  408. /*  Nickname and subject match.  */
  409. subjMap->pNickname = nickNode;
  410. nickMap->pSubject = subjNode;
  411.     } else {
  412. /*  Nickname entry found is for diff. subject.  */
  413. subjMap->pNickname = WrongEntry;
  414.     }
  415. }
  416.     }
  417. } else {
  418.     subjMap->pNickname = NoNickname;
  419. }
  420. if (subjectEntry->emailAddr) {
  421.     /* Subject should have an smime entry, so create a link. */
  422.     for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
  423.          mElem != &dbArray->smime.link; mElem = PR_NEXT_LINK(mElem)) {
  424. /*  Look for subject's email in S/MIME entries.  */
  425. smimeNode = LISTNODE_CAST(mElem);
  426. smimeEntry = (certDBEntrySMime *)&smimeNode->entry;
  427. smimeMap = (certDBEntryMap *)smimeNode->appData;
  428. if (PL_strcmp(subjectEntry->emailAddr, 
  429.               smimeEntry->emailAddr) == 0) {
  430.     /*  Found a S/MIME entry for subject's email.  */
  431.     if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
  432.                               &smimeEntry->subjectName)) {
  433. /*  S/MIME entry and subject match.  */
  434. subjMap->pSMime = smimeNode;
  435. smimeMap->pSubject = subjNode;
  436.     } else {
  437. /*  S/MIME entry found is for diff. subject.  */
  438. subjMap->pSMime = WrongEntry;
  439.     }
  440. }
  441.     }
  442. } else {
  443.     subjMap->pSMime = NoSMime;
  444. }
  445.     }
  446.     return SECSuccess;
  447. }
  448. void
  449. printnode(dbDebugInfo *info, const char *str, int num)
  450. {
  451.     if (!info->dograph)
  452. return;
  453.     if (num < 0) {
  454. PR_fprintf(info->graphfile, str);
  455.     } else {
  456. PR_fprintf(info->graphfile, str, num);
  457.     }
  458. }
  459. PRBool
  460. map_handle_is_ok(dbDebugInfo *info, void *mapPtr, int indent)
  461. {
  462.     if (mapPtr == NULL) {
  463. if (indent > 0)
  464.     printnode(info, "                ", -1);
  465. if (indent >= 0)
  466.     printnode(info, "******************* ", -1);
  467. return PR_FALSE;
  468.     } else if (mapPtr == WrongEntry) {
  469. if (indent > 0)
  470.     printnode(info, "                  ", -1);
  471. if (indent >= 0)
  472.     printnode(info, "??????????????????? ", -1);
  473. return PR_FALSE;
  474.     } else {
  475. return PR_TRUE;
  476.     }
  477. }
  478. /* these call each other */
  479. void print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, 
  480.                        int direction);
  481. void print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, 
  482.                           int direction);
  483. void print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 
  484.                          int direction, int optindex, int opttype);
  485. void print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, 
  486.                       int direction);
  487. /* Given an smime entry, print its unique identifier.  If GOLEFT is 
  488.  * specified, print the cert<-subject<-smime map, else just print
  489.  * the smime entry.
  490.  */
  491. void
  492. print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, int direction)
  493. {
  494.     certDBSubjectEntryMap *subjMap;
  495.     certDBEntryListNode *subjNode;
  496.     if (direction == GOLEFT) {
  497. /* Need to output subject and cert first, see print_subject_graph */
  498. subjNode = smimeMap->pSubject;
  499. if (map_handle_is_ok(info, (void *)subjNode, 1)) {
  500.     subjMap = (certDBSubjectEntryMap *)subjNode->appData; 
  501.     print_subject_graph(info, subjMap, GOLEFT,
  502.                         smimeMap->index, certDBEntryTypeSMimeProfile);
  503. } else {
  504.     printnode(info, "<---- S/MIME   %5d   ", smimeMap->index);
  505. }
  506.     } else {
  507. printnode(info, "S/MIME   %5d   ", smimeMap->index);
  508.     }
  509. }
  510. /* Given a nickname entry, print its unique identifier.  If GOLEFT is 
  511.  * specified, print the cert<-subject<-nickname map, else just print
  512.  * the nickname entry.
  513.  */
  514. void
  515. print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, int direction)
  516. {
  517.     certDBSubjectEntryMap *subjMap;
  518.     certDBEntryListNode *subjNode;
  519.     if (direction == GOLEFT) {
  520. /* Need to output subject and cert first, see print_subject_graph */
  521. subjNode = nickMap->pSubject;
  522. if (map_handle_is_ok(info, (void *)subjNode, 1)) {
  523.     subjMap = (certDBSubjectEntryMap *)subjNode->appData;
  524.     print_subject_graph(info, subjMap, GOLEFT,
  525.                         nickMap->index, certDBEntryTypeNickname);
  526. } else {
  527.     printnode(info, "<---- Nickname %5d   ", nickMap->index);
  528. }
  529.     } else {
  530. printnode(info, "Nickname %5d   ", nickMap->index);
  531.     }
  532. }
  533. /* Given a subject entry, if going right print the graph of the nickname|smime
  534.  * that it maps to (by its unique identifier); and if going left
  535.  * print the list of certs that it points to.
  536.  */
  537. void
  538. print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 
  539.                     int direction, int optindex, int opttype)
  540. {
  541.     certDBEntryMap *map;
  542.     certDBEntryListNode *node;
  543.     int i;
  544.     /* The first line of output always contains the cert id, subject id,
  545.      * and nickname|smime id.  Subsequent lines may contain additional
  546.      * cert id's for the subject if going left or both directions.
  547.      * Ex. of printing the graph for a subject entry:
  548.      * Cert 3 <- Subject 5 -> Nickname 32
  549.      * Cert 8 /
  550.      * Cert 9 /
  551.      * means subject 5 has 3 certs, 3, 8, and 9, and corresponds
  552.      * to nickname entry 32.
  553.      * To accomplish the above, it is required to dump the entire first
  554.      * line left-to-right, regardless of the input direction, and then
  555.      * finish up any remaining cert entries.  Hence the code is uglier
  556.      * than one may expect.
  557.      */
  558.     if (direction == GOLEFT || direction == GOBOTH) {
  559. /* In this case, nothing should be output until the first cert is
  560.  * located and output (cert 3 in the above example).
  561.  */
  562. if (subjMap->numCerts == 0 || subjMap->pCerts == NULL)
  563.     /* XXX uh-oh */
  564.     return;
  565. /* get the first cert and dump it. */
  566. node = subjMap->pCerts[0];
  567. if (map_handle_is_ok(info, (void *)node, 0)) {
  568.     map = (certDBEntryMap *)node->appData;
  569.     /* going left here stops. */
  570.     print_cert_graph(info, map, GOLEFT); 
  571. }
  572. /* Now it is safe to output the subject id. */
  573. if (direction == GOLEFT)
  574.     printnode(info, "Subject  %5d <---- ", subjMap->index);
  575. else /* direction == GOBOTH */
  576.     printnode(info, "Subject  %5d ----> ", subjMap->index);
  577.     }
  578.     if (direction == GORIGHT || direction == GOBOTH) { 
  579. /* Okay, now output the nickname|smime for this subject. */
  580. if (direction != GOBOTH) /* handled above */
  581.    printnode(info, "Subject  %5d ----> ", subjMap->index);
  582. if (subjMap->pNickname) {
  583.     node = subjMap->pNickname;
  584.     if (map_handle_is_ok(info, (void *)node, 0)) {
  585. map = (certDBEntryMap *)node->appData;
  586. /* going right here stops. */
  587. print_nickname_graph(info, map, GORIGHT);
  588.     }
  589. }
  590. if (subjMap->pSMime) {
  591.     node = subjMap->pSMime;
  592.     if (map_handle_is_ok(info, (void *)node, 0)) {
  593. map = (certDBEntryMap *)node->appData;
  594. /* going right here stops. */
  595. print_smime_graph(info, map, GORIGHT); 
  596.     }
  597. }
  598. if (!subjMap->pNickname && !subjMap->pSMime) {
  599.     printnode(info, "******************* ", -1);
  600. }
  601.     }
  602.     if (direction != GORIGHT) { /* going right has only one cert */
  603. if (opttype == certDBEntryTypeNickname)
  604.     printnode(info, "Nickname %5d   ", optindex);
  605. else if (opttype == certDBEntryTypeSMimeProfile)
  606.     printnode(info, "S/MIME   %5d   ", optindex);
  607. for (i=1 /* 1st one already done */; i<subjMap->numCerts; i++) {
  608.     printnode(info, "n", -1); /* start a new line */
  609.     node = subjMap->pCerts[i];
  610.     if (map_handle_is_ok(info, (void *)node, 0)) {
  611. map = (certDBEntryMap *)node->appData;
  612. /* going left here stops. */
  613. print_cert_graph(info, map, GOLEFT); 
  614. printnode(info, "/", -1);
  615.     }
  616. }
  617.     }
  618. }
  619. /* Given a cert entry, print its unique identifer.  If GORIGHT is specified,
  620.  * print the cert->subject->nickname|smime map, else just print
  621.  * the cert entry.
  622.  */
  623. void
  624. print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, int direction)
  625. {
  626.     certDBSubjectEntryMap *subjMap;
  627.     certDBEntryListNode *subjNode;
  628.     if (direction == GOLEFT) {
  629. printnode(info, "Cert     %5d <---- ", certMap->index);
  630. /* only want cert entry, terminate here. */
  631. return;
  632.     }
  633.     /* Keep going right then. */
  634.     printnode(info, "Cert     %5d ----> ", certMap->index);
  635.     subjNode = certMap->pSubject;
  636.     if (map_handle_is_ok(info, (void *)subjNode, 0)) {
  637. subjMap = (certDBSubjectEntryMap *)subjNode->appData;
  638. print_subject_graph(info, subjMap, GORIGHT, -1, -1);
  639.     }
  640. }
  641. SECStatus
  642. computeDBGraph(certDBArray *dbArray, dbDebugInfo *info)
  643. {
  644.     PRCList *cElem, *sElem, *nElem, *mElem;
  645.     certDBEntryListNode *node;
  646.     certDBEntryMap *map;
  647.     certDBSubjectEntryMap *subjMap;
  648.     /* Graph is of this form:
  649.      *
  650.      * certs:
  651.      * cert ---> subject ---> (nickname|smime)
  652.      *
  653.      * subjects:
  654.      * cert <--- subject ---> (nickname|smime)
  655.      *
  656.      * nicknames and smime:
  657.      * cert <--- subject <--- (nickname|smime)
  658.      */
  659.     /* Print cert graph. */
  660.     for (cElem = PR_LIST_HEAD(&dbArray->certs.link);
  661.          cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
  662. /* Print graph of everything to right of cert entry. */
  663. node = LISTNODE_CAST(cElem);
  664. map = (certDBEntryMap *)node->appData;
  665. print_cert_graph(info, map, GORIGHT);
  666. printnode(info, "n", -1);
  667.     }
  668.     printnode(info, "n", -1);
  669.     /* Print subject graph. */
  670.     for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
  671.          sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
  672. /* Print graph of everything to both sides of subject entry. */
  673. node = LISTNODE_CAST(sElem);
  674. subjMap = (certDBSubjectEntryMap *)node->appData;
  675. print_subject_graph(info, subjMap, GOBOTH, -1, -1);
  676. printnode(info, "n", -1);
  677.     }
  678.     printnode(info, "n", -1);
  679.     /* Print nickname graph. */
  680.     for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
  681.          nElem != &dbArray->nicknames.link; nElem = PR_NEXT_LINK(nElem)) {
  682. /* Print graph of everything to left of nickname entry. */
  683. node = LISTNODE_CAST(nElem);
  684. map = (certDBEntryMap *)node->appData;
  685. print_nickname_graph(info, map, GOLEFT);
  686. printnode(info, "n", -1);
  687.     }
  688.     printnode(info, "n", -1);
  689.     /* Print smime graph. */
  690.     for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
  691.          mElem != &dbArray->smime.link; mElem = PR_NEXT_LINK(mElem)) {
  692. /* Print graph of everything to left of smime entry. */
  693. node = LISTNODE_CAST(mElem);
  694. if (node == NULL) break;
  695. map = (certDBEntryMap *)node->appData;
  696. print_smime_graph(info, map, GOLEFT);
  697. printnode(info, "n", -1);
  698.     }
  699.     printnode(info, "n", -1);
  700.     return SECSuccess;
  701. }
  702. /*
  703.  * List the entries in the db, showing handles between entry types.
  704.  */
  705. void
  706. verboseOutput(certDBArray *dbArray, dbDebugInfo *info)
  707. {
  708.     int i, ref;
  709.     PRCList *elem;
  710.     certDBEntryListNode *node;
  711.     certDBEntryMap *map;
  712.     certDBSubjectEntryMap *smap;
  713.     certDBEntrySubject *subjectEntry;
  714.     /* List certs */
  715.     for (elem = PR_LIST_HEAD(&dbArray->certs.link);
  716.          elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) {
  717. node = LISTNODE_CAST(elem);
  718. map = (certDBEntryMap *)node->appData;
  719. dumpCertEntry((certDBEntryCert*)&node->entry, map->index, info->out);
  720. /* walk the cert handle to it's subject entry */
  721. if (map_handle_is_ok(info, map->pSubject, -1)) {
  722.     smap = (certDBSubjectEntryMap *)map->pSubject->appData;
  723.     ref = smap->index;
  724.     PR_fprintf(info->out, "-->(subject %d)nnn", ref);
  725. } else {
  726.     PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)nnn");
  727. }
  728.     }
  729.     /* List subjects */
  730.     for (elem = PR_LIST_HEAD(&dbArray->subjects.link);
  731.          elem != &dbArray->subjects.link; elem = PR_NEXT_LINK(elem)) {
  732. node = LISTNODE_CAST(elem);
  733. subjectEntry = (certDBEntrySubject *)&node->entry;
  734. smap = (certDBSubjectEntryMap *)node->appData;
  735. dumpSubjectEntry(subjectEntry, smap->index, info->out);
  736. /* iterate over subject's certs */
  737. for (i=0; i<smap->numCerts; i++) {
  738.     /* walk each subject handle to it's cert entries */
  739.     if (map_handle_is_ok(info, smap->pCerts[i], -1)) {
  740. ref = ((certDBEntryMap *)smap->pCerts[i]->appData)->index;
  741. PR_fprintf(info->out, "-->(%d. certificate %d)n", i, ref);
  742.     } else {
  743. PR_fprintf(info->out, "-->(%d. MISSING CERT ENTRY)n", i);
  744.     }
  745. }
  746. if (subjectEntry->nickname) {
  747.     /* walk each subject handle to it's nickname entry */
  748.     if (map_handle_is_ok(info, smap->pNickname, -1)) {
  749. ref = ((certDBEntryMap *)smap->pNickname->appData)->index;
  750. PR_fprintf(info->out, "-->(nickname %d)n", ref);
  751.     } else {
  752. PR_fprintf(info->out, "-->(MISSING NICKNAME ENTRY)n");
  753.     }
  754. }
  755. if (subjectEntry->emailAddr) {
  756.     /* walk each subject handle to it's smime entry */
  757.     if (map_handle_is_ok(info, smap->pSMime, -1)) {
  758. ref = ((certDBEntryMap *)smap->pSMime->appData)->index;
  759. PR_fprintf(info->out, "-->(s/mime %d)n", ref);
  760.     } else {
  761. PR_fprintf(info->out, "-->(MISSING S/MIME ENTRY)n");
  762.     }
  763. }
  764. PR_fprintf(info->out, "nn");
  765.     }
  766.     for (elem = PR_LIST_HEAD(&dbArray->nicknames.link);
  767.          elem != &dbArray->nicknames.link; elem = PR_NEXT_LINK(elem)) {
  768. node = LISTNODE_CAST(elem);
  769. map = (certDBEntryMap *)node->appData;
  770. dumpNicknameEntry((certDBEntryNickname*)&node->entry, map->index, 
  771.                   info->out);
  772. if (map_handle_is_ok(info, map->pSubject, -1)) {
  773.     ref = ((certDBEntryMap *)map->pSubject->appData)->index;
  774.     PR_fprintf(info->out, "-->(subject %d)nnn", ref);
  775. } else {
  776.     PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)nnn");
  777. }
  778.     }
  779.     for (elem = PR_LIST_HEAD(&dbArray->smime.link);
  780.          elem != &dbArray->smime.link; elem = PR_NEXT_LINK(elem)) {
  781. node = LISTNODE_CAST(elem);
  782. map = (certDBEntryMap *)node->appData;
  783. dumpSMimeEntry((certDBEntrySMime*)&node->entry, map->index, info->out);
  784. if (map_handle_is_ok(info, map->pSubject, -1)) {
  785.     ref = ((certDBEntryMap *)map->pSubject->appData)->index;
  786.     PR_fprintf(info->out, "-->(subject %d)nnn", ref);
  787. } else {
  788.     PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)nnn");
  789. }
  790.     }
  791.     PR_fprintf(info->out, "nn");
  792. }
  793. char *errResult[] = {
  794.     "Certificate entries that had no subject entry.", 
  795.     "Certificate entries that had no key in their subject entry.", 
  796.     "Subject entries that had no nickname or email address.",
  797.     "Redundant nicknames (subjects with the same nickname).",
  798.     "Subject entries that had no nickname entry.",
  799.     "Redundant email addresses (subjects with the same email address).",
  800.     "Subject entries that had no S/MIME entry.",
  801.     "Nickname entries that had no subject entry.", 
  802.     "S/MIME entries that had no subject entry.",
  803. };
  804. int
  805. fillDBEntryArray(CERTCertDBHandle *handle, certDBEntryType type, 
  806.                  certDBEntryListNode *list)
  807. {
  808.     PRCList *elem;
  809.     certDBEntryListNode *node;
  810.     certDBEntryMap *mnode;
  811.     certDBSubjectEntryMap *smnode;
  812.     PRArenaPool *arena;
  813.     int count = 0;
  814.     /* Initialize a dummy entry in the list.  The list head will be the
  815.      * next element, so this element is skipped by for loops.
  816.      */
  817.     PR_INIT_CLIST((PRCList *)list);
  818.     /* Collect all of the cert db entries for this type into a list. */
  819.     SEC_TraverseDBEntries(handle, type, SEC_GetCertDBEntryList, 
  820.                           (PRCList *)list);
  821.     for (elem = PR_LIST_HEAD(&list->link); 
  822.          elem != &list->link; elem = PR_NEXT_LINK(elem)) {
  823. /* Iterate over the entries and ... */
  824. node = (certDBEntryListNode *)elem;
  825. if (type != certDBEntryTypeSubject) {
  826.     arena = PORT_NewArena(sizeof(*mnode));
  827.     mnode = (certDBEntryMap *)PORT_ArenaZAlloc(arena, sizeof(*mnode));
  828.     mnode->arena = arena;
  829.     /* ... assign a unique index number to each node, and ... */
  830.     mnode->index = count;
  831.     /* ... set the map pointer for the node. */
  832.     node->appData = (void *)mnode;
  833. } else {
  834.     /* allocate some room for the cert pointers also */
  835.     arena = PORT_NewArena(sizeof(*smnode) + 20*sizeof(void *));
  836.     smnode = (certDBSubjectEntryMap *)
  837.               PORT_ArenaZAlloc(arena, sizeof(*smnode));
  838.     smnode->arena = arena;
  839.     smnode->index = count;
  840.     node->appData = (void *)smnode;
  841. }
  842. count++;
  843.     }
  844.     return count;
  845. }
  846. void
  847. freeDBEntryList(PRCList *list)
  848. {
  849.     PRCList *next, *elem;
  850.     certDBEntryListNode *node;
  851.     certDBEntryMap *map;
  852.     for (elem = PR_LIST_HEAD(list); elem != list;) { 
  853. next = PR_NEXT_LINK(elem);
  854. node = (certDBEntryListNode *)elem;
  855. map = (certDBEntryMap *)node->appData;
  856. PR_REMOVE_LINK(&node->link);
  857. PORT_FreeArena(map->arena, PR_TRUE);
  858. PORT_FreeArena(node->entry.common.arena, PR_TRUE);
  859. elem = next;
  860.     }
  861. }
  862. void
  863. DBCK_DebugDB(CERTCertDBHandle *handle, PRFileDesc *out, PRFileDesc *mailfile)
  864. {
  865.     int i, nCertsFound, nSubjFound, nErr;
  866.     int nCerts, nSubjects, nSubjCerts, nNicknames, nSMime;
  867.     PRCList *elem;
  868.     char c;
  869.     dbDebugInfo info;
  870.     certDBArray dbArray;
  871.     PORT_Memset(&dbArray, 0, sizeof(dbArray));
  872.     PORT_Memset(&info, 0, sizeof(info));
  873.     info.verbose = (out == NULL) ? PR_FALSE : PR_TRUE ;
  874.     info.dograph = (mailfile == NULL) ? PR_FALSE : PR_TRUE ;
  875.     info.out = (out) ? out : PR_STDOUT;
  876.     info.graphfile = mailfile;
  877.     /*  Fill the array structure with cert/subject/nickname/smime entries.  */
  878.     dbArray.numCerts = fillDBEntryArray(handle, certDBEntryTypeCert, 
  879.                                         &dbArray.certs);
  880.     dbArray.numSubjects = fillDBEntryArray(handle, certDBEntryTypeSubject, 
  881.                                            &dbArray.subjects);
  882.     dbArray.numNicknames = fillDBEntryArray(handle, certDBEntryTypeNickname, 
  883.                                             &dbArray.nicknames);
  884.     dbArray.numSMime = fillDBEntryArray(handle, certDBEntryTypeSMimeProfile, 
  885.                                         &dbArray.smime);
  886.     /*  Compute the map between the database entries.  */
  887.     mapSubjectEntries(&dbArray);
  888.     mapCertEntries(&dbArray);
  889.     computeDBGraph(&dbArray, &info);
  890.     /*  Store the totals for later reference.  */
  891.     nCerts = dbArray.numCerts;
  892.     nSubjects = dbArray.numSubjects;
  893.     nNicknames = dbArray.numNicknames;
  894.     nSMime = dbArray.numSMime;
  895.     nSubjCerts = 0;
  896.     for (elem = PR_LIST_HEAD(&dbArray.subjects.link);
  897.          elem != &dbArray.subjects.link; elem = PR_NEXT_LINK(elem)) {
  898. certDBSubjectEntryMap *smap;
  899. smap = (certDBSubjectEntryMap *)LISTNODE_CAST(elem)->appData;
  900. nSubjCerts += smap->numCerts;
  901.     }
  902.     if (info.verbose) {
  903. /*  Dump the database contents.  */
  904. verboseOutput(&dbArray, &info);
  905.     }
  906.     freeDBEntryList(&dbArray.certs.link);
  907.     freeDBEntryList(&dbArray.subjects.link);
  908.     freeDBEntryList(&dbArray.nicknames.link);
  909.     freeDBEntryList(&dbArray.smime.link);
  910.     PR_fprintf(info.out, "n");
  911.     PR_fprintf(info.out, "Database statistics:n");
  912.     PR_fprintf(info.out, "N0: Found %4d Certificate entries.n", 
  913.                           nCerts);
  914.     PR_fprintf(info.out, "N1: Found %4d Subject entries (unique DN's).n", 
  915.                           nSubjects);
  916.     PR_fprintf(info.out, "N2: Found %4d Cert keys within Subject entries.n", 
  917.                           nSubjCerts);
  918.     PR_fprintf(info.out, "N3: Found %4d Nickname entries.n", 
  919.                           nNicknames);
  920.     PR_fprintf(info.out, "N4: Found %4d S/MIME entries.n", 
  921.                           nSMime);
  922.     PR_fprintf(info.out, "n");
  923.     nErr = 0;
  924.     for (i=0; i<sizeof(errResult)/sizeof(char*); i++) {
  925. PR_fprintf(info.out, "E%d: Found %4d %sn", 
  926.            i, info.dbErrors[i], errResult[i]);
  927. nErr += info.dbErrors[i];
  928.     }
  929.     PR_fprintf(info.out, "--------------n    Found %4d errors in database.n", 
  930.                nErr);
  931.     PR_fprintf(info.out, "nCertificates:n");
  932.     PR_fprintf(info.out, "N0 == N2 + E%d + E%dn", NoSubjectForCert, 
  933.                                                    SubjectHasNoKeyForCert);
  934.     nCertsFound = nSubjCerts +
  935.                   info.dbErrors[NoSubjectForCert] +
  936.                   info.dbErrors[SubjectHasNoKeyForCert];
  937.     c = (nCertsFound == nCerts) ? '=' : '!';
  938.     PR_fprintf(info.out, "%d %c= %d + %d + %dn", nCerts, c, nSubjCerts, 
  939.                   info.dbErrors[NoSubjectForCert],
  940.                   info.dbErrors[SubjectHasNoKeyForCert]);
  941.     PR_fprintf(info.out, "nSubjects:n");
  942.     PR_fprintf(info.out, "N1 == N3 + N4 + E%d + E%d + E%d + E%d + E%d - E%d - E%dn",
  943.                   NoNicknameOrSMimeForSubject, WrongNicknameForSubject,
  944.   NoNicknameEntry, WrongSMimeForSubject, NoSMimeEntry,
  945.   NoSubjectForNickname, NoSubjectForSMime);
  946.     PR_fprintf(info.out, "      - #(subjects with both nickname and S/MIME entries)n");
  947.     nSubjFound = nNicknames + nSMime + 
  948.                  info.dbErrors[NoNicknameOrSMimeForSubject] +
  949.  info.dbErrors[WrongNicknameForSubject] +
  950.  info.dbErrors[NoNicknameEntry] +
  951.  info.dbErrors[WrongSMimeForSubject] +
  952.                  info.dbErrors[NoSMimeEntry] -
  953.  info.dbErrors[NoSubjectForNickname] -
  954.  info.dbErrors[NoSubjectForSMime] -
  955.  info.dbErrors[NicknameAndSMimeEntry];
  956.     c = (nSubjFound == nSubjects) ? '=' : '!';
  957.     PR_fprintf(info.out, "%d %c= %d + %d + %d + %d + %d + %d + %d - %d - %d - %dn",
  958.                   nSubjects, c, nNicknames, nSMime,
  959.                   info.dbErrors[NoNicknameOrSMimeForSubject],
  960.   info.dbErrors[WrongNicknameForSubject],
  961.   info.dbErrors[NoNicknameEntry],
  962.   info.dbErrors[WrongSMimeForSubject],
  963.                   info.dbErrors[NoSMimeEntry],
  964.   info.dbErrors[NoSubjectForNickname],
  965.   info.dbErrors[NoSubjectForSMime],
  966.   info.dbErrors[NicknameAndSMimeEntry]);
  967.     PR_fprintf(info.out, "n");
  968. }
  969. #ifdef DORECOVER
  970. enum {
  971.     dbInvalidCert = 0,
  972.     dbNoSMimeProfile,
  973.     dbOlderCert,
  974.     dbBadCertificate,
  975.     dbCertNotWrittenToDB
  976. };
  977. typedef struct dbRestoreInfoStr
  978. {
  979.     CERTCertDBHandle *handle;
  980.     PRBool verbose;
  981.     PRFileDesc *out;
  982.     int nCerts;
  983.     int nOldCerts;
  984.     int dbErrors[5];
  985.     PRBool removeType[3];
  986.     PRBool promptUser[3];
  987. } dbRestoreInfo;
  988. char *
  989. IsEmailCert(CERTCertificate *cert)
  990. {
  991.     char *email, *tmp1, *tmp2;
  992.     PRBool isCA;
  993.     int len;
  994.     if (!cert->subjectName) {
  995. return NULL;
  996.     }
  997.     tmp1 = PORT_Strstr(cert->subjectName, "E=");
  998.     tmp2 = PORT_Strstr(cert->subjectName, "MAIL=");
  999.     /* XXX Nelson has cert for KTrilli which does not have either
  1000.      * of above but is email cert (has cert->emailAddr). 
  1001.      */
  1002.     if (!tmp1 && !tmp2 && !cert->emailAddr) {
  1003. return NULL;
  1004.     }
  1005.     /*  Server or CA cert, not personal email.  */
  1006.     isCA = CERT_IsCACert(cert, NULL);
  1007.     if (isCA)
  1008. return NULL;
  1009.     /*  XXX CERT_IsCACert advertises checking the key usage ext.,
  1010. but doesn't appear to. */
  1011.     /*  Check the key usage extension.  */
  1012.     if (cert->keyUsagePresent) {
  1013. /*  Must at least be able to sign or encrypt (not neccesarily
  1014.  *  both if it is one of a dual cert).  
  1015.  */
  1016. if (!((cert->rawKeyUsage & KU_DIGITAL_SIGNATURE) || 
  1017.               (cert->rawKeyUsage & KU_KEY_ENCIPHERMENT)))
  1018.     return NULL;
  1019. /*  CA cert, not personal email.  */
  1020. if (cert->rawKeyUsage & (KU_KEY_CERT_SIGN | KU_CRL_SIGN))
  1021.     return NULL;
  1022.     }
  1023.     if (cert->emailAddr) {
  1024. email = PORT_Strdup(cert->emailAddr);
  1025.     } else {
  1026. if (tmp1)
  1027.     tmp1 += 2; /* "E="  */
  1028. else
  1029.     tmp1 = tmp2 + 5; /* "MAIL=" */
  1030. len = strcspn(tmp1, ", ");
  1031. email = (char*)PORT_Alloc(len+1);
  1032. PORT_Strncpy(email, tmp1, len);
  1033. email[len] = '';
  1034.     }
  1035.     return email;
  1036. }
  1037. SECStatus
  1038. deleteit(CERTCertificate *cert, void *arg)
  1039. {
  1040.     return SEC_DeletePermCertificate(cert);
  1041. }
  1042. /*  Different than DeleteCertificate - has the added bonus of removing
  1043.  *  all certs with the same DN.  
  1044.  */
  1045. SECStatus
  1046. deleteAllEntriesForCert(CERTCertDBHandle *handle, CERTCertificate *cert,
  1047.                         PRFileDesc *outfile)
  1048. {
  1049. #if 0
  1050.     certDBEntrySubject *subjectEntry;
  1051.     certDBEntryNickname *nicknameEntry;
  1052.     certDBEntrySMime *smimeEntry;
  1053.     int i;
  1054. #endif
  1055.     if (outfile) {
  1056. PR_fprintf(outfile, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$nn");
  1057. PR_fprintf(outfile, "Deleting redundant certificate:n");
  1058. dumpCertificate(cert, -1, outfile);
  1059.     }
  1060.     CERT_TraverseCertsForSubject(handle, cert->subjectList, deleteit, NULL);
  1061. #if 0
  1062.     CERT_LockDB(handle);
  1063.     subjectEntry = ReadDBSubjectEntry(handle, &cert->derSubject);
  1064.     /*  It had better be there, or created a bad db.  */
  1065.     PORT_Assert(subjectEntry);
  1066.     for (i=0; i<subjectEntry->ncerts; i++) {
  1067. DeleteDBCertEntry(handle, &subjectEntry->certKeys[i]);
  1068.     }
  1069.     DeleteDBSubjectEntry(handle, &cert->derSubject);
  1070.     if (subjectEntry->emailAddr) {
  1071. smimeEntry = ReadDBSMimeEntry(handle, subjectEntry->emailAddr);
  1072. if (smimeEntry) {
  1073.     if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
  1074.                               &smimeEntry->subjectName))
  1075. /*  Only delete it if it's for this subject!  */
  1076. DeleteDBSMimeEntry(handle, subjectEntry->emailAddr);
  1077.     SEC_DestroyDBEntry((certDBEntry*)smimeEntry);
  1078. }
  1079.     }
  1080.     if (subjectEntry->nickname) {
  1081. nicknameEntry = ReadDBNicknameEntry(handle, subjectEntry->nickname);
  1082. if (nicknameEntry) {
  1083.     if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
  1084.                               &nicknameEntry->subjectName))
  1085. /*  Only delete it if it's for this subject!  */
  1086. DeleteDBNicknameEntry(handle, subjectEntry->nickname);
  1087.     SEC_DestroyDBEntry((certDBEntry*)nicknameEntry);
  1088. }
  1089.     }
  1090.     SEC_DestroyDBEntry((certDBEntry*)subjectEntry);
  1091.     CERT_UnlockDB(handle);
  1092. #endif
  1093.     return SECSuccess;
  1094. }
  1095. void
  1096. getCertsToDelete(char *numlist, int len, int *certNums, int nCerts)
  1097. {
  1098.     int j, num;
  1099.     char *numstr, *numend, *end;
  1100.     numstr = numlist;
  1101.     end = numstr + len - 1;
  1102.     while (numstr != end) {
  1103. numend = strpbrk(numstr, ", n");
  1104. *numend = '';
  1105. if (PORT_Strlen(numstr) == 0)
  1106.     return;
  1107. num = PORT_Atoi(numstr);
  1108. if (numstr == numlist)
  1109.     certNums[0] = num;
  1110. for (j=1; j<nCerts+1; j++) {
  1111.     if (num == certNums[j]) {
  1112. certNums[j] = -1;
  1113. break;
  1114.     }
  1115. }
  1116. if (numend == end)
  1117.     break;
  1118. numstr = strpbrk(numend+1, "0123456789");
  1119.     }
  1120. }
  1121. PRBool
  1122. userSaysDeleteCert(CERTCertificate **certs, int nCerts,
  1123.                    int errtype, dbRestoreInfo *info, int *certNums)
  1124. {
  1125.     char response[32];
  1126.     int32 nb;
  1127.     int i;
  1128.     /*  User wants to remove cert without prompting.  */
  1129.     if (info->promptUser[errtype] == PR_FALSE)
  1130. return (info->removeType[errtype]);
  1131.     switch (errtype) {
  1132.     case dbInvalidCert:
  1133. PR_fprintf(PR_STDOUT, "********  Expired ********n");
  1134. PR_fprintf(PR_STDOUT, "Cert has expired.nn");
  1135. dumpCertificate(certs[0], -1, PR_STDOUT);
  1136. PR_fprintf(PR_STDOUT,
  1137.            "Keep it? (y/n - this one, Y/N - all expired certs) [n] ");
  1138. break;
  1139.     case dbNoSMimeProfile:
  1140. PR_fprintf(PR_STDOUT, "********  No Profile ********n");
  1141. PR_fprintf(PR_STDOUT, "S/MIME cert has no profile.nn");
  1142. dumpCertificate(certs[0], -1, PR_STDOUT);
  1143. PR_fprintf(PR_STDOUT,
  1144.       "Keep it? (y/n - this one, Y/N - all S/MIME w/o profile) [n] ");
  1145. break;
  1146.     case dbOlderCert:
  1147. PR_fprintf(PR_STDOUT, "*******  Redundant nickname/email *******nn");
  1148. PR_fprintf(PR_STDOUT, "These certs have the same nickname/email:n");
  1149. for (i=0; i<nCerts; i++)
  1150.     dumpCertificate(certs[i], i, PR_STDOUT);
  1151. PR_fprintf(PR_STDOUT, 
  1152. "Enter the certs you would like to keep from those listed above.n");
  1153. PR_fprintf(PR_STDOUT, 
  1154. "Use a comma-separated list of the cert numbers (ex. 0, 8, 12).n");
  1155. PR_fprintf(PR_STDOUT, 
  1156. "The first cert in the list will be the primary certn");
  1157. PR_fprintf(PR_STDOUT, 
  1158. " accessed by the nickname/email handle.n");
  1159. PR_fprintf(PR_STDOUT, 
  1160. "List cert numbers to keep here, or hit entern");
  1161. PR_fprintf(PR_STDOUT, 
  1162. " to always keep only the newest cert:  ");
  1163. break;
  1164.     default:
  1165.     }
  1166.     nb = PR_Read(PR_STDIN, response, sizeof(response));
  1167.     PR_fprintf(PR_STDOUT, "nn");
  1168.     if (errtype == dbOlderCert) {
  1169. if (!isdigit(response[0])) {
  1170.     info->promptUser[errtype] = PR_FALSE;
  1171.     info->removeType[errtype] = PR_TRUE;
  1172.     return PR_TRUE;
  1173. }
  1174. getCertsToDelete(response, nb, certNums, nCerts);
  1175. return PR_TRUE;
  1176.     }
  1177.     /*  User doesn't want to be prompted for this type anymore.  */
  1178.     if (response[0] == 'Y') {
  1179. info->promptUser[errtype] = PR_FALSE;
  1180. info->removeType[errtype] = PR_FALSE;
  1181. return PR_FALSE;
  1182.     } else if (response[0] == 'N') {
  1183. info->promptUser[errtype] = PR_FALSE;
  1184. info->removeType[errtype] = PR_TRUE;
  1185. return PR_TRUE;
  1186.     }
  1187.     return (response[0] != 'y') ? PR_TRUE : PR_FALSE;
  1188. }
  1189. SECStatus
  1190. addCertToDB(certDBEntryCert *certEntry, dbRestoreInfo *info, 
  1191.             CERTCertDBHandle *oldhandle)
  1192. {
  1193.     SECStatus rv = SECSuccess;
  1194.     PRBool allowOverride;
  1195.     PRBool userCert;
  1196.     SECCertTimeValidity validity;
  1197.     CERTCertificate *oldCert = NULL;
  1198.     CERTCertificate *dbCert = NULL;
  1199.     CERTCertificate *newCert = NULL;
  1200.     CERTCertTrust *trust;
  1201.     certDBEntrySMime *smimeEntry = NULL;
  1202.     char *email = NULL;
  1203.     char *nickname = NULL;
  1204.     int nCertsForSubject = 1;
  1205.     oldCert = CERT_DecodeDERCertificate(&certEntry->derCert, PR_FALSE,
  1206.                                         certEntry->nickname);
  1207.     if (!oldCert) {
  1208. info->dbErrors[dbBadCertificate]++;
  1209. SEC_DestroyDBEntry((certDBEntry*)certEntry);
  1210. return SECSuccess;
  1211.     }
  1212.     oldCert->dbEntry = certEntry;
  1213.     oldCert->trust = &certEntry->trust;
  1214.     oldCert->dbhandle = oldhandle;
  1215.     trust = oldCert->trust;
  1216.     info->nOldCerts++;
  1217.     if (info->verbose)
  1218. PR_fprintf(info->out, "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%nn");
  1219.     if (oldCert->nickname)
  1220. nickname = PORT_Strdup(oldCert->nickname);
  1221.     /*  Always keep user certs.  Skip ahead.  */
  1222.     /*  XXX if someone sends themselves a signed message, it is possible
  1223. for their cert to be imported as an "other" cert, not a user cert.
  1224. this mucks with smime entries...  */
  1225.     userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
  1226.                (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
  1227.                (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
  1228.     if (userCert)
  1229. goto createcert;
  1230.     /*  If user chooses so, ignore expired certificates.  */
  1231.     allowOverride = (PRBool)((oldCert->keyUsage == certUsageSSLServer) ||
  1232.                          (oldCert->keyUsage == certUsageSSLServerWithStepUp));
  1233.     validity = CERT_CheckCertValidTimes(oldCert, PR_Now(), allowOverride);
  1234.     /*  If cert expired and user wants to delete it, ignore it. */
  1235.     if ((validity != secCertTimeValid) && 
  1236.  userSaysDeleteCert(&oldCert, 1, dbInvalidCert, info, 0)) {
  1237. info->dbErrors[dbInvalidCert]++;
  1238. if (info->verbose) {
  1239.     PR_fprintf(info->out, "Deleting expired certificate:n");
  1240.     dumpCertificate(oldCert, -1, info->out);
  1241. }
  1242. goto cleanup;
  1243.     }
  1244.     /*  New database will already have default certs, don't attempt
  1245. to overwrite them.  */
  1246.     dbCert = CERT_FindCertByDERCert(info->handle, &oldCert->derCert);
  1247.     if (dbCert) {
  1248. info->nCerts++;
  1249. if (info->verbose) {
  1250.     PR_fprintf(info->out, "Added certificate to database:n");
  1251.     dumpCertificate(oldCert, -1, info->out);
  1252. }
  1253. goto cleanup;
  1254.     }
  1255.     
  1256.     /*  Determine if cert is S/MIME and get its email if so.  */
  1257.     email = IsEmailCert(oldCert);
  1258.     /*
  1259. XXX  Just create empty profiles?
  1260.     if (email) {
  1261. SECItem *profile = CERT_FindSMimeProfile(oldCert);
  1262. if (!profile &&
  1263.     userSaysDeleteCert(&oldCert, 1, dbNoSMimeProfile, info, 0)) {
  1264.     info->dbErrors[dbNoSMimeProfile]++;
  1265.     if (info->verbose) {
  1266. PR_fprintf(info->out, 
  1267.            "Deleted cert missing S/MIME profile.n");
  1268. dumpCertificate(oldCert, -1, info->out);
  1269.     }
  1270.     goto cleanup;
  1271. } else {
  1272.     SECITEM_FreeItem(profile);
  1273. }
  1274.     }
  1275.     */
  1276. createcert:
  1277.     /*  Sometimes happens... */
  1278.     if (!nickname && userCert)
  1279. nickname = PORT_Strdup(oldCert->subjectName);
  1280.     /*  Create a new certificate, copy of the old one.  */
  1281.     newCert = CERT_NewTempCertificate(info->handle, &oldCert->derCert, 
  1282.                                       nickname, PR_FALSE, PR_TRUE);
  1283.     if (!newCert) {
  1284. PR_fprintf(PR_STDERR, "Unable to create new certificate.n");
  1285. dumpCertificate(oldCert, -1, PR_STDERR);
  1286. info->dbErrors[dbBadCertificate]++;
  1287. goto cleanup;
  1288.     }
  1289.     /*  Add the cert to the new database.  */
  1290.     rv = CERT_AddTempCertToPerm(newCert, nickname, oldCert->trust);
  1291.     if (rv) {
  1292. PR_fprintf(PR_STDERR, "Failed to write temp cert to perm database.n");
  1293. dumpCertificate(oldCert, -1, PR_STDERR);
  1294. info->dbErrors[dbCertNotWrittenToDB]++;
  1295. goto cleanup;
  1296.     }
  1297.     if (info->verbose) {
  1298. PR_fprintf(info->out, "Added certificate to database:n");
  1299. dumpCertificate(oldCert, -1, info->out);
  1300.     }
  1301.     /*  If the cert is an S/MIME cert, and the first with it's subject,
  1302.      *  modify the subject entry to include the email address,
  1303.      *  CERT_AddTempCertToPerm does not do email addresses and S/MIME entries.
  1304.      */
  1305.     if (smimeEntry) { /*&& !userCert && nCertsForSubject == 1) { */
  1306. #if 0
  1307. UpdateSubjectWithEmailAddr(newCert, email);
  1308. #endif
  1309. SECItem emailProfile, profileTime;
  1310. rv = CERT_FindFullSMimeProfile(oldCert, &emailProfile, &profileTime);
  1311. /*  calls UpdateSubjectWithEmailAddr  */
  1312. if (rv == SECSuccess)
  1313.     rv = CERT_SaveSMimeProfile(newCert, &emailProfile, &profileTime);
  1314.     }
  1315.     info->nCerts++;
  1316. cleanup:
  1317.     if (nickname)
  1318. PORT_Free(nickname);
  1319.     if (email)
  1320. PORT_Free(email);
  1321.     if (oldCert)
  1322. CERT_DestroyCertificate(oldCert);
  1323.     if (dbCert)
  1324. CERT_DestroyCertificate(dbCert);
  1325.     if (newCert)
  1326. CERT_DestroyCertificate(newCert);
  1327.     if (smimeEntry)
  1328. SEC_DestroyDBEntry((certDBEntry*)smimeEntry);
  1329.     return SECSuccess;
  1330. }
  1331. #if 0
  1332. SECStatus
  1333. copyDBEntry(SECItem *data, SECItem *key, certDBEntryType type, void *pdata)
  1334. {
  1335.     SECStatus rv;
  1336.     CERTCertDBHandle *newdb = (CERTCertDBHandle *)pdata;
  1337.     certDBEntryCommon common;
  1338.     SECItem dbkey;
  1339.     common.type = type;
  1340.     common.version = CERT_DB_FILE_VERSION;
  1341.     common.flags = data->data[2];
  1342.     common.arena = NULL;
  1343.     dbkey.len = key->len + SEC_DB_KEY_HEADER_LEN;
  1344.     dbkey.data = (unsigned char *)PORT_Alloc(dbkey.len*sizeof(unsigned char));
  1345.     PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], key->data, key->len);
  1346.     dbkey.data[0] = type;
  1347.     rv = WriteDBEntry(newdb, &common, &dbkey, data);
  1348.     PORT_Free(dbkey.data);
  1349.     return rv;
  1350. }
  1351. #endif
  1352. int
  1353. certIsOlder(CERTCertificate **cert1, CERTCertificate** cert2)
  1354. {
  1355.     return !CERT_IsNewer(*cert1, *cert2);
  1356. }
  1357. int
  1358. findNewestSubjectForEmail(CERTCertDBHandle *handle, int subjectNum,
  1359.                           certDBArray *dbArray, dbRestoreInfo *info,
  1360.                           int *subjectWithSMime, int *smimeForSubject)
  1361. {
  1362.     int newestSubject;
  1363.     int subjectsForEmail[50];
  1364.     int i, j, ns, sNum;
  1365.     certDBEntryListNode *subjects = &dbArray->subjects;
  1366.     certDBEntryListNode *smime = &dbArray->smime;
  1367.     certDBEntrySubject *subjectEntry1, *subjectEntry2;
  1368.     certDBEntrySMime *smimeEntry;
  1369.     CERTCertificate **certs;
  1370.     CERTCertificate *cert;
  1371.     CERTCertTrust *trust;
  1372.     PRBool userCert;
  1373.     int *certNums;
  1374.     ns = 0;
  1375.     subjectEntry1 = (certDBEntrySubject*)&subjects.entries[subjectNum];
  1376.     subjectsForEmail[ns++] = subjectNum;
  1377.     *subjectWithSMime = -1;
  1378.     *smimeForSubject = -1;
  1379.     newestSubject = subjectNum;
  1380.     cert = CERT_FindCertByKey(handle, &subjectEntry1->certKeys[0]);
  1381.     if (cert) {
  1382. trust = cert->trust;
  1383. userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
  1384.           (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
  1385.          (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
  1386. CERT_DestroyCertificate(cert);
  1387.     }
  1388.     /*  Loop over the remaining subjects.  */
  1389.     for (i=subjectNum+1; i<subjects.numEntries; i++) {
  1390. subjectEntry2 = (certDBEntrySubject*)&subjects.entries[i];
  1391. if (!subjectEntry2)
  1392.     continue;
  1393. if (subjectEntry2->emailAddr && 
  1394.      PORT_Strcmp(subjectEntry1->emailAddr, 
  1395.                  subjectEntry2->emailAddr) == 0) {
  1396.     /*  Found a subject using the same email address.  */
  1397.     subjectsForEmail[ns++] = i;
  1398. }
  1399.     }
  1400.     /*  Find the S/MIME entry for this email address.  */
  1401.     for (i=0; i<smime.numEntries; i++) {
  1402. smimeEntry = (certDBEntrySMime*)&smime.entries[i];
  1403. if (smimeEntry->common.arena == NULL)
  1404.     continue;
  1405. if (PORT_Strcmp(subjectEntry1->emailAddr, smimeEntry->emailAddr) == 0) {
  1406.     /*  Find which of the subjects uses this S/MIME entry.  */
  1407.     for (j=0; j<ns && *subjectWithSMime < 0; j++) {
  1408. sNum = subjectsForEmail[j];
  1409. subjectEntry2 = (certDBEntrySubject*)&subjects.entries[sNum];
  1410. if (SECITEM_ItemsAreEqual(&smimeEntry->subjectName,
  1411.                           &subjectEntry2->derSubject)) {
  1412.     /*  Found the subject corresponding to the S/MIME entry. */
  1413.     *subjectWithSMime = sNum;
  1414.     *smimeForSubject = i;
  1415. }
  1416.     }
  1417.     SEC_DestroyDBEntry((certDBEntry*)smimeEntry);
  1418.     PORT_Memset(smimeEntry, 0, sizeof(certDBEntry));
  1419.     break;
  1420. }
  1421.     }
  1422.     if (ns <= 1)
  1423. return subjectNum;
  1424.     if (userCert)
  1425. return *subjectWithSMime;
  1426.     /*  Now find which of the subjects has the newest cert.  */
  1427.     certs = (CERTCertificate**)PORT_Alloc(ns*sizeof(CERTCertificate*));
  1428.     certNums = (int*)PORT_Alloc((ns+1)*sizeof(int));
  1429.     certNums[0] = 0;
  1430.     for (i=0; i<ns; i++) {
  1431. sNum = subjectsForEmail[i];
  1432. subjectEntry1 = (certDBEntrySubject*)&subjects.entries[sNum];
  1433. certs[i] = CERT_FindCertByKey(handle, &subjectEntry1->certKeys[0]);
  1434. certNums[i+1] = i;
  1435.     }
  1436.     /*  Sort the array by validity.  */
  1437.     qsort(certs, ns, sizeof(CERTCertificate*), 
  1438.           (int (*)(const void *, const void *))certIsOlder);
  1439.     newestSubject = -1;
  1440.     for (i=0; i<ns; i++) {
  1441. sNum = subjectsForEmail[i];
  1442. subjectEntry1 = (certDBEntrySubject*)&subjects.entries[sNum];
  1443. if (SECITEM_ItemsAreEqual(&subjectEntry1->derSubject,
  1444.                           &certs[0]->derSubject))
  1445.     newestSubject = sNum;
  1446. else
  1447.     SEC_DestroyDBEntry((certDBEntry*)subjectEntry1);
  1448.     }
  1449.     if (info && userSaysDeleteCert(certs, ns, dbOlderCert, info, certNums)) {
  1450. for (i=1; i<ns+1; i++) {
  1451.     if (certNums[i] >= 0 && certNums[i] != certNums[0]) {
  1452. deleteAllEntriesForCert(handle, certs[certNums[i]], info->out);
  1453. info->dbErrors[dbOlderCert]++;
  1454.     }
  1455. }
  1456.     }
  1457.     CERT_DestroyCertArray(certs, ns);
  1458.     return newestSubject;
  1459. }
  1460. CERTCertDBHandle *
  1461. DBCK_ReconstructDBFromCerts(CERTCertDBHandle *oldhandle, char *newdbname,
  1462.                             PRFileDesc *outfile, PRBool removeExpired,
  1463.                             PRBool requireProfile, PRBool singleEntry,
  1464.                             PRBool promptUser)
  1465. {
  1466.     SECStatus rv;
  1467.     dbRestoreInfo info;
  1468.     certDBEntryContentVersion *oldContentVersion;
  1469.     certDBArray dbArray;
  1470.     int i;
  1471.     PORT_Memset(&dbArray, 0, sizeof(dbArray));
  1472.     PORT_Memset(&info, 0, sizeof(info));
  1473.     info.verbose = (outfile) ? PR_TRUE : PR_FALSE;
  1474.     info.out = (outfile) ? outfile : PR_STDOUT;
  1475.     info.removeType[dbInvalidCert] = removeExpired;
  1476.     info.removeType[dbNoSMimeProfile] = requireProfile;
  1477.     info.removeType[dbOlderCert] = singleEntry;
  1478.     info.promptUser[dbInvalidCert]  = promptUser;
  1479.     info.promptUser[dbNoSMimeProfile]  = promptUser;
  1480.     info.promptUser[dbOlderCert]  = promptUser;
  1481.     /*  Allocate a handle to fill with CERT_OpenCertDB below.  */
  1482.     info.handle = (CERTCertDBHandle *)PORT_ZAlloc(sizeof(CERTCertDBHandle));
  1483.     if (!info.handle) {
  1484. fprintf(stderr, "unable to get database handle");
  1485. return NULL;
  1486.     }
  1487.     /*  Create a certdb with the most recent set of roots.  */
  1488.     rv = CERT_OpenCertDBFilename(info.handle, newdbname, PR_FALSE);
  1489.     if (rv) {
  1490. fprintf(stderr, "could not open certificate database");
  1491. goto loser;
  1492.     }
  1493.     /*  Create certificate, subject, nickname, and email records.
  1494.      *  mcom_db seems to have a sequential access bug.  Though reads and writes
  1495.      *  should be allowed during traversal, they seem to screw up the sequence.
  1496.      *  So, stuff all the cert entries into an array, and loop over the array
  1497.      *  doing read/writes in the db.
  1498.      */
  1499.     fillDBEntryArray(oldhandle, certDBEntryTypeCert, &dbArray.certs);
  1500.     for (elem = PR_LIST_HEAD(&dbArray->certs.link);
  1501.          elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) {
  1502. node = LISTNODE_CAST(elem);
  1503. addCertToDB((certDBEntryCert*)&node->entry, &info, oldhandle);
  1504. /* entries get destroyed in addCertToDB */
  1505.     }
  1506. #if 0
  1507.     rv = SEC_TraverseDBEntries(oldhandle, certDBEntryTypeSMimeProfile, 
  1508.                                copyDBEntry, info.handle);
  1509. #endif
  1510.     /*  Fix up the pointers between (nickname|S/MIME) --> (subject).
  1511.      *  Create S/MIME entries for S/MIME certs.
  1512.      *  Have the S/MIME entry point to the last-expiring cert using
  1513.      *  an email address.
  1514.      */
  1515. #if 0
  1516.     CERT_RedoHandlesForSubjects(info.handle, singleEntry, &info);
  1517. #endif
  1518.     freeDBEntryList(&dbArray.certs.link);
  1519.     /*  Copy over the version record.  */
  1520.     /*  XXX Already exists - and _must_ be correct... */
  1521.     /*
  1522.     versionEntry = ReadDBVersionEntry(oldhandle);
  1523.     rv = WriteDBVersionEntry(info.handle, versionEntry);
  1524.     */
  1525.     /*  Copy over the content version record.  */
  1526.     /*  XXX Can probably get useful info from old content version?
  1527.      *      Was this db created before/after this tool?  etc.
  1528.      */
  1529. #if 0
  1530.     oldContentVersion = ReadDBContentVersionEntry(oldhandle);
  1531.     CERT_SetDBContentVersion(oldContentVersion->contentVersion, info.handle); 
  1532. #endif
  1533. #if 0
  1534.     /*  Copy over the CRL & KRL records.  */
  1535.     rv = SEC_TraverseDBEntries(oldhandle, certDBEntryTypeRevocation, 
  1536.                                copyDBEntry, info.handle);
  1537.     /*  XXX Only one KRL, just do db->get? */
  1538.     rv = SEC_TraverseDBEntries(oldhandle, certDBEntryTypeKeyRevocation, 
  1539.                                copyDBEntry, info.handle);
  1540. #endif
  1541.     PR_fprintf(info.out, "Database had %d certificates.n", info.nOldCerts);
  1542.     PR_fprintf(info.out, "Reconstructed %d certificates.n", info.nCerts);
  1543.     PR_fprintf(info.out, "(ax) Rejected %d expired certificates.n", 
  1544.                        info.dbErrors[dbInvalidCert]);
  1545.     PR_fprintf(info.out, "(as) Rejected %d S/MIME certificates missing a profile.n", 
  1546.                        info.dbErrors[dbNoSMimeProfile]);
  1547.     PR_fprintf(info.out, "(ar) Rejected %d certificates for which a newer certificate was found.n", 
  1548.                        info.dbErrors[dbOlderCert]);
  1549.     PR_fprintf(info.out, "     Rejected %d corrupt certificates.n", 
  1550.                        info.dbErrors[dbBadCertificate]);
  1551.     PR_fprintf(info.out, "     Rejected %d certificates which did not write to the DB.n", 
  1552.                        info.dbErrors[dbCertNotWrittenToDB]);
  1553.     if (rv)
  1554. goto loser;
  1555.     return info.handle;
  1556. loser:
  1557.     if (info.handle) 
  1558. PORT_Free(info.handle);
  1559.     return NULL;
  1560. }
  1561. #endif /* DORECOVER */
  1562. enum {
  1563.     cmd_Debug = 0,
  1564.     cmd_LongUsage,
  1565.     cmd_Recover
  1566. };
  1567. enum {
  1568.     opt_KeepAll = 0,
  1569.     opt_CertDir,
  1570.     opt_Dumpfile,
  1571.     opt_InputDB,
  1572.     opt_OutputDB,
  1573.     opt_Mailfile,
  1574.     opt_Prompt,
  1575.     opt_KeepRedundant,
  1576.     opt_KeepNoSMimeProfile,
  1577.     opt_Verbose,
  1578.     opt_KeepExpired
  1579. };
  1580. static secuCommandFlag dbck_commands[] =
  1581. {
  1582.     { /* cmd_Debug,    */  'D', PR_FALSE, 0, PR_FALSE },
  1583.     { /* cmd_LongUsage,*/  'H', PR_FALSE, 0, PR_FALSE },
  1584.     { /* cmd_Recover,  */  'R', PR_FALSE, 0, PR_FALSE }
  1585. };
  1586. static secuCommandFlag dbck_options[] =
  1587. {
  1588.     { /* opt_KeepAll,           */  'a', PR_FALSE, 0, PR_FALSE },
  1589.     { /* opt_CertDir,           */  'd', PR_TRUE,  0, PR_FALSE },
  1590.     { /* opt_Dumpfile,          */  'f', PR_TRUE,  0, PR_FALSE },
  1591.     { /* opt_InputDB,           */  'i', PR_TRUE,  0, PR_FALSE },
  1592.     { /* opt_OutputDB,          */  'o', PR_TRUE,  0, PR_FALSE },
  1593.     { /* opt_Mailfile,          */  'm', PR_FALSE, 0, PR_FALSE },
  1594.     { /* opt_Prompt,            */  'p', PR_FALSE, 0, PR_FALSE },
  1595.     { /* opt_KeepRedundant,     */  'r', PR_FALSE, 0, PR_FALSE },
  1596.     { /* opt_KeepNoSMimeProfile,*/  's', PR_FALSE, 0, PR_FALSE },
  1597.     { /* opt_Verbose,           */  'v', PR_FALSE, 0, PR_FALSE },
  1598.     { /* opt_KeepExpired,       */  'x', PR_FALSE, 0, PR_FALSE }
  1599. };
  1600. int 
  1601. main(int argc, char **argv)
  1602. {
  1603.     CERTCertDBHandle *certHandle;
  1604.     PRFileInfo fileInfo;
  1605.     PRFileDesc *mailfile = NULL;
  1606.     PRFileDesc *dumpfile = NULL;
  1607.     char * pathname     = 0;
  1608.     char * fullname     = 0;
  1609.     char * newdbname    = 0;
  1610.     PRBool removeExpired, requireProfile, singleEntry;
  1611.     
  1612.     SECStatus rv;
  1613.     secuCommand dbck;
  1614.     dbck.numCommands = sizeof(dbck_commands) / sizeof(secuCommandFlag);
  1615.     dbck.numOptions = sizeof(dbck_options) / sizeof(secuCommandFlag);
  1616.     dbck.commands = dbck_commands;
  1617.     dbck.options = dbck_options;
  1618.     progName = strrchr(argv[0], '/');
  1619.     progName = progName ? progName+1 : argv[0];
  1620.     rv = SECU_ParseCommandLine(argc, argv, progName, &dbck);
  1621.     if (rv != SECSuccess)
  1622. Usage(progName);
  1623.     if (dbck.commands[cmd_LongUsage].activated)
  1624. LongUsage(progName);
  1625.     if (!dbck.commands[cmd_Debug].activated &&
  1626.         !dbck.commands[cmd_Recover].activated) {
  1627. PR_fprintf(PR_STDERR, "Please specify -D or -R.n");
  1628. Usage(progName);
  1629.     }
  1630.     removeExpired = !(dbck.options[opt_KeepAll].activated ||
  1631.                       dbck.options[opt_KeepExpired].activated);
  1632.     requireProfile = !(dbck.options[opt_KeepAll].activated ||
  1633.                     dbck.options[opt_KeepNoSMimeProfile].activated);
  1634.     singleEntry = !(dbck.options[opt_KeepAll].activated ||
  1635.                     dbck.options[opt_KeepRedundant].activated);
  1636.     if (dbck.options[opt_OutputDB].activated) {
  1637. newdbname = PL_strdup(dbck.options[opt_OutputDB].arg);
  1638.     } else {
  1639. newdbname = PL_strdup("new_cert7.db");
  1640.     }
  1641.     /*  Create a generic graph of the database.  */
  1642.     if (dbck.options[opt_Mailfile].activated) {
  1643. mailfile = PR_Open("./mailfile", PR_RDWR | PR_CREATE_FILE, 00660);
  1644. if (!mailfile) {
  1645.     fprintf(stderr, "Unable to create mailfile.n");
  1646.     return -1;
  1647. }
  1648.     }
  1649.     /*  Dump all debugging info while running.  */
  1650.     if (dbck.options[opt_Verbose].activated) {
  1651. if (dbck.options[opt_Dumpfile].activated) {
  1652.     dumpfile = PR_Open(dbck.options[opt_Dumpfile].arg,
  1653.                        PR_RDWR | PR_CREATE_FILE, 00660);
  1654. }
  1655. if (!dumpfile) {
  1656.     fprintf(stderr, "Unable to create dumpfile.n");
  1657.     return -1;
  1658. }
  1659.     }
  1660.     /*  Set the cert database directory.  */
  1661.     if (dbck.options[opt_CertDir].activated) {
  1662. SECU_ConfigDirectory(dbck.options[opt_CertDir].arg);
  1663.     }
  1664.     PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
  1665.     SEC_Init();
  1666.     certHandle = (CERTCertDBHandle *)PORT_ZAlloc(sizeof(CERTCertDBHandle));
  1667.     if (!certHandle) {
  1668. SECU_PrintError(progName, "unable to get database handle");
  1669. return -1;
  1670.     }
  1671.     /*  Open the possibly corrupt database.  */
  1672.     if (dbck.options[opt_InputDB].activated) {
  1673. pathname = SECU_ConfigDirectory(NULL);
  1674. fullname = PR_smprintf("%s/%s", pathname, 
  1675.                                 dbck.options[opt_InputDB].arg);
  1676. if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
  1677.     fprintf(stderr, "Unable to read file "%s".n", fullname);
  1678.     return -1;
  1679. }
  1680. rv = CERT_OpenCertDBFilename(certHandle, fullname, PR_TRUE);
  1681.     } else {
  1682. /*  Use the default.  */
  1683. fullname = SECU_CertDBNameCallback(NULL, CERT_DB_FILE_VERSION);
  1684. if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
  1685.     fprintf(stderr, "Unable to read file "%s".n", fullname);
  1686.     return -1;
  1687. }
  1688. rv = CERT_OpenCertDB(certHandle, PR_TRUE, 
  1689.                      SECU_CertDBNameCallback, NULL);
  1690.     }
  1691.     if (rv) {
  1692. SECU_PrintError(progName, "unable to open cert database");
  1693. return -1;
  1694.     }
  1695.     if (dbck.commands[cmd_Debug].activated) {
  1696. DBCK_DebugDB(certHandle, dumpfile, mailfile);
  1697. return 0;
  1698.     }
  1699. #ifdef DORECOVER
  1700.     if (dbck.commands[cmd_Recover].activated) {
  1701. DBCK_ReconstructDBFromCerts(certHandle, newdbname,
  1702.                             dumpfile, removeExpired, 
  1703.                             requireProfile, singleEntry, 
  1704.                             dbck.options[opt_Prompt].activated);
  1705. return 0;
  1706.     }
  1707. #endif
  1708.     if (mailfile)
  1709. PR_Close(mailfile);
  1710.     if (dumpfile)
  1711. PR_Close(dumpfile);
  1712.     if (certHandle) {
  1713. CERT_ClosePermCertDB(certHandle);
  1714. PORT_Free(certHandle);
  1715.     }
  1716.     return -1;
  1717. }