~ubuntu-branches/ubuntu/lucid/seamonkey/lucid-security

« back to all changes in this revision

Viewing changes to security/nss-fips/cmd/dbck/dbck.c

  • Committer: Bazaar Package Importer
  • Author(s): Fabien Tassin
  • Date: 2008-07-29 21:29:02 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20080729212902-spm9kpvchp9udwbw
Tags: 1.1.11+nobinonly-0ubuntu1
* New security upstream release: 1.1.11 (LP: #218534)
  Fixes USN-602-1, USN-619-1, USN-623-1 and USN-629-1
* Refresh diverged patch:
  - update debian/patches/80_security_build.patch
* Fix FTBFS with missing -lfontconfig
  - add debian/patches/11_fix_ftbfs_with_fontconfig.patch
  - update debian/patches/series
* Build with default gcc (hardy: 4.2, intrepid: 4.3)
  - update debian/rules
  - update debian/control

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ***** BEGIN LICENSE BLOCK *****
 
2
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
3
 *
 
4
 * The contents of this file are subject to the Mozilla Public License Version
 
5
 * 1.1 (the "License"); you may not use this file except in compliance with
 
6
 * the License. You may obtain a copy of the License at
 
7
 * http://www.mozilla.org/MPL/
 
8
 *
 
9
 * Software distributed under the License is distributed on an "AS IS" basis,
 
10
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
11
 * for the specific language governing rights and limitations under the
 
12
 * License.
 
13
 *
 
14
 * The Original Code is the Netscape security libraries.
 
15
 *
 
16
 * The Initial Developer of the Original Code is
 
17
 * Netscape Communications Corporation.
 
18
 * Portions created by the Initial Developer are Copyright (C) 1994-2000
 
19
 * the Initial Developer. All Rights Reserved.
 
20
 *
 
21
 * Contributor(s):
 
22
 *
 
23
 * Alternatively, the contents of this file may be used under the terms of
 
24
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 
25
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
26
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
27
 * of those above. If you wish to allow use of your version of this file only
 
28
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
29
 * use your version of this file under the terms of the MPL, indicate your
 
30
 * decision by deleting the provisions above and replace them with the notice
 
31
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
32
 * the provisions above, a recipient may use your version of this file under
 
33
 * the terms of any one of the MPL, the GPL or the LGPL.
 
34
 *
 
35
 * ***** END LICENSE BLOCK ***** */
 
36
 
 
37
/*
 
38
** dbck.c
 
39
**
 
40
** utility for fixing corrupt cert databases
 
41
**
 
42
*/
 
43
#include <stdio.h>
 
44
#include <string.h>
 
45
 
 
46
#include "secutil.h"
 
47
#include "cdbhdl.h"
 
48
#include "certdb.h"
 
49
#include "cert.h"
 
50
#include "nspr.h"
 
51
#include "prtypes.h"
 
52
#include "prtime.h"
 
53
#include "prlong.h"
 
54
#include "pcert.h"
 
55
#include "nss.h"
 
56
 
 
57
static char *progName;
 
58
 
 
59
/* placeholders for pointer error types */
 
60
static void *WrongEntry;
 
61
static void *NoNickname;
 
62
static void *NoSMime;
 
63
 
 
64
typedef enum {
 
65
/* 0*/ NoSubjectForCert = 0,
 
66
/* 1*/ SubjectHasNoKeyForCert,
 
67
/* 2*/ NoNicknameOrSMimeForSubject,
 
68
/* 3*/ WrongNicknameForSubject,
 
69
/* 4*/ NoNicknameEntry,
 
70
/* 5*/ WrongSMimeForSubject,
 
71
/* 6*/ NoSMimeEntry,
 
72
/* 7*/ NoSubjectForNickname,
 
73
/* 8*/ NoSubjectForSMime,
 
74
/* 9*/ NicknameAndSMimeEntries,
 
75
    NUM_ERROR_TYPES
 
76
} dbErrorType;
 
77
 
 
78
static char *dbErrorString[NUM_ERROR_TYPES] = {
 
79
/* 0*/ "<CERT ENTRY>\nDid not find a subject entry for this certificate.",
 
80
/* 1*/ "<SUBJECT ENTRY>\nSubject has certKey which is not in db.",
 
81
/* 2*/ "<SUBJECT ENTRY>\nSubject does not have a nickname or email address.",
 
82
/* 3*/ "<SUBJECT ENTRY>\nUsing this subject's nickname, found a nickname entry for a different subject.",
 
83
/* 4*/ "<SUBJECT ENTRY>\nDid not find a nickname entry for this subject.",
 
84
/* 5*/ "<SUBJECT ENTRY>\nUsing this subject's email, found an S/MIME entry for a different subject.",
 
85
/* 6*/ "<SUBJECT ENTRY>\nDid not find an S/MIME entry for this subject.",
 
86
/* 7*/ "<NICKNAME ENTRY>\nDid not find a subject entry for this nickname.",
 
87
/* 8*/ "<S/MIME ENTRY>\nDid not find a subject entry for this S/MIME profile.",
 
88
};
 
89
 
 
90
static char *errResult[NUM_ERROR_TYPES] = {
 
91
    "Certificate entries that had no subject entry.", 
 
92
    "Subject entries with no corresponding Certificate entries.", 
 
93
    "Subject entries that had no nickname or S/MIME entries.",
 
94
    "Redundant nicknames (subjects with the same nickname).",
 
95
    "Subject entries that had no nickname entry.",
 
96
    "Redundant email addresses (subjects with the same email address).",
 
97
    "Subject entries that had no S/MIME entry.",
 
98
    "Nickname entries that had no subject entry.", 
 
99
    "S/MIME entries that had no subject entry.",
 
100
    "Subject entries with BOTH nickname and S/MIME entries."
 
101
};
 
102
 
 
103
 
 
104
enum {
 
105
    GOBOTH = 0,
 
106
    GORIGHT,
 
107
    GOLEFT
 
108
};
 
109
 
 
110
typedef struct
 
111
{
 
112
    PRBool verbose;
 
113
    PRBool dograph;
 
114
    PRFileDesc *out;
 
115
    PRFileDesc *graphfile;
 
116
    int dbErrors[NUM_ERROR_TYPES];
 
117
} dbDebugInfo;
 
118
 
 
119
struct certDBEntryListNodeStr {
 
120
    PRCList link;
 
121
    certDBEntry entry;
 
122
    void *appData;
 
123
};
 
124
typedef struct certDBEntryListNodeStr  certDBEntryListNode;
 
125
 
 
126
/*
 
127
 * A list node for a cert db entry.  The index is a unique identifier
 
128
 * to use for creating generic maps of a db.  This struct handles
 
129
 * the cert, nickname, and smime db entry types, as all three have a
 
130
 * single handle to a subject entry.
 
131
 * This structure is pointed to by certDBEntryListNode->appData.
 
132
 */
 
133
typedef struct 
 
134
{
 
135
    PRArenaPool *arena;
 
136
    int index;
 
137
    certDBEntryListNode *pSubject;
 
138
} certDBEntryMap;
 
139
 
 
140
/*
 
141
 * Subject entry is special case, it has bidirectional handles.  One
 
142
 * subject entry can point to several certs (using the same DN), and
 
143
 * a nickname and/or smime entry.
 
144
 * This structure is pointed to by certDBEntryListNode->appData.
 
145
 */
 
146
typedef struct
 
147
{
 
148
    PRArenaPool *arena;
 
149
    int index;
 
150
    int numCerts;
 
151
    certDBEntryListNode **pCerts;
 
152
    certDBEntryListNode *pNickname;
 
153
    certDBEntryListNode *pSMime;
 
154
} certDBSubjectEntryMap;
 
155
 
 
156
/*
 
157
 * A map of a certdb.
 
158
 */
 
159
typedef struct
 
160
{
 
161
    int numCerts;
 
162
    int numSubjects;
 
163
    int numNicknames;
 
164
    int numSMime;
 
165
    int numRevocation;
 
166
    certDBEntryListNode certs;      /* pointer to head of cert list */
 
167
    certDBEntryListNode subjects;   /* pointer to head of subject list */
 
168
    certDBEntryListNode nicknames;  /* pointer to head of nickname list */
 
169
    certDBEntryListNode smime;      /* pointer to head of smime list */
 
170
    certDBEntryListNode revocation; /* pointer to head of revocation list */
 
171
} certDBArray;
 
172
 
 
173
/* Cast list to the base element, a certDBEntryListNode. */
 
174
#define LISTNODE_CAST(node) \
 
175
    ((certDBEntryListNode *)(node))
 
176
 
 
177
static void 
 
178
Usage(char *progName)
 
179
{
 
180
#define FPS fprintf(stderr, 
 
181
    FPS "Type %s -H for more detailed descriptions\n", progName);
 
182
    FPS "Usage:  %s -D [-d certdir] [-m] [-v [-f dumpfile]]\n", 
 
183
        progName);
 
184
#ifdef DORECOVER
 
185
    FPS "        %s -R -o newdbname [-d certdir] [-aprsx] [-v [-f dumpfile]]\n", 
 
186
        progName);
 
187
#endif
 
188
    exit(-1);
 
189
}
 
190
 
 
191
static void
 
192
LongUsage(char *progName)
 
193
{
 
194
    FPS "%-15s Display this help message.\n",
 
195
        "-H");
 
196
    FPS "%-15s Dump analysis.  No changes will be made to the database.\n",
 
197
        "-D");
 
198
    FPS "%-15s Cert database directory (default is ~/.netscape)\n",
 
199
        "   -d certdir");
 
200
    FPS "%-15s Put database graph in ./mailfile (default is stdout).\n",
 
201
        "   -m");
 
202
    FPS "%-15s Verbose mode.  Dumps the entire contents of your cert8.db.\n",
 
203
        "   -v");
 
204
    FPS "%-15s File to dump verbose output into. (default is stdout)\n",
 
205
        "   -f dumpfile");
 
206
#ifdef DORECOVER
 
207
    FPS "%-15s Repair the database.  The program will look for broken\n",
 
208
        "-R");
 
209
    FPS "%-15s dependencies between subject entries and certificates,\n",
 
210
        "");
 
211
    FPS "%-15s between nickname entries and subjects, and between SMIME\n",
 
212
        "");
 
213
    FPS "%-15s profiles and subjects.  Any duplicate entries will be\n",
 
214
        "");
 
215
    FPS "%-15s removed, any missing entries will be created.\n",
 
216
        "");
 
217
    FPS "%-15s File to store new database in (default is new_cert8.db)\n",
 
218
        "   -o newdbname");
 
219
    FPS "%-15s Cert database directory (default is ~/.netscape)\n",
 
220
        "   -d certdir");
 
221
    FPS "%-15s Prompt before removing any certificates.\n",
 
222
        "   -p");
 
223
    FPS "%-15s Keep all possible certificates.  Only remove certificates\n",
 
224
        "   -a");
 
225
    FPS "%-15s which prevent creation of a consistent database.  Thus any\n",
 
226
        "");
 
227
    FPS "%-15s expired or redundant entries will be kept.\n",
 
228
        "");
 
229
    FPS "%-15s Keep redundant nickname/email entries.  It is possible\n",
 
230
        "   -r");
 
231
    FPS "%-15s only one such entry will be usable.\n",
 
232
        "");
 
233
    FPS "%-15s Don't require an S/MIME profile in order to keep an S/MIME\n",
 
234
        "   -s");
 
235
    FPS "%-15s cert.  An empty profile will be created.\n",
 
236
        "");
 
237
    FPS "%-15s Keep expired certificates.\n",
 
238
        "   -x");
 
239
    FPS "%-15s Verbose mode - report all activity while recovering db.\n",
 
240
        "   -v");
 
241
    FPS "%-15s File to dump verbose output into.\n",
 
242
        "   -f dumpfile");
 
243
    FPS "\n");
 
244
#endif
 
245
    exit(-1);
 
246
#undef FPS
 
247
}
 
248
 
 
249
/*******************************************************************
 
250
 *
 
251
 *  Functions for dbck.
 
252
 *
 
253
 ******************************************************************/
 
254
 
 
255
void
 
256
printHexString(PRFileDesc *out, SECItem *hexval)
 
257
{
 
258
    unsigned int i;
 
259
    for (i = 0; i < hexval->len; i++) {
 
260
        if (i != hexval->len - 1) {
 
261
            PR_fprintf(out, "%02x:", hexval->data[i]);
 
262
        } else {
 
263
            PR_fprintf(out, "%02x", hexval->data[i]);
 
264
        }
 
265
    }
 
266
    PR_fprintf(out, "\n");
 
267
}
 
268
 
 
269
 
 
270
SECStatus
 
271
dumpCertificate(CERTCertificate *cert, int num, PRFileDesc *outfile)
 
272
{
 
273
    int userCert = 0;
 
274
    CERTCertTrust *trust = cert->trust;
 
275
    userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
 
276
               (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
 
277
               (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
 
278
    if (num >= 0) {
 
279
        PR_fprintf(outfile, "Certificate: %3d\n", num);
 
280
    } else {
 
281
        PR_fprintf(outfile, "Certificate:\n");
 
282
    }
 
283
    PR_fprintf(outfile, "----------------\n");
 
284
    if (userCert)
 
285
        PR_fprintf(outfile, "(User Cert)\n");
 
286
    PR_fprintf(outfile, "## SUBJECT:  %s\n", cert->subjectName);
 
287
    PR_fprintf(outfile, "## ISSUER:  %s\n", cert->issuerName);
 
288
    PR_fprintf(outfile, "## SERIAL NUMBER:  ");
 
289
    printHexString(outfile, &cert->serialNumber);
 
290
    {  /*  XXX should be separate function.  */
 
291
        int64 timeBefore, timeAfter;
 
292
        PRExplodedTime beforePrintable, afterPrintable;
 
293
        char *beforestr, *afterstr;
 
294
        DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore);
 
295
        DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter);
 
296
        PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable);
 
297
        PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable);
 
298
        beforestr = PORT_Alloc(100);
 
299
        afterstr = PORT_Alloc(100);
 
300
        PR_FormatTime(beforestr, 100, "%a %b %d %H:%M:%S %Y", &beforePrintable);
 
301
        PR_FormatTime(afterstr, 100, "%a %b %d %H:%M:%S %Y", &afterPrintable);
 
302
        PR_fprintf(outfile, "## VALIDITY:  %s to %s\n", beforestr, afterstr);
 
303
    }
 
304
    PR_fprintf(outfile, "\n");
 
305
    return SECSuccess;
 
306
}
 
307
 
 
308
SECStatus
 
309
dumpCertEntry(certDBEntryCert *entry, int num, PRFileDesc *outfile)
 
310
{
 
311
#if 0
 
312
    NSSLOWCERTCertificate *cert;
 
313
    /* should we check for existing duplicates? */
 
314
    cert = nsslowcert_DecodeDERCertificate(&entry->cert.derCert, 
 
315
                                            entry->cert.nickname);
 
316
#else
 
317
    CERTCertificate *cert;
 
318
    cert = CERT_DecodeDERCertificate(&entry->derCert, PR_FALSE, NULL);
 
319
#endif
 
320
    if (!cert) {
 
321
        fprintf(stderr, "Failed to decode certificate.\n");
 
322
        return SECFailure;
 
323
    }
 
324
    cert->trust = (CERTCertTrust *)&entry->trust;
 
325
    dumpCertificate(cert, num, outfile);
 
326
    CERT_DestroyCertificate(cert);
 
327
    return SECSuccess;
 
328
}
 
329
 
 
330
SECStatus
 
331
dumpSubjectEntry(certDBEntrySubject *entry, int num, PRFileDesc *outfile)
 
332
{
 
333
    char *subjectName = CERT_DerNameToAscii(&entry->derSubject);
 
334
 
 
335
    PR_fprintf(outfile, "Subject: %3d\n", num);
 
336
    PR_fprintf(outfile, "------------\n");
 
337
    PR_fprintf(outfile, "## %s\n", subjectName);
 
338
    if (entry->nickname)
 
339
        PR_fprintf(outfile, "## Subject nickname:  %s\n", entry->nickname);
 
340
    if (entry->emailAddrs) {
 
341
        unsigned int n;
 
342
        for (n = 0; n < entry->nemailAddrs && entry->emailAddrs[n]; ++n) {
 
343
            char * emailAddr = entry->emailAddrs[n];
 
344
            if (emailAddr[0]) {
 
345
                PR_fprintf(outfile, "## Subject email address:  %s\n", 
 
346
                   emailAddr);
 
347
            }
 
348
        }
 
349
    }
 
350
    PR_fprintf(outfile, "## This subject has %d cert(s).\n", entry->ncerts);
 
351
    PR_fprintf(outfile, "\n");
 
352
    PORT_Free(subjectName);
 
353
    return SECSuccess;
 
354
}
 
355
 
 
356
SECStatus
 
357
dumpNicknameEntry(certDBEntryNickname *entry, int num, PRFileDesc *outfile)
 
358
{
 
359
    PR_fprintf(outfile, "Nickname: %3d\n", num);
 
360
    PR_fprintf(outfile, "-------------\n");
 
361
    PR_fprintf(outfile, "##  \"%s\"\n\n", entry->nickname);
 
362
    return SECSuccess;
 
363
}
 
364
 
 
365
SECStatus
 
366
dumpSMimeEntry(certDBEntrySMime *entry, int num, PRFileDesc *outfile)
 
367
{
 
368
    PR_fprintf(outfile, "S/MIME Profile: %3d\n", num);
 
369
    PR_fprintf(outfile, "-------------------\n");
 
370
    PR_fprintf(outfile, "##  \"%s\"\n", entry->emailAddr);
 
371
#ifdef OLDWAY
 
372
    PR_fprintf(outfile, "##  OPTIONS:  ");
 
373
    printHexString(outfile, &entry->smimeOptions);
 
374
    PR_fprintf(outfile, "##  TIMESTAMP:  ");
 
375
    printHexString(outfile, &entry->optionsDate);
 
376
#else
 
377
    SECU_PrintAny(stdout, &entry->smimeOptions, "##  OPTIONS  ", 0);
 
378
    fflush(stdout);
 
379
    if (entry->optionsDate.len && entry->optionsDate.data)
 
380
        PR_fprintf(outfile, "##  TIMESTAMP: %.*s\n", 
 
381
                   entry->optionsDate.len, entry->optionsDate.data);
 
382
#endif
 
383
    PR_fprintf(outfile, "\n");
 
384
    return SECSuccess;
 
385
}
 
386
 
 
387
SECStatus
 
388
mapCertEntries(certDBArray *dbArray)
 
389
{
 
390
    certDBEntryCert *certEntry;
 
391
    certDBEntrySubject *subjectEntry;
 
392
    certDBEntryListNode *certNode, *subjNode;
 
393
    certDBSubjectEntryMap *smap;
 
394
    certDBEntryMap *map;
 
395
    PRArenaPool *tmparena;
 
396
    SECItem derSubject;
 
397
    SECItem certKey;
 
398
    PRCList *cElem, *sElem;
 
399
 
 
400
    /* Arena for decoded entries */
 
401
    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
 
402
    if (tmparena == NULL) {
 
403
        PORT_SetError(SEC_ERROR_NO_MEMORY);
 
404
        return SECFailure;
 
405
    }
 
406
 
 
407
    /* Iterate over cert entries and map them to subject entries. 
 
408
     * NOTE: mapSubjectEntries must be called first to alloc memory
 
409
     * for array of subject->cert map.
 
410
     */
 
411
    for (cElem = PR_LIST_HEAD(&dbArray->certs.link); 
 
412
         cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
 
413
        certNode = LISTNODE_CAST(cElem);
 
414
        certEntry = (certDBEntryCert *)&certNode->entry;
 
415
        map = (certDBEntryMap *)certNode->appData;
 
416
        CERT_NameFromDERCert(&certEntry->derCert, &derSubject);
 
417
        CERT_KeyFromDERCert(tmparena, &certEntry->derCert, &certKey);
 
418
        /*  Loop over found subjects for cert's DN.  */
 
419
        for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
 
420
             sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
 
421
            subjNode = LISTNODE_CAST(sElem);
 
422
            subjectEntry = (certDBEntrySubject *)&subjNode->entry;
 
423
            if (SECITEM_ItemsAreEqual(&derSubject, &subjectEntry->derSubject)) {
 
424
                unsigned int i;
 
425
                /*  Found matching subject name, create link.  */
 
426
                map->pSubject = subjNode;
 
427
                /*  Make sure subject entry has cert's key.  */
 
428
                for (i=0; i<subjectEntry->ncerts; i++) {
 
429
                    if (SECITEM_ItemsAreEqual(&certKey,
 
430
                                              &subjectEntry->certKeys[i])) {
 
431
                        /*  Found matching cert key.  */
 
432
                        smap = (certDBSubjectEntryMap *)subjNode->appData;
 
433
                        smap->pCerts[i] = certNode;
 
434
                        break;
 
435
                    }
 
436
                }
 
437
            }
 
438
        }
 
439
    }
 
440
    PORT_FreeArena(tmparena, PR_FALSE);
 
441
    return SECSuccess;
 
442
}
 
443
 
 
444
SECStatus
 
445
mapSubjectEntries(certDBArray *dbArray)
 
446
{
 
447
    certDBEntrySubject *subjectEntry;
 
448
    certDBEntryListNode *subjNode;
 
449
    certDBSubjectEntryMap *subjMap;
 
450
    PRCList *sElem;
 
451
 
 
452
    for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
 
453
         sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
 
454
        /* Iterate over subject entries and map subjects to nickname
 
455
         * and smime entries.  The cert<->subject map will be handled
 
456
         * by a subsequent call to mapCertEntries.
 
457
         */
 
458
        subjNode = LISTNODE_CAST(sElem);
 
459
        subjectEntry = (certDBEntrySubject *)&subjNode->entry;
 
460
        subjMap = (certDBSubjectEntryMap *)subjNode->appData;
 
461
        /* need to alloc memory here for array of matching certs. */
 
462
        subjMap->pCerts = PORT_ArenaAlloc(subjMap->arena, 
 
463
                                          subjectEntry->ncerts*sizeof(int));
 
464
        subjMap->numCerts = subjectEntry->ncerts;
 
465
        subjMap->pNickname = NoNickname;
 
466
        subjMap->pSMime = NoSMime;
 
467
 
 
468
        if (subjectEntry->nickname) {
 
469
            /* Subject should have a nickname entry, so create a link. */
 
470
            PRCList *nElem;
 
471
            for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
 
472
                 nElem != &dbArray->nicknames.link; 
 
473
                 nElem = PR_NEXT_LINK(nElem)) {
 
474
                certDBEntryListNode *nickNode;
 
475
                certDBEntryNickname *nicknameEntry;
 
476
                /*  Look for subject's nickname in nickname entries.  */
 
477
                nickNode = LISTNODE_CAST(nElem);
 
478
                nicknameEntry = (certDBEntryNickname *)&nickNode->entry;
 
479
                if (PL_strcmp(subjectEntry->nickname, 
 
480
                              nicknameEntry->nickname) == 0) {
 
481
                    /*  Found a nickname entry for subject's nickname.  */
 
482
                    if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
 
483
                                              &nicknameEntry->subjectName)) {
 
484
                        certDBEntryMap *nickMap;
 
485
                        nickMap = (certDBEntryMap *)nickNode->appData;
 
486
                        /*  Nickname and subject match.  */
 
487
                        subjMap->pNickname = nickNode;
 
488
                        nickMap->pSubject = subjNode;
 
489
                    } else if (subjMap->pNickname == NoNickname) {
 
490
                        /*  Nickname entry found is for diff. subject.  */
 
491
                        subjMap->pNickname = WrongEntry;
 
492
                    }
 
493
                }
 
494
            }
 
495
        }
 
496
        if (subjectEntry->emailAddrs) {
 
497
            unsigned int n;
 
498
            for (n = 0; n < subjectEntry->nemailAddrs && 
 
499
                        subjectEntry->emailAddrs[n]; ++n) {
 
500
                char * emailAddr = subjectEntry->emailAddrs[n];
 
501
                if (emailAddr[0]) {
 
502
                    PRCList *mElem;
 
503
                    /* Subject should have an smime entry, so create a link. */
 
504
                    for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
 
505
                         mElem != &dbArray->smime.link; 
 
506
                         mElem = PR_NEXT_LINK(mElem)) {
 
507
                        certDBEntryListNode *smimeNode;
 
508
                        certDBEntrySMime *smimeEntry;
 
509
                        /*  Look for subject's email in S/MIME entries.  */
 
510
                        smimeNode = LISTNODE_CAST(mElem);
 
511
                        smimeEntry = (certDBEntrySMime *)&smimeNode->entry;
 
512
                        if (PL_strcmp(emailAddr, 
 
513
                                      smimeEntry->emailAddr) == 0) {
 
514
                            /*  Found a S/MIME entry for subject's email.  */
 
515
                            if (SECITEM_ItemsAreEqual(
 
516
                                        &subjectEntry->derSubject,
 
517
                                        &smimeEntry->subjectName)) {
 
518
                                certDBEntryMap *smimeMap;
 
519
                                /*  S/MIME entry and subject match.  */
 
520
                                subjMap->pSMime = smimeNode;
 
521
                                smimeMap = (certDBEntryMap *)smimeNode->appData;
 
522
                                smimeMap->pSubject = subjNode;
 
523
                            } else if (subjMap->pSMime == NoSMime) {
 
524
                                /*  S/MIME entry found is for diff. subject.  */
 
525
                                subjMap->pSMime = WrongEntry;
 
526
                            }
 
527
                        }
 
528
                    }   /* end for */
 
529
                }   /* endif (emailAddr[0]) */
 
530
            }   /* end for */
 
531
        }   /* endif (subjectEntry->emailAddrs) */
 
532
    }
 
533
    return SECSuccess;
 
534
}
 
535
 
 
536
void
 
537
printnode(dbDebugInfo *info, const char *str, int num)
 
538
{
 
539
    if (!info->dograph)
 
540
        return;
 
541
    if (num < 0) {
 
542
        PR_fprintf(info->graphfile, str);
 
543
    } else {
 
544
        PR_fprintf(info->graphfile, str, num);
 
545
    }
 
546
}
 
547
 
 
548
PRBool
 
549
map_handle_is_ok(dbDebugInfo *info, void *mapPtr, int indent)
 
550
{
 
551
    if (mapPtr == NULL) {
 
552
        if (indent > 0)
 
553
            printnode(info, "                ", -1);
 
554
        if (indent >= 0)
 
555
            printnode(info, "******************* ", -1);
 
556
        return PR_FALSE;
 
557
    } else if (mapPtr == WrongEntry) {
 
558
        if (indent > 0)
 
559
            printnode(info, "                  ", -1);
 
560
        if (indent >= 0)
 
561
            printnode(info, "??????????????????? ", -1);
 
562
        return PR_FALSE;
 
563
    } else {
 
564
        return PR_TRUE;
 
565
    }
 
566
}
 
567
 
 
568
/* these call each other */
 
569
void print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, 
 
570
                       int direction);
 
571
void print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, 
 
572
                          int direction);
 
573
void print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 
 
574
                         int direction, int optindex, int opttype);
 
575
void print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, 
 
576
                      int direction);
 
577
 
 
578
/* Given an smime entry, print its unique identifier.  If GOLEFT is 
 
579
 * specified, print the cert<-subject<-smime map, else just print
 
580
 * the smime entry.
 
581
 */
 
582
void
 
583
print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, int direction)
 
584
{
 
585
    certDBSubjectEntryMap *subjMap;
 
586
    certDBEntryListNode *subjNode;
 
587
    if (direction == GOLEFT) {
 
588
        /* Need to output subject and cert first, see print_subject_graph */
 
589
        subjNode = smimeMap->pSubject;
 
590
        if (map_handle_is_ok(info, (void *)subjNode, 1)) {
 
591
            subjMap = (certDBSubjectEntryMap *)subjNode->appData; 
 
592
            print_subject_graph(info, subjMap, GOLEFT,
 
593
                                smimeMap->index, certDBEntryTypeSMimeProfile);
 
594
        } else {
 
595
            printnode(info, "<---- S/MIME   %5d   ", smimeMap->index);
 
596
            info->dbErrors[NoSubjectForSMime]++;
 
597
        }
 
598
    } else {
 
599
        printnode(info, "S/MIME   %5d   ", smimeMap->index);
 
600
    }
 
601
}
 
602
 
 
603
/* Given a nickname entry, print its unique identifier.  If GOLEFT is 
 
604
 * specified, print the cert<-subject<-nickname map, else just print
 
605
 * the nickname entry.
 
606
 */
 
607
void
 
608
print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, int direction)
 
609
{
 
610
    certDBSubjectEntryMap *subjMap;
 
611
    certDBEntryListNode *subjNode;
 
612
    if (direction == GOLEFT) {
 
613
        /* Need to output subject and cert first, see print_subject_graph */
 
614
        subjNode = nickMap->pSubject;
 
615
        if (map_handle_is_ok(info, (void *)subjNode, 1)) {
 
616
            subjMap = (certDBSubjectEntryMap *)subjNode->appData;
 
617
            print_subject_graph(info, subjMap, GOLEFT,
 
618
                                nickMap->index, certDBEntryTypeNickname);
 
619
        } else {
 
620
            printnode(info, "<---- Nickname %5d   ", nickMap->index);
 
621
            info->dbErrors[NoSubjectForNickname]++;
 
622
        }
 
623
    } else {
 
624
        printnode(info, "Nickname %5d   ", nickMap->index);
 
625
    }
 
626
}
 
627
 
 
628
/* Given a subject entry, if going right print the graph of the nickname|smime
 
629
 * that it maps to (by its unique identifier); and if going left
 
630
 * print the list of certs that it points to.
 
631
 */
 
632
void
 
633
print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 
 
634
                    int direction, int optindex, int opttype)
 
635
{
 
636
    certDBEntryMap *map;
 
637
    certDBEntryListNode *node;
 
638
    int i;
 
639
    /* The first line of output always contains the cert id, subject id,
 
640
     * and nickname|smime id.  Subsequent lines may contain additional
 
641
     * cert id's for the subject if going left or both directions.
 
642
     * Ex. of printing the graph for a subject entry:
 
643
     * Cert 3 <- Subject 5 -> Nickname 32
 
644
     * Cert 8 /
 
645
     * Cert 9 /
 
646
     * means subject 5 has 3 certs, 3, 8, and 9, and corresponds
 
647
     * to nickname entry 32.
 
648
     * To accomplish the above, it is required to dump the entire first
 
649
     * line left-to-right, regardless of the input direction, and then
 
650
     * finish up any remaining cert entries.  Hence the code is uglier
 
651
     * than one may expect.
 
652
     */
 
653
    if (direction == GOLEFT || direction == GOBOTH) {
 
654
        /* In this case, nothing should be output until the first cert is
 
655
         * located and output (cert 3 in the above example).
 
656
         */
 
657
        if (subjMap->numCerts == 0 || subjMap->pCerts == NULL)
 
658
            /* XXX uh-oh */
 
659
            return;
 
660
        /* get the first cert and dump it. */
 
661
        node = subjMap->pCerts[0];
 
662
        if (map_handle_is_ok(info, (void *)node, 0)) {
 
663
            map = (certDBEntryMap *)node->appData;
 
664
            /* going left here stops. */
 
665
            print_cert_graph(info, map, GOLEFT); 
 
666
        } else {
 
667
            info->dbErrors[SubjectHasNoKeyForCert]++;
 
668
        }
 
669
        /* Now it is safe to output the subject id. */
 
670
        if (direction == GOLEFT)
 
671
            printnode(info, "Subject  %5d <---- ", subjMap->index);
 
672
        else /* direction == GOBOTH */
 
673
            printnode(info, "Subject  %5d ----> ", subjMap->index);
 
674
    }
 
675
    if (direction == GORIGHT || direction == GOBOTH) { 
 
676
        /* Okay, now output the nickname|smime for this subject. */
 
677
        if (direction != GOBOTH) /* handled above */
 
678
           printnode(info, "Subject  %5d ----> ", subjMap->index);
 
679
        if (subjMap->pNickname) {
 
680
            node = subjMap->pNickname;
 
681
            if (map_handle_is_ok(info, (void *)node, 0)) {
 
682
                map = (certDBEntryMap *)node->appData;
 
683
                /* going right here stops. */
 
684
                print_nickname_graph(info, map, GORIGHT);
 
685
            }
 
686
        }
 
687
        if (subjMap->pSMime) {
 
688
            node = subjMap->pSMime;
 
689
            if (map_handle_is_ok(info, (void *)node, 0)) {
 
690
                map = (certDBEntryMap *)node->appData;
 
691
                /* going right here stops. */
 
692
                print_smime_graph(info, map, GORIGHT); 
 
693
            }
 
694
        }
 
695
        if (!subjMap->pNickname && !subjMap->pSMime) {
 
696
            printnode(info, "******************* ", -1);
 
697
            info->dbErrors[NoNicknameOrSMimeForSubject]++;
 
698
        }
 
699
        if (subjMap->pNickname && subjMap->pSMime) {
 
700
            info->dbErrors[NicknameAndSMimeEntries]++;
 
701
        }
 
702
    }
 
703
    if (direction != GORIGHT) { /* going right has only one cert */
 
704
        if (opttype == certDBEntryTypeNickname)
 
705
            printnode(info, "Nickname %5d   ", optindex);
 
706
        else if (opttype == certDBEntryTypeSMimeProfile)
 
707
            printnode(info, "S/MIME   %5d   ", optindex);
 
708
        for (i=1 /* 1st one already done */; i<subjMap->numCerts; i++) {
 
709
            printnode(info, "\n", -1); /* start a new line */
 
710
            node = subjMap->pCerts[i];
 
711
            if (map_handle_is_ok(info, (void *)node, 0)) {
 
712
                map = (certDBEntryMap *)node->appData;
 
713
                /* going left here stops. */
 
714
                print_cert_graph(info, map, GOLEFT); 
 
715
                printnode(info, "/", -1);
 
716
            }
 
717
        }
 
718
    }
 
719
}
 
720
 
 
721
/* Given a cert entry, print its unique identifer.  If GORIGHT is specified,
 
722
 * print the cert->subject->nickname|smime map, else just print
 
723
 * the cert entry.
 
724
 */
 
725
void
 
726
print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, int direction)
 
727
{
 
728
    certDBSubjectEntryMap *subjMap;
 
729
    certDBEntryListNode *subjNode;
 
730
    if (direction == GOLEFT) {
 
731
        printnode(info, "Cert     %5d <---- ", certMap->index);
 
732
        /* only want cert entry, terminate here. */
 
733
        return;
 
734
    }
 
735
    /* Keep going right then. */
 
736
    printnode(info, "Cert     %5d ----> ", certMap->index);
 
737
    subjNode = certMap->pSubject;
 
738
    if (map_handle_is_ok(info, (void *)subjNode, 0)) {
 
739
        subjMap = (certDBSubjectEntryMap *)subjNode->appData;
 
740
        print_subject_graph(info, subjMap, GORIGHT, -1, -1);
 
741
    } else {
 
742
        info->dbErrors[NoSubjectForCert]++;
 
743
    }
 
744
}
 
745
 
 
746
SECStatus
 
747
computeDBGraph(certDBArray *dbArray, dbDebugInfo *info)
 
748
{
 
749
    PRCList *cElem, *sElem, *nElem, *mElem;
 
750
    certDBEntryListNode *node;
 
751
    certDBEntryMap *map;
 
752
    certDBSubjectEntryMap *subjMap;
 
753
 
 
754
    /* Graph is of this form:
 
755
     *
 
756
     * certs:
 
757
     * cert ---> subject ---> (nickname|smime)
 
758
     *
 
759
     * subjects:
 
760
     * cert <--- subject ---> (nickname|smime)
 
761
     *
 
762
     * nicknames and smime:
 
763
     * cert <--- subject <--- (nickname|smime)
 
764
     */
 
765
 
 
766
    /* Print cert graph. */
 
767
    for (cElem = PR_LIST_HEAD(&dbArray->certs.link);
 
768
         cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
 
769
        /* Print graph of everything to right of cert entry. */
 
770
        node = LISTNODE_CAST(cElem);
 
771
        map = (certDBEntryMap *)node->appData;
 
772
        print_cert_graph(info, map, GORIGHT);
 
773
        printnode(info, "\n", -1);
 
774
    }
 
775
    printnode(info, "\n", -1);
 
776
 
 
777
    /* Print subject graph. */
 
778
    for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
 
779
         sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
 
780
        /* Print graph of everything to both sides of subject entry. */
 
781
        node = LISTNODE_CAST(sElem);
 
782
        subjMap = (certDBSubjectEntryMap *)node->appData;
 
783
        print_subject_graph(info, subjMap, GOBOTH, -1, -1);
 
784
        printnode(info, "\n", -1);
 
785
    }
 
786
    printnode(info, "\n", -1);
 
787
 
 
788
    /* Print nickname graph. */
 
789
    for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
 
790
         nElem != &dbArray->nicknames.link; nElem = PR_NEXT_LINK(nElem)) {
 
791
        /* Print graph of everything to left of nickname entry. */
 
792
        node = LISTNODE_CAST(nElem);
 
793
        map = (certDBEntryMap *)node->appData;
 
794
        print_nickname_graph(info, map, GOLEFT);
 
795
        printnode(info, "\n", -1);
 
796
    }
 
797
    printnode(info, "\n", -1);
 
798
 
 
799
    /* Print smime graph. */
 
800
    for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
 
801
         mElem != &dbArray->smime.link; mElem = PR_NEXT_LINK(mElem)) {
 
802
        /* Print graph of everything to left of smime entry. */
 
803
        node = LISTNODE_CAST(mElem);
 
804
        if (node == NULL) break;
 
805
        map = (certDBEntryMap *)node->appData;
 
806
        print_smime_graph(info, map, GOLEFT);
 
807
        printnode(info, "\n", -1);
 
808
    }
 
809
    printnode(info, "\n", -1);
 
810
 
 
811
    return SECSuccess;
 
812
}
 
813
 
 
814
/*
 
815
 * List the entries in the db, showing handles between entry types.
 
816
 */
 
817
void
 
818
verboseOutput(certDBArray *dbArray, dbDebugInfo *info)
 
819
{
 
820
    int i, ref;
 
821
    PRCList *elem;
 
822
    certDBEntryListNode *node;
 
823
    certDBEntryMap *map;
 
824
    certDBSubjectEntryMap *smap;
 
825
    certDBEntrySubject *subjectEntry;
 
826
 
 
827
    /* List certs */
 
828
    for (elem = PR_LIST_HEAD(&dbArray->certs.link);
 
829
         elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) {
 
830
        node = LISTNODE_CAST(elem);
 
831
        map = (certDBEntryMap *)node->appData;
 
832
        dumpCertEntry((certDBEntryCert*)&node->entry, map->index, info->out);
 
833
        /* walk the cert handle to it's subject entry */
 
834
        if (map_handle_is_ok(info, map->pSubject, -1)) {
 
835
            smap = (certDBSubjectEntryMap *)map->pSubject->appData;
 
836
            ref = smap->index;
 
837
            PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
 
838
        } else {
 
839
            PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
 
840
        }
 
841
    }
 
842
    /* List subjects */
 
843
    for (elem = PR_LIST_HEAD(&dbArray->subjects.link);
 
844
         elem != &dbArray->subjects.link; elem = PR_NEXT_LINK(elem)) {
 
845
        int refs = 0;
 
846
        node = LISTNODE_CAST(elem);
 
847
        subjectEntry = (certDBEntrySubject *)&node->entry;
 
848
        smap = (certDBSubjectEntryMap *)node->appData;
 
849
        dumpSubjectEntry(subjectEntry, smap->index, info->out);
 
850
        /* iterate over subject's certs */
 
851
        for (i=0; i<smap->numCerts; i++) {
 
852
            /* walk each subject handle to it's cert entries */
 
853
            if (map_handle_is_ok(info, smap->pCerts[i], -1)) {
 
854
                ref = ((certDBEntryMap *)smap->pCerts[i]->appData)->index;
 
855
                PR_fprintf(info->out, "-->(%d. certificate %d)\n", i, ref);
 
856
            } else {
 
857
                PR_fprintf(info->out, "-->(%d. MISSING CERT ENTRY)\n", i);
 
858
            }
 
859
        }
 
860
        if (subjectEntry->nickname) {
 
861
            ++refs;
 
862
            /* walk each subject handle to it's nickname entry */
 
863
            if (map_handle_is_ok(info, smap->pNickname, -1)) {
 
864
                ref = ((certDBEntryMap *)smap->pNickname->appData)->index;
 
865
                PR_fprintf(info->out, "-->(nickname %d)\n", ref);
 
866
            } else {
 
867
                PR_fprintf(info->out, "-->(MISSING NICKNAME ENTRY)\n");
 
868
            }
 
869
        }
 
870
        if (subjectEntry->nemailAddrs && 
 
871
            subjectEntry->emailAddrs &&
 
872
            subjectEntry->emailAddrs[0] &&
 
873
            subjectEntry->emailAddrs[0][0]) {
 
874
            ++refs;
 
875
            /* walk each subject handle to it's smime entry */
 
876
            if (map_handle_is_ok(info, smap->pSMime, -1)) {
 
877
                ref = ((certDBEntryMap *)smap->pSMime->appData)->index;
 
878
                PR_fprintf(info->out, "-->(s/mime %d)\n", ref);
 
879
            } else {
 
880
                PR_fprintf(info->out, "-->(MISSING S/MIME ENTRY)\n");
 
881
            }
 
882
        }
 
883
        if (!refs) {
 
884
            PR_fprintf(info->out, "-->(NO NICKNAME+S/MIME ENTRY)\n");
 
885
        }
 
886
        PR_fprintf(info->out, "\n\n");
 
887
    }
 
888
    for (elem = PR_LIST_HEAD(&dbArray->nicknames.link);
 
889
         elem != &dbArray->nicknames.link; elem = PR_NEXT_LINK(elem)) {
 
890
        node = LISTNODE_CAST(elem);
 
891
        map = (certDBEntryMap *)node->appData;
 
892
        dumpNicknameEntry((certDBEntryNickname*)&node->entry, map->index, 
 
893
                          info->out);
 
894
        if (map_handle_is_ok(info, map->pSubject, -1)) {
 
895
            ref = ((certDBEntryMap *)map->pSubject->appData)->index;
 
896
            PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
 
897
        } else {
 
898
            PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
 
899
        }
 
900
    }
 
901
    for (elem = PR_LIST_HEAD(&dbArray->smime.link);
 
902
         elem != &dbArray->smime.link; elem = PR_NEXT_LINK(elem)) {
 
903
        node = LISTNODE_CAST(elem);
 
904
        map = (certDBEntryMap *)node->appData;
 
905
        dumpSMimeEntry((certDBEntrySMime*)&node->entry, map->index, info->out);
 
906
        if (map_handle_is_ok(info, map->pSubject, -1)) {
 
907
            ref = ((certDBEntryMap *)map->pSubject->appData)->index;
 
908
            PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
 
909
        } else {
 
910
            PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
 
911
        }
 
912
    }
 
913
    PR_fprintf(info->out, "\n\n");
 
914
}
 
915
 
 
916
 
 
917
/* A callback function, intended to be called from nsslowcert_TraverseDBEntries
 
918
 * Builds a PRCList of DB entries of the specified type.
 
919
 */
 
920
SECStatus 
 
921
SEC_GetCertDBEntryList(SECItem *dbdata, SECItem *dbkey, 
 
922
                       certDBEntryType entryType, void *pdata)
 
923
{
 
924
    certDBEntry         * entry;
 
925
    certDBEntryListNode * node;
 
926
    PRCList             * list = (PRCList *)pdata;
 
927
 
 
928
    if (!dbdata || !dbkey || !pdata || !dbdata->data || !dbkey->data) {
 
929
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
 
930
        return SECFailure;
 
931
    }
 
932
    entry = nsslowcert_DecodeAnyDBEntry(dbdata, dbkey, entryType, NULL);
 
933
    if (!entry) {
 
934
        return SECSuccess; /* skip it */
 
935
    }
 
936
    node = PORT_ArenaZNew(entry->common.arena, certDBEntryListNode);
 
937
    if (!node) {
 
938
        /* DestroyDBEntry(entry); */
 
939
        PLArenaPool *arena = entry->common.arena;
 
940
        PORT_Memset(&entry->common, 0, sizeof entry->common);
 
941
        PORT_FreeArena(arena, PR_FALSE);
 
942
        return SECFailure;
 
943
    }
 
944
    node->entry = *entry;               /* crude but effective. */
 
945
    PR_INIT_CLIST(&node->link);
 
946
    PR_INSERT_BEFORE(&node->link, list);
 
947
    return SECSuccess;
 
948
}
 
949
 
 
950
 
 
951
int
 
952
fillDBEntryArray(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, 
 
953
                 certDBEntryListNode *list)
 
954
{
 
955
    PRCList *elem;
 
956
    certDBEntryListNode *node;
 
957
    certDBEntryMap *mnode;
 
958
    certDBSubjectEntryMap *smnode;
 
959
    PRArenaPool *arena;
 
960
    int count = 0;
 
961
 
 
962
    /* Initialize a dummy entry in the list.  The list head will be the
 
963
     * next element, so this element is skipped by for loops.
 
964
     */
 
965
    PR_INIT_CLIST((PRCList *)list);
 
966
    /* Collect all of the cert db entries for this type into a list. */
 
967
    nsslowcert_TraverseDBEntries(handle, type, SEC_GetCertDBEntryList, list);
 
968
 
 
969
    for (elem = PR_LIST_HEAD(&list->link); 
 
970
         elem != &list->link; elem = PR_NEXT_LINK(elem)) {
 
971
        /* Iterate over the entries and ... */
 
972
        node = (certDBEntryListNode *)elem;
 
973
        if (type != certDBEntryTypeSubject) {
 
974
            arena = PORT_NewArena(sizeof(*mnode));
 
975
            mnode = PORT_ArenaZNew(arena, certDBEntryMap);
 
976
            mnode->arena = arena;
 
977
            /* ... assign a unique index number to each node, and ... */
 
978
            mnode->index = count;
 
979
            /* ... set the map pointer for the node. */
 
980
            node->appData = (void *)mnode;
 
981
        } else {
 
982
            /* allocate some room for the cert pointers also */
 
983
            arena = PORT_NewArena(sizeof(*smnode) + 20*sizeof(void *));
 
984
            smnode = PORT_ArenaZNew(arena, certDBSubjectEntryMap);
 
985
            smnode->arena = arena;
 
986
            smnode->index = count;
 
987
            node->appData = (void *)smnode;
 
988
        }
 
989
        count++;
 
990
    }
 
991
    return count;
 
992
}
 
993
 
 
994
void
 
995
freeDBEntryList(PRCList *list)
 
996
{
 
997
    PRCList *next, *elem;
 
998
    certDBEntryListNode *node;
 
999
    certDBEntryMap *map;
 
1000
 
 
1001
    for (elem = PR_LIST_HEAD(list); elem != list;) { 
 
1002
        next = PR_NEXT_LINK(elem);
 
1003
        node = (certDBEntryListNode *)elem;
 
1004
        map = (certDBEntryMap *)node->appData;
 
1005
        PR_REMOVE_LINK(&node->link);
 
1006
        PORT_FreeArena(map->arena, PR_TRUE);
 
1007
        PORT_FreeArena(node->entry.common.arena, PR_TRUE);
 
1008
        elem = next;
 
1009
    }
 
1010
}
 
1011
 
 
1012
void
 
1013
DBCK_DebugDB(NSSLOWCERTCertDBHandle *handle, PRFileDesc *out, 
 
1014
             PRFileDesc *mailfile)
 
1015
{
 
1016
    int i, nCertsFound, nSubjFound, nErr;
 
1017
    int nCerts, nSubjects, nSubjCerts, nNicknames, nSMime, nRevocation;
 
1018
    PRCList *elem;
 
1019
    char c;
 
1020
    dbDebugInfo info;
 
1021
    certDBArray dbArray;
 
1022
 
 
1023
    PORT_Memset(&dbArray, 0, sizeof(dbArray));
 
1024
    PORT_Memset(&info, 0, sizeof(info));
 
1025
    info.verbose = (PRBool)(out != NULL);
 
1026
    info.dograph = info.verbose;
 
1027
    info.out       = (out)    ? out      : PR_STDOUT;
 
1028
    info.graphfile = mailfile ? mailfile : PR_STDOUT;
 
1029
 
 
1030
    /*  Fill the array structure with cert/subject/nickname/smime entries.  */
 
1031
    dbArray.numCerts     = fillDBEntryArray(handle, certDBEntryTypeCert, 
 
1032
                                            &dbArray.certs);
 
1033
    dbArray.numSubjects  = fillDBEntryArray(handle, certDBEntryTypeSubject, 
 
1034
                                            &dbArray.subjects);
 
1035
    dbArray.numNicknames = fillDBEntryArray(handle, certDBEntryTypeNickname, 
 
1036
                                            &dbArray.nicknames);
 
1037
    dbArray.numSMime     = fillDBEntryArray(handle, certDBEntryTypeSMimeProfile, 
 
1038
                                            &dbArray.smime);
 
1039
    dbArray.numRevocation= fillDBEntryArray(handle, certDBEntryTypeRevocation, 
 
1040
                                            &dbArray.revocation);
 
1041
 
 
1042
    /*  Compute the map between the database entries.  */
 
1043
    mapSubjectEntries(&dbArray);
 
1044
    mapCertEntries(&dbArray);
 
1045
    computeDBGraph(&dbArray, &info);
 
1046
 
 
1047
    /*  Store the totals for later reference.  */
 
1048
    nCerts     = dbArray.numCerts;
 
1049
    nSubjects  = dbArray.numSubjects;
 
1050
    nNicknames = dbArray.numNicknames;
 
1051
    nSMime     = dbArray.numSMime;
 
1052
    nRevocation= dbArray.numRevocation;
 
1053
    nSubjCerts = 0;
 
1054
    for (elem = PR_LIST_HEAD(&dbArray.subjects.link);
 
1055
         elem != &dbArray.subjects.link; elem = PR_NEXT_LINK(elem)) {
 
1056
        certDBSubjectEntryMap *smap;
 
1057
        smap = (certDBSubjectEntryMap *)LISTNODE_CAST(elem)->appData;
 
1058
        nSubjCerts += smap->numCerts;
 
1059
    }
 
1060
 
 
1061
    if (info.verbose) {
 
1062
        /*  Dump the database contents.  */
 
1063
        verboseOutput(&dbArray, &info);
 
1064
    }
 
1065
 
 
1066
    freeDBEntryList(&dbArray.certs.link);
 
1067
    freeDBEntryList(&dbArray.subjects.link);
 
1068
    freeDBEntryList(&dbArray.nicknames.link);
 
1069
    freeDBEntryList(&dbArray.smime.link);
 
1070
    freeDBEntryList(&dbArray.revocation.link);
 
1071
 
 
1072
    PR_fprintf(info.out, "\n");
 
1073
    PR_fprintf(info.out, "Database statistics:\n");
 
1074
    PR_fprintf(info.out, "N0: Found %4d Certificate entries.\n", 
 
1075
                          nCerts);
 
1076
    PR_fprintf(info.out, "N1: Found %4d Subject entries (unique DN's).\n", 
 
1077
                          nSubjects);
 
1078
    PR_fprintf(info.out, "N2: Found %4d Cert keys within Subject entries.\n", 
 
1079
                          nSubjCerts);
 
1080
    PR_fprintf(info.out, "N3: Found %4d Nickname entries.\n", 
 
1081
                          nNicknames);
 
1082
    PR_fprintf(info.out, "N4: Found %4d S/MIME entries.\n", 
 
1083
                          nSMime);
 
1084
    PR_fprintf(info.out, "N5: Found %4d CRL entries.\n", 
 
1085
                          nRevocation);
 
1086
    PR_fprintf(info.out, "\n");
 
1087
 
 
1088
    nErr = 0;
 
1089
    for (i=0; i < NUM_ERROR_TYPES; i++) {
 
1090
        PR_fprintf(info.out, "E%d: Found %4d %s\n", 
 
1091
                   i, info.dbErrors[i], errResult[i]);
 
1092
        nErr += info.dbErrors[i];
 
1093
    }
 
1094
    PR_fprintf(info.out, "--------------\n    Found %4d errors in database.\n", 
 
1095
               nErr);
 
1096
 
 
1097
    PR_fprintf(info.out, "\nCertificates:\n");
 
1098
    PR_fprintf(info.out, "N0 == N2 + E%d + E%d\n", NoSubjectForCert, 
 
1099
                                                   SubjectHasNoKeyForCert);
 
1100
    nCertsFound = nSubjCerts +
 
1101
                  info.dbErrors[NoSubjectForCert] +
 
1102
                  info.dbErrors[SubjectHasNoKeyForCert];
 
1103
    c = (nCertsFound == nCerts) ? '=' : '!';
 
1104
    PR_fprintf(info.out, "%d %c= %d + %d + %d\n", nCerts, c, nSubjCerts, 
 
1105
                  info.dbErrors[NoSubjectForCert],
 
1106
                  info.dbErrors[SubjectHasNoKeyForCert]);
 
1107
    PR_fprintf(info.out, "\nSubjects:\n");
 
1108
    PR_fprintf(info.out, 
 
1109
    "N1 == N3 + N4 + E%d + E%d + E%d + E%d + E%d - E%d - E%d - E%d\n",
 
1110
                  NoNicknameOrSMimeForSubject, 
 
1111
                  WrongNicknameForSubject,
 
1112
                  NoNicknameEntry, 
 
1113
                  WrongSMimeForSubject, 
 
1114
                  NoSMimeEntry,
 
1115
                  NoSubjectForNickname, 
 
1116
                  NoSubjectForSMime,
 
1117
                  NicknameAndSMimeEntries);
 
1118
    nSubjFound = nNicknames + nSMime + 
 
1119
                 info.dbErrors[NoNicknameOrSMimeForSubject] +
 
1120
                 info.dbErrors[WrongNicknameForSubject] +
 
1121
                 info.dbErrors[NoNicknameEntry] +
 
1122
                 info.dbErrors[WrongSMimeForSubject] +
 
1123
                 info.dbErrors[NoSMimeEntry] -
 
1124
                 info.dbErrors[NoSubjectForNickname] -
 
1125
                 info.dbErrors[NoSubjectForSMime] -
 
1126
                 info.dbErrors[NicknameAndSMimeEntries];
 
1127
    c = (nSubjFound == nSubjects) ? '=' : '!';
 
1128
    PR_fprintf(info.out, 
 
1129
    "%2d %c= %2d + %2d + %2d + %2d + %2d + %2d + %2d - %2d - %2d - %2d\n",
 
1130
                  nSubjects, c, nNicknames, nSMime,
 
1131
                  info.dbErrors[NoNicknameOrSMimeForSubject],
 
1132
                  info.dbErrors[WrongNicknameForSubject],
 
1133
                  info.dbErrors[NoNicknameEntry],
 
1134
                  info.dbErrors[WrongSMimeForSubject],
 
1135
                  info.dbErrors[NoSMimeEntry],
 
1136
                  info.dbErrors[NoSubjectForNickname],
 
1137
                  info.dbErrors[NoSubjectForSMime],
 
1138
                  info.dbErrors[NicknameAndSMimeEntries]);
 
1139
    PR_fprintf(info.out, "\n");
 
1140
}
 
1141
 
 
1142
#ifdef DORECOVER
 
1143
#include "dbrecover.c"
 
1144
#endif /* DORECOVER */
 
1145
 
 
1146
enum {
 
1147
    cmd_Debug = 0,
 
1148
    cmd_LongUsage,
 
1149
    cmd_Recover
 
1150
};
 
1151
 
 
1152
enum {
 
1153
    opt_KeepAll = 0,
 
1154
    opt_CertDir,
 
1155
    opt_Dumpfile,
 
1156
    opt_InputDB,
 
1157
    opt_OutputDB,
 
1158
    opt_Mailfile,
 
1159
    opt_Prompt,
 
1160
    opt_KeepRedundant,
 
1161
    opt_KeepNoSMimeProfile,
 
1162
    opt_Verbose,
 
1163
    opt_KeepExpired
 
1164
};
 
1165
 
 
1166
static secuCommandFlag dbck_commands[] =
 
1167
{
 
1168
    { /* cmd_Debug,    */  'D', PR_FALSE, 0, PR_FALSE },
 
1169
    { /* cmd_LongUsage,*/  'H', PR_FALSE, 0, PR_FALSE },
 
1170
    { /* cmd_Recover,  */  'R', PR_FALSE, 0, PR_FALSE }
 
1171
};
 
1172
 
 
1173
static secuCommandFlag dbck_options[] =
 
1174
{
 
1175
    { /* opt_KeepAll,           */  'a', PR_FALSE, 0, PR_FALSE },
 
1176
    { /* opt_CertDir,           */  'd', PR_TRUE,  0, PR_FALSE },
 
1177
    { /* opt_Dumpfile,          */  'f', PR_TRUE,  0, PR_FALSE },
 
1178
    { /* opt_InputDB,           */  'i', PR_TRUE,  0, PR_FALSE },
 
1179
    { /* opt_OutputDB,          */  'o', PR_TRUE,  0, PR_FALSE },
 
1180
    { /* opt_Mailfile,          */  'm', PR_FALSE, 0, PR_FALSE },
 
1181
    { /* opt_Prompt,            */  'p', PR_FALSE, 0, PR_FALSE },
 
1182
    { /* opt_KeepRedundant,     */  'r', PR_FALSE, 0, PR_FALSE },
 
1183
    { /* opt_KeepNoSMimeProfile,*/  's', PR_FALSE, 0, PR_FALSE },
 
1184
    { /* opt_Verbose,           */  'v', PR_FALSE, 0, PR_FALSE },
 
1185
    { /* opt_KeepExpired,       */  'x', PR_FALSE, 0, PR_FALSE }
 
1186
};
 
1187
 
 
1188
#define CERT_DB_FMT "%s/cert%s.db"
 
1189
 
 
1190
static char *
 
1191
dbck_certdb_name_cb(void *arg, int dbVersion)
 
1192
{
 
1193
    const char *configdir = (const char *)arg;
 
1194
    const char *dbver;
 
1195
    char *smpname = NULL;
 
1196
    char *dbname = NULL;
 
1197
 
 
1198
    switch (dbVersion) {
 
1199
      case 8:
 
1200
        dbver = "8";
 
1201
        break;
 
1202
      case 7:
 
1203
        dbver = "7";
 
1204
        break;
 
1205
      case 6:
 
1206
        dbver = "6";
 
1207
        break;
 
1208
      case 5:
 
1209
        dbver = "5";
 
1210
        break;
 
1211
      case 4:
 
1212
      default:
 
1213
        dbver = "";
 
1214
        break;
 
1215
    }
 
1216
 
 
1217
    /* make sure we return something allocated with PORT_ so we have properly
 
1218
     * matched frees at the end */
 
1219
    smpname = PR_smprintf(CERT_DB_FMT, configdir, dbver);
 
1220
    if (smpname) {
 
1221
        dbname = PORT_Strdup(smpname);
 
1222
        PR_smprintf_free(smpname);
 
1223
    }
 
1224
    return dbname;
 
1225
}
 
1226
    
 
1227
 
 
1228
int 
 
1229
main(int argc, char **argv)
 
1230
{
 
1231
    NSSLOWCERTCertDBHandle *certHandle;
 
1232
 
 
1233
    PRFileDesc *mailfile = NULL;
 
1234
    PRFileDesc *dumpfile = NULL;
 
1235
 
 
1236
    char * pathname     = 0;
 
1237
    char * fullname     = 0;
 
1238
    char * newdbname    = 0;
 
1239
 
 
1240
    PRBool removeExpired, requireProfile, singleEntry;
 
1241
    SECStatus   rv;
 
1242
    secuCommand dbck;
 
1243
 
 
1244
    dbck.numCommands = sizeof(dbck_commands) / sizeof(secuCommandFlag);
 
1245
    dbck.numOptions = sizeof(dbck_options) / sizeof(secuCommandFlag);
 
1246
    dbck.commands = dbck_commands;
 
1247
    dbck.options = dbck_options;
 
1248
 
 
1249
    progName = strrchr(argv[0], '/');
 
1250
    progName = progName ? progName+1 : argv[0];
 
1251
 
 
1252
    rv = SECU_ParseCommandLine(argc, argv, progName, &dbck);
 
1253
 
 
1254
    if (rv != SECSuccess)
 
1255
        Usage(progName);
 
1256
 
 
1257
    if (dbck.commands[cmd_LongUsage].activated)
 
1258
        LongUsage(progName);
 
1259
 
 
1260
    if (!dbck.commands[cmd_Debug].activated &&
 
1261
        !dbck.commands[cmd_Recover].activated) {
 
1262
        PR_fprintf(PR_STDERR, "Please specify -H, -D or -R.\n");
 
1263
        Usage(progName);
 
1264
    }
 
1265
 
 
1266
    removeExpired = !(dbck.options[opt_KeepAll].activated ||
 
1267
                      dbck.options[opt_KeepExpired].activated);
 
1268
 
 
1269
    requireProfile = !(dbck.options[opt_KeepAll].activated ||
 
1270
                    dbck.options[opt_KeepNoSMimeProfile].activated);
 
1271
 
 
1272
    singleEntry = !(dbck.options[opt_KeepAll].activated ||
 
1273
                    dbck.options[opt_KeepRedundant].activated);
 
1274
 
 
1275
    if (dbck.options[opt_OutputDB].activated) {
 
1276
        newdbname = PL_strdup(dbck.options[opt_OutputDB].arg);
 
1277
    } else {
 
1278
        newdbname = PL_strdup("new_cert8.db");
 
1279
    }
 
1280
 
 
1281
    /*  Create a generic graph of the database.  */
 
1282
    if (dbck.options[opt_Mailfile].activated) {
 
1283
        mailfile = PR_Open("./mailfile", PR_RDWR | PR_CREATE_FILE, 00660);
 
1284
        if (!mailfile) {
 
1285
            fprintf(stderr, "Unable to create mailfile.\n");
 
1286
            return -1;
 
1287
        }
 
1288
    }
 
1289
 
 
1290
    /*  Dump all debugging info while running.  */
 
1291
    if (dbck.options[opt_Verbose].activated) {
 
1292
        if (dbck.options[opt_Dumpfile].activated) {
 
1293
            dumpfile = PR_Open(dbck.options[opt_Dumpfile].arg,
 
1294
                               PR_RDWR | PR_CREATE_FILE, 00660);
 
1295
            if (!dumpfile) {
 
1296
                fprintf(stderr, "Unable to create dumpfile.\n");
 
1297
                return -1;
 
1298
            }
 
1299
        } else {
 
1300
            dumpfile = PR_STDOUT;
 
1301
        }
 
1302
    }
 
1303
 
 
1304
    /*  Set the cert database directory.  */
 
1305
    if (dbck.options[opt_CertDir].activated) {
 
1306
        SECU_ConfigDirectory(dbck.options[opt_CertDir].arg);
 
1307
    }
 
1308
 
 
1309
    pathname = SECU_ConfigDirectory(NULL);
 
1310
 
 
1311
    PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
 
1312
    rv = NSS_NoDB_Init(pathname);
 
1313
    if (rv != SECSuccess) {
 
1314
        fprintf(stderr, "NSS_NoDB_Init failed\n");
 
1315
        return -1;
 
1316
    }
 
1317
 
 
1318
    certHandle = PORT_ZNew(NSSLOWCERTCertDBHandle);
 
1319
    if (!certHandle) {
 
1320
        SECU_PrintError(progName, "unable to get database handle");
 
1321
        return -1;
 
1322
    }
 
1323
    certHandle->ref = 1;
 
1324
 
 
1325
#ifdef NOTYET
 
1326
    /*  Open the possibly corrupt database.  */
 
1327
    if (dbck.options[opt_InputDB].activated) {
 
1328
        PRFileInfo fileInfo;
 
1329
        fullname = PR_smprintf("%s/%s", pathname, 
 
1330
                                        dbck.options[opt_InputDB].arg);
 
1331
        if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
 
1332
            fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
 
1333
            return -1;
 
1334
        }
 
1335
        rv = CERT_OpenCertDBFilename(certHandle, fullname, PR_TRUE);
 
1336
    } else 
 
1337
#endif
 
1338
    {
 
1339
        /*  Use the default.  */
 
1340
#ifdef NOTYET
 
1341
        fullname = SECU_CertDBNameCallback(NULL, CERT_DB_FILE_VERSION);
 
1342
        if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
 
1343
            fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
 
1344
            return -1;
 
1345
        }
 
1346
#endif
 
1347
        rv = nsslowcert_OpenCertDB(certHandle, 
 
1348
                                   PR_TRUE,                 /* readOnly */
 
1349
                                   NULL,                    /* rdb appName */
 
1350
                                   "",                      /* rdb prefix */
 
1351
                                   dbck_certdb_name_cb,     /* namecb */
 
1352
                                   pathname,                /* configDir */
 
1353
                                   PR_FALSE);               /* volatile */
 
1354
    }
 
1355
 
 
1356
    if (rv) {
 
1357
        SECU_PrintError(progName, "unable to open cert database");
 
1358
        return -1;
 
1359
    }
 
1360
 
 
1361
    if (dbck.commands[cmd_Debug].activated) {
 
1362
        DBCK_DebugDB(certHandle, dumpfile, mailfile);
 
1363
        return 0;
 
1364
    }
 
1365
 
 
1366
#ifdef DORECOVER
 
1367
    if (dbck.commands[cmd_Recover].activated) {
 
1368
        DBCK_ReconstructDBFromCerts(certHandle, newdbname,
 
1369
                                    dumpfile, removeExpired, 
 
1370
                                    requireProfile, singleEntry, 
 
1371
                                    dbck.options[opt_Prompt].activated);
 
1372
        return 0;
 
1373
    }
 
1374
#endif
 
1375
 
 
1376
    if (mailfile)
 
1377
        PR_Close(mailfile);
 
1378
    if (dumpfile)
 
1379
        PR_Close(dumpfile);
 
1380
    if (certHandle) {
 
1381
        nsslowcert_ClosePermCertDB(certHandle);
 
1382
        PORT_Free(certHandle);
 
1383
    }
 
1384
    return -1;
 
1385
}