mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
1386 lines
45 KiB
C
1386 lines
45 KiB
C
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is the Netscape security libraries.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Netscape Communications Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1994-2000
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
/*
|
||
|
** dbck.c
|
||
|
**
|
||
|
** utility for fixing corrupt cert databases
|
||
|
**
|
||
|
*/
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "secutil.h"
|
||
|
#include "cdbhdl.h"
|
||
|
#include "certdb.h"
|
||
|
#include "cert.h"
|
||
|
#include "nspr.h"
|
||
|
#include "prtypes.h"
|
||
|
#include "prtime.h"
|
||
|
#include "prlong.h"
|
||
|
#include "pcert.h"
|
||
|
#include "nss.h"
|
||
|
|
||
|
static char *progName;
|
||
|
|
||
|
/* placeholders for pointer error types */
|
||
|
static void *WrongEntry;
|
||
|
static void *NoNickname;
|
||
|
static void *NoSMime;
|
||
|
|
||
|
typedef enum {
|
||
|
/* 0*/ NoSubjectForCert = 0,
|
||
|
/* 1*/ SubjectHasNoKeyForCert,
|
||
|
/* 2*/ NoNicknameOrSMimeForSubject,
|
||
|
/* 3*/ WrongNicknameForSubject,
|
||
|
/* 4*/ NoNicknameEntry,
|
||
|
/* 5*/ WrongSMimeForSubject,
|
||
|
/* 6*/ NoSMimeEntry,
|
||
|
/* 7*/ NoSubjectForNickname,
|
||
|
/* 8*/ NoSubjectForSMime,
|
||
|
/* 9*/ NicknameAndSMimeEntries,
|
||
|
NUM_ERROR_TYPES
|
||
|
} dbErrorType;
|
||
|
|
||
|
static char *dbErrorString[NUM_ERROR_TYPES] = {
|
||
|
/* 0*/ "<CERT ENTRY>\nDid not find a subject entry for this certificate.",
|
||
|
/* 1*/ "<SUBJECT ENTRY>\nSubject has certKey which is not in db.",
|
||
|
/* 2*/ "<SUBJECT ENTRY>\nSubject does not have a nickname or email address.",
|
||
|
/* 3*/ "<SUBJECT ENTRY>\nUsing this subject's nickname, found a nickname entry for a different subject.",
|
||
|
/* 4*/ "<SUBJECT ENTRY>\nDid not find a nickname entry for this subject.",
|
||
|
/* 5*/ "<SUBJECT ENTRY>\nUsing this subject's email, found an S/MIME entry for a different subject.",
|
||
|
/* 6*/ "<SUBJECT ENTRY>\nDid not find an S/MIME entry for this subject.",
|
||
|
/* 7*/ "<NICKNAME ENTRY>\nDid not find a subject entry for this nickname.",
|
||
|
/* 8*/ "<S/MIME ENTRY>\nDid not find a subject entry for this S/MIME profile.",
|
||
|
};
|
||
|
|
||
|
static char *errResult[NUM_ERROR_TYPES] = {
|
||
|
"Certificate entries that had no subject entry.",
|
||
|
"Subject entries with no corresponding Certificate entries.",
|
||
|
"Subject entries that had no nickname or S/MIME entries.",
|
||
|
"Redundant nicknames (subjects with the same nickname).",
|
||
|
"Subject entries that had no nickname entry.",
|
||
|
"Redundant email addresses (subjects with the same email address).",
|
||
|
"Subject entries that had no S/MIME entry.",
|
||
|
"Nickname entries that had no subject entry.",
|
||
|
"S/MIME entries that had no subject entry.",
|
||
|
"Subject entries with BOTH nickname and S/MIME entries."
|
||
|
};
|
||
|
|
||
|
|
||
|
enum {
|
||
|
GOBOTH = 0,
|
||
|
GORIGHT,
|
||
|
GOLEFT
|
||
|
};
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
PRBool verbose;
|
||
|
PRBool dograph;
|
||
|
PRFileDesc *out;
|
||
|
PRFileDesc *graphfile;
|
||
|
int dbErrors[NUM_ERROR_TYPES];
|
||
|
} dbDebugInfo;
|
||
|
|
||
|
struct certDBEntryListNodeStr {
|
||
|
PRCList link;
|
||
|
certDBEntry entry;
|
||
|
void *appData;
|
||
|
};
|
||
|
typedef struct certDBEntryListNodeStr certDBEntryListNode;
|
||
|
|
||
|
/*
|
||
|
* A list node for a cert db entry. The index is a unique identifier
|
||
|
* to use for creating generic maps of a db. This struct handles
|
||
|
* the cert, nickname, and smime db entry types, as all three have a
|
||
|
* single handle to a subject entry.
|
||
|
* This structure is pointed to by certDBEntryListNode->appData.
|
||
|
*/
|
||
|
typedef struct
|
||
|
{
|
||
|
PRArenaPool *arena;
|
||
|
int index;
|
||
|
certDBEntryListNode *pSubject;
|
||
|
} certDBEntryMap;
|
||
|
|
||
|
/*
|
||
|
* Subject entry is special case, it has bidirectional handles. One
|
||
|
* subject entry can point to several certs (using the same DN), and
|
||
|
* a nickname and/or smime entry.
|
||
|
* This structure is pointed to by certDBEntryListNode->appData.
|
||
|
*/
|
||
|
typedef struct
|
||
|
{
|
||
|
PRArenaPool *arena;
|
||
|
int index;
|
||
|
int numCerts;
|
||
|
certDBEntryListNode **pCerts;
|
||
|
certDBEntryListNode *pNickname;
|
||
|
certDBEntryListNode *pSMime;
|
||
|
} certDBSubjectEntryMap;
|
||
|
|
||
|
/*
|
||
|
* A map of a certdb.
|
||
|
*/
|
||
|
typedef struct
|
||
|
{
|
||
|
int numCerts;
|
||
|
int numSubjects;
|
||
|
int numNicknames;
|
||
|
int numSMime;
|
||
|
int numRevocation;
|
||
|
certDBEntryListNode certs; /* pointer to head of cert list */
|
||
|
certDBEntryListNode subjects; /* pointer to head of subject list */
|
||
|
certDBEntryListNode nicknames; /* pointer to head of nickname list */
|
||
|
certDBEntryListNode smime; /* pointer to head of smime list */
|
||
|
certDBEntryListNode revocation; /* pointer to head of revocation list */
|
||
|
} certDBArray;
|
||
|
|
||
|
/* Cast list to the base element, a certDBEntryListNode. */
|
||
|
#define LISTNODE_CAST(node) \
|
||
|
((certDBEntryListNode *)(node))
|
||
|
|
||
|
static void
|
||
|
Usage(char *progName)
|
||
|
{
|
||
|
#define FPS fprintf(stderr,
|
||
|
FPS "Type %s -H for more detailed descriptions\n", progName);
|
||
|
FPS "Usage: %s -D [-d certdir] [-m] [-v [-f dumpfile]]\n",
|
||
|
progName);
|
||
|
#ifdef DORECOVER
|
||
|
FPS " %s -R -o newdbname [-d certdir] [-aprsx] [-v [-f dumpfile]]\n",
|
||
|
progName);
|
||
|
#endif
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
LongUsage(char *progName)
|
||
|
{
|
||
|
FPS "%-15s Display this help message.\n",
|
||
|
"-H");
|
||
|
FPS "%-15s Dump analysis. No changes will be made to the database.\n",
|
||
|
"-D");
|
||
|
FPS "%-15s Cert database directory (default is ~/.netscape)\n",
|
||
|
" -d certdir");
|
||
|
FPS "%-15s Put database graph in ./mailfile (default is stdout).\n",
|
||
|
" -m");
|
||
|
FPS "%-15s Verbose mode. Dumps the entire contents of your cert8.db.\n",
|
||
|
" -v");
|
||
|
FPS "%-15s File to dump verbose output into. (default is stdout)\n",
|
||
|
" -f dumpfile");
|
||
|
#ifdef DORECOVER
|
||
|
FPS "%-15s Repair the database. The program will look for broken\n",
|
||
|
"-R");
|
||
|
FPS "%-15s dependencies between subject entries and certificates,\n",
|
||
|
"");
|
||
|
FPS "%-15s between nickname entries and subjects, and between SMIME\n",
|
||
|
"");
|
||
|
FPS "%-15s profiles and subjects. Any duplicate entries will be\n",
|
||
|
"");
|
||
|
FPS "%-15s removed, any missing entries will be created.\n",
|
||
|
"");
|
||
|
FPS "%-15s File to store new database in (default is new_cert8.db)\n",
|
||
|
" -o newdbname");
|
||
|
FPS "%-15s Cert database directory (default is ~/.netscape)\n",
|
||
|
" -d certdir");
|
||
|
FPS "%-15s Prompt before removing any certificates.\n",
|
||
|
" -p");
|
||
|
FPS "%-15s Keep all possible certificates. Only remove certificates\n",
|
||
|
" -a");
|
||
|
FPS "%-15s which prevent creation of a consistent database. Thus any\n",
|
||
|
"");
|
||
|
FPS "%-15s expired or redundant entries will be kept.\n",
|
||
|
"");
|
||
|
FPS "%-15s Keep redundant nickname/email entries. It is possible\n",
|
||
|
" -r");
|
||
|
FPS "%-15s only one such entry will be usable.\n",
|
||
|
"");
|
||
|
FPS "%-15s Don't require an S/MIME profile in order to keep an S/MIME\n",
|
||
|
" -s");
|
||
|
FPS "%-15s cert. An empty profile will be created.\n",
|
||
|
"");
|
||
|
FPS "%-15s Keep expired certificates.\n",
|
||
|
" -x");
|
||
|
FPS "%-15s Verbose mode - report all activity while recovering db.\n",
|
||
|
" -v");
|
||
|
FPS "%-15s File to dump verbose output into.\n",
|
||
|
" -f dumpfile");
|
||
|
FPS "\n");
|
||
|
#endif
|
||
|
exit(-1);
|
||
|
#undef FPS
|
||
|
}
|
||
|
|
||
|
/*******************************************************************
|
||
|
*
|
||
|
* Functions for dbck.
|
||
|
*
|
||
|
******************************************************************/
|
||
|
|
||
|
void
|
||
|
printHexString(PRFileDesc *out, SECItem *hexval)
|
||
|
{
|
||
|
unsigned int i;
|
||
|
for (i = 0; i < hexval->len; i++) {
|
||
|
if (i != hexval->len - 1) {
|
||
|
PR_fprintf(out, "%02x:", hexval->data[i]);
|
||
|
} else {
|
||
|
PR_fprintf(out, "%02x", hexval->data[i]);
|
||
|
}
|
||
|
}
|
||
|
PR_fprintf(out, "\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
SECStatus
|
||
|
dumpCertificate(CERTCertificate *cert, int num, PRFileDesc *outfile)
|
||
|
{
|
||
|
int userCert = 0;
|
||
|
CERTCertTrust *trust = cert->trust;
|
||
|
userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
|
||
|
(SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
|
||
|
(SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
|
||
|
if (num >= 0) {
|
||
|
PR_fprintf(outfile, "Certificate: %3d\n", num);
|
||
|
} else {
|
||
|
PR_fprintf(outfile, "Certificate:\n");
|
||
|
}
|
||
|
PR_fprintf(outfile, "----------------\n");
|
||
|
if (userCert)
|
||
|
PR_fprintf(outfile, "(User Cert)\n");
|
||
|
PR_fprintf(outfile, "## SUBJECT: %s\n", cert->subjectName);
|
||
|
PR_fprintf(outfile, "## ISSUER: %s\n", cert->issuerName);
|
||
|
PR_fprintf(outfile, "## SERIAL NUMBER: ");
|
||
|
printHexString(outfile, &cert->serialNumber);
|
||
|
{ /* XXX should be separate function. */
|
||
|
int64 timeBefore, timeAfter;
|
||
|
PRExplodedTime beforePrintable, afterPrintable;
|
||
|
char *beforestr, *afterstr;
|
||
|
DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore);
|
||
|
DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter);
|
||
|
PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable);
|
||
|
PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable);
|
||
|
beforestr = PORT_Alloc(100);
|
||
|
afterstr = PORT_Alloc(100);
|
||
|
PR_FormatTime(beforestr, 100, "%a %b %d %H:%M:%S %Y", &beforePrintable);
|
||
|
PR_FormatTime(afterstr, 100, "%a %b %d %H:%M:%S %Y", &afterPrintable);
|
||
|
PR_fprintf(outfile, "## VALIDITY: %s to %s\n", beforestr, afterstr);
|
||
|
}
|
||
|
PR_fprintf(outfile, "\n");
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
SECStatus
|
||
|
dumpCertEntry(certDBEntryCert *entry, int num, PRFileDesc *outfile)
|
||
|
{
|
||
|
#if 0
|
||
|
NSSLOWCERTCertificate *cert;
|
||
|
/* should we check for existing duplicates? */
|
||
|
cert = nsslowcert_DecodeDERCertificate(&entry->cert.derCert,
|
||
|
entry->cert.nickname);
|
||
|
#else
|
||
|
CERTCertificate *cert;
|
||
|
cert = CERT_DecodeDERCertificate(&entry->derCert, PR_FALSE, NULL);
|
||
|
#endif
|
||
|
if (!cert) {
|
||
|
fprintf(stderr, "Failed to decode certificate.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
cert->trust = (CERTCertTrust *)&entry->trust;
|
||
|
dumpCertificate(cert, num, outfile);
|
||
|
CERT_DestroyCertificate(cert);
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
SECStatus
|
||
|
dumpSubjectEntry(certDBEntrySubject *entry, int num, PRFileDesc *outfile)
|
||
|
{
|
||
|
char *subjectName = CERT_DerNameToAscii(&entry->derSubject);
|
||
|
|
||
|
PR_fprintf(outfile, "Subject: %3d\n", num);
|
||
|
PR_fprintf(outfile, "------------\n");
|
||
|
PR_fprintf(outfile, "## %s\n", subjectName);
|
||
|
if (entry->nickname)
|
||
|
PR_fprintf(outfile, "## Subject nickname: %s\n", entry->nickname);
|
||
|
if (entry->emailAddrs) {
|
||
|
unsigned int n;
|
||
|
for (n = 0; n < entry->nemailAddrs && entry->emailAddrs[n]; ++n) {
|
||
|
char * emailAddr = entry->emailAddrs[n];
|
||
|
if (emailAddr[0]) {
|
||
|
PR_fprintf(outfile, "## Subject email address: %s\n",
|
||
|
emailAddr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
PR_fprintf(outfile, "## This subject has %d cert(s).\n", entry->ncerts);
|
||
|
PR_fprintf(outfile, "\n");
|
||
|
PORT_Free(subjectName);
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
SECStatus
|
||
|
dumpNicknameEntry(certDBEntryNickname *entry, int num, PRFileDesc *outfile)
|
||
|
{
|
||
|
PR_fprintf(outfile, "Nickname: %3d\n", num);
|
||
|
PR_fprintf(outfile, "-------------\n");
|
||
|
PR_fprintf(outfile, "## \"%s\"\n\n", entry->nickname);
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
SECStatus
|
||
|
dumpSMimeEntry(certDBEntrySMime *entry, int num, PRFileDesc *outfile)
|
||
|
{
|
||
|
PR_fprintf(outfile, "S/MIME Profile: %3d\n", num);
|
||
|
PR_fprintf(outfile, "-------------------\n");
|
||
|
PR_fprintf(outfile, "## \"%s\"\n", entry->emailAddr);
|
||
|
#ifdef OLDWAY
|
||
|
PR_fprintf(outfile, "## OPTIONS: ");
|
||
|
printHexString(outfile, &entry->smimeOptions);
|
||
|
PR_fprintf(outfile, "## TIMESTAMP: ");
|
||
|
printHexString(outfile, &entry->optionsDate);
|
||
|
#else
|
||
|
SECU_PrintAny(stdout, &entry->smimeOptions, "## OPTIONS ", 0);
|
||
|
fflush(stdout);
|
||
|
if (entry->optionsDate.len && entry->optionsDate.data)
|
||
|
PR_fprintf(outfile, "## TIMESTAMP: %.*s\n",
|
||
|
entry->optionsDate.len, entry->optionsDate.data);
|
||
|
#endif
|
||
|
PR_fprintf(outfile, "\n");
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
SECStatus
|
||
|
mapCertEntries(certDBArray *dbArray)
|
||
|
{
|
||
|
certDBEntryCert *certEntry;
|
||
|
certDBEntrySubject *subjectEntry;
|
||
|
certDBEntryListNode *certNode, *subjNode;
|
||
|
certDBSubjectEntryMap *smap;
|
||
|
certDBEntryMap *map;
|
||
|
PRArenaPool *tmparena;
|
||
|
SECItem derSubject;
|
||
|
SECItem certKey;
|
||
|
PRCList *cElem, *sElem;
|
||
|
|
||
|
/* Arena for decoded entries */
|
||
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||
|
if (tmparena == NULL) {
|
||
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
/* Iterate over cert entries and map them to subject entries.
|
||
|
* NOTE: mapSubjectEntries must be called first to alloc memory
|
||
|
* for array of subject->cert map.
|
||
|
*/
|
||
|
for (cElem = PR_LIST_HEAD(&dbArray->certs.link);
|
||
|
cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
|
||
|
certNode = LISTNODE_CAST(cElem);
|
||
|
certEntry = (certDBEntryCert *)&certNode->entry;
|
||
|
map = (certDBEntryMap *)certNode->appData;
|
||
|
CERT_NameFromDERCert(&certEntry->derCert, &derSubject);
|
||
|
CERT_KeyFromDERCert(tmparena, &certEntry->derCert, &certKey);
|
||
|
/* Loop over found subjects for cert's DN. */
|
||
|
for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
|
||
|
sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
|
||
|
subjNode = LISTNODE_CAST(sElem);
|
||
|
subjectEntry = (certDBEntrySubject *)&subjNode->entry;
|
||
|
if (SECITEM_ItemsAreEqual(&derSubject, &subjectEntry->derSubject)) {
|
||
|
unsigned int i;
|
||
|
/* Found matching subject name, create link. */
|
||
|
map->pSubject = subjNode;
|
||
|
/* Make sure subject entry has cert's key. */
|
||
|
for (i=0; i<subjectEntry->ncerts; i++) {
|
||
|
if (SECITEM_ItemsAreEqual(&certKey,
|
||
|
&subjectEntry->certKeys[i])) {
|
||
|
/* Found matching cert key. */
|
||
|
smap = (certDBSubjectEntryMap *)subjNode->appData;
|
||
|
smap->pCerts[i] = certNode;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
PORT_FreeArena(tmparena, PR_FALSE);
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
SECStatus
|
||
|
mapSubjectEntries(certDBArray *dbArray)
|
||
|
{
|
||
|
certDBEntrySubject *subjectEntry;
|
||
|
certDBEntryListNode *subjNode;
|
||
|
certDBSubjectEntryMap *subjMap;
|
||
|
PRCList *sElem;
|
||
|
|
||
|
for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
|
||
|
sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
|
||
|
/* Iterate over subject entries and map subjects to nickname
|
||
|
* and smime entries. The cert<->subject map will be handled
|
||
|
* by a subsequent call to mapCertEntries.
|
||
|
*/
|
||
|
subjNode = LISTNODE_CAST(sElem);
|
||
|
subjectEntry = (certDBEntrySubject *)&subjNode->entry;
|
||
|
subjMap = (certDBSubjectEntryMap *)subjNode->appData;
|
||
|
/* need to alloc memory here for array of matching certs. */
|
||
|
subjMap->pCerts = PORT_ArenaAlloc(subjMap->arena,
|
||
|
subjectEntry->ncerts*sizeof(int));
|
||
|
subjMap->numCerts = subjectEntry->ncerts;
|
||
|
subjMap->pNickname = NoNickname;
|
||
|
subjMap->pSMime = NoSMime;
|
||
|
|
||
|
if (subjectEntry->nickname) {
|
||
|
/* Subject should have a nickname entry, so create a link. */
|
||
|
PRCList *nElem;
|
||
|
for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
|
||
|
nElem != &dbArray->nicknames.link;
|
||
|
nElem = PR_NEXT_LINK(nElem)) {
|
||
|
certDBEntryListNode *nickNode;
|
||
|
certDBEntryNickname *nicknameEntry;
|
||
|
/* Look for subject's nickname in nickname entries. */
|
||
|
nickNode = LISTNODE_CAST(nElem);
|
||
|
nicknameEntry = (certDBEntryNickname *)&nickNode->entry;
|
||
|
if (PL_strcmp(subjectEntry->nickname,
|
||
|
nicknameEntry->nickname) == 0) {
|
||
|
/* Found a nickname entry for subject's nickname. */
|
||
|
if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
|
||
|
&nicknameEntry->subjectName)) {
|
||
|
certDBEntryMap *nickMap;
|
||
|
nickMap = (certDBEntryMap *)nickNode->appData;
|
||
|
/* Nickname and subject match. */
|
||
|
subjMap->pNickname = nickNode;
|
||
|
nickMap->pSubject = subjNode;
|
||
|
} else if (subjMap->pNickname == NoNickname) {
|
||
|
/* Nickname entry found is for diff. subject. */
|
||
|
subjMap->pNickname = WrongEntry;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (subjectEntry->emailAddrs) {
|
||
|
unsigned int n;
|
||
|
for (n = 0; n < subjectEntry->nemailAddrs &&
|
||
|
subjectEntry->emailAddrs[n]; ++n) {
|
||
|
char * emailAddr = subjectEntry->emailAddrs[n];
|
||
|
if (emailAddr[0]) {
|
||
|
PRCList *mElem;
|
||
|
/* Subject should have an smime entry, so create a link. */
|
||
|
for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
|
||
|
mElem != &dbArray->smime.link;
|
||
|
mElem = PR_NEXT_LINK(mElem)) {
|
||
|
certDBEntryListNode *smimeNode;
|
||
|
certDBEntrySMime *smimeEntry;
|
||
|
/* Look for subject's email in S/MIME entries. */
|
||
|
smimeNode = LISTNODE_CAST(mElem);
|
||
|
smimeEntry = (certDBEntrySMime *)&smimeNode->entry;
|
||
|
if (PL_strcmp(emailAddr,
|
||
|
smimeEntry->emailAddr) == 0) {
|
||
|
/* Found a S/MIME entry for subject's email. */
|
||
|
if (SECITEM_ItemsAreEqual(
|
||
|
&subjectEntry->derSubject,
|
||
|
&smimeEntry->subjectName)) {
|
||
|
certDBEntryMap *smimeMap;
|
||
|
/* S/MIME entry and subject match. */
|
||
|
subjMap->pSMime = smimeNode;
|
||
|
smimeMap = (certDBEntryMap *)smimeNode->appData;
|
||
|
smimeMap->pSubject = subjNode;
|
||
|
} else if (subjMap->pSMime == NoSMime) {
|
||
|
/* S/MIME entry found is for diff. subject. */
|
||
|
subjMap->pSMime = WrongEntry;
|
||
|
}
|
||
|
}
|
||
|
} /* end for */
|
||
|
} /* endif (emailAddr[0]) */
|
||
|
} /* end for */
|
||
|
} /* endif (subjectEntry->emailAddrs) */
|
||
|
}
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
printnode(dbDebugInfo *info, const char *str, int num)
|
||
|
{
|
||
|
if (!info->dograph)
|
||
|
return;
|
||
|
if (num < 0) {
|
||
|
PR_fprintf(info->graphfile, str);
|
||
|
} else {
|
||
|
PR_fprintf(info->graphfile, str, num);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
map_handle_is_ok(dbDebugInfo *info, void *mapPtr, int indent)
|
||
|
{
|
||
|
if (mapPtr == NULL) {
|
||
|
if (indent > 0)
|
||
|
printnode(info, " ", -1);
|
||
|
if (indent >= 0)
|
||
|
printnode(info, "******************* ", -1);
|
||
|
return PR_FALSE;
|
||
|
} else if (mapPtr == WrongEntry) {
|
||
|
if (indent > 0)
|
||
|
printnode(info, " ", -1);
|
||
|
if (indent >= 0)
|
||
|
printnode(info, "??????????????????? ", -1);
|
||
|
return PR_FALSE;
|
||
|
} else {
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* these call each other */
|
||
|
void print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap,
|
||
|
int direction);
|
||
|
void print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap,
|
||
|
int direction);
|
||
|
void print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap,
|
||
|
int direction, int optindex, int opttype);
|
||
|
void print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap,
|
||
|
int direction);
|
||
|
|
||
|
/* Given an smime entry, print its unique identifier. If GOLEFT is
|
||
|
* specified, print the cert<-subject<-smime map, else just print
|
||
|
* the smime entry.
|
||
|
*/
|
||
|
void
|
||
|
print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, int direction)
|
||
|
{
|
||
|
certDBSubjectEntryMap *subjMap;
|
||
|
certDBEntryListNode *subjNode;
|
||
|
if (direction == GOLEFT) {
|
||
|
/* Need to output subject and cert first, see print_subject_graph */
|
||
|
subjNode = smimeMap->pSubject;
|
||
|
if (map_handle_is_ok(info, (void *)subjNode, 1)) {
|
||
|
subjMap = (certDBSubjectEntryMap *)subjNode->appData;
|
||
|
print_subject_graph(info, subjMap, GOLEFT,
|
||
|
smimeMap->index, certDBEntryTypeSMimeProfile);
|
||
|
} else {
|
||
|
printnode(info, "<---- S/MIME %5d ", smimeMap->index);
|
||
|
info->dbErrors[NoSubjectForSMime]++;
|
||
|
}
|
||
|
} else {
|
||
|
printnode(info, "S/MIME %5d ", smimeMap->index);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Given a nickname entry, print its unique identifier. If GOLEFT is
|
||
|
* specified, print the cert<-subject<-nickname map, else just print
|
||
|
* the nickname entry.
|
||
|
*/
|
||
|
void
|
||
|
print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, int direction)
|
||
|
{
|
||
|
certDBSubjectEntryMap *subjMap;
|
||
|
certDBEntryListNode *subjNode;
|
||
|
if (direction == GOLEFT) {
|
||
|
/* Need to output subject and cert first, see print_subject_graph */
|
||
|
subjNode = nickMap->pSubject;
|
||
|
if (map_handle_is_ok(info, (void *)subjNode, 1)) {
|
||
|
subjMap = (certDBSubjectEntryMap *)subjNode->appData;
|
||
|
print_subject_graph(info, subjMap, GOLEFT,
|
||
|
nickMap->index, certDBEntryTypeNickname);
|
||
|
} else {
|
||
|
printnode(info, "<---- Nickname %5d ", nickMap->index);
|
||
|
info->dbErrors[NoSubjectForNickname]++;
|
||
|
}
|
||
|
} else {
|
||
|
printnode(info, "Nickname %5d ", nickMap->index);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Given a subject entry, if going right print the graph of the nickname|smime
|
||
|
* that it maps to (by its unique identifier); and if going left
|
||
|
* print the list of certs that it points to.
|
||
|
*/
|
||
|
void
|
||
|
print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap,
|
||
|
int direction, int optindex, int opttype)
|
||
|
{
|
||
|
certDBEntryMap *map;
|
||
|
certDBEntryListNode *node;
|
||
|
int i;
|
||
|
/* The first line of output always contains the cert id, subject id,
|
||
|
* and nickname|smime id. Subsequent lines may contain additional
|
||
|
* cert id's for the subject if going left or both directions.
|
||
|
* Ex. of printing the graph for a subject entry:
|
||
|
* Cert 3 <- Subject 5 -> Nickname 32
|
||
|
* Cert 8 /
|
||
|
* Cert 9 /
|
||
|
* means subject 5 has 3 certs, 3, 8, and 9, and corresponds
|
||
|
* to nickname entry 32.
|
||
|
* To accomplish the above, it is required to dump the entire first
|
||
|
* line left-to-right, regardless of the input direction, and then
|
||
|
* finish up any remaining cert entries. Hence the code is uglier
|
||
|
* than one may expect.
|
||
|
*/
|
||
|
if (direction == GOLEFT || direction == GOBOTH) {
|
||
|
/* In this case, nothing should be output until the first cert is
|
||
|
* located and output (cert 3 in the above example).
|
||
|
*/
|
||
|
if (subjMap->numCerts == 0 || subjMap->pCerts == NULL)
|
||
|
/* XXX uh-oh */
|
||
|
return;
|
||
|
/* get the first cert and dump it. */
|
||
|
node = subjMap->pCerts[0];
|
||
|
if (map_handle_is_ok(info, (void *)node, 0)) {
|
||
|
map = (certDBEntryMap *)node->appData;
|
||
|
/* going left here stops. */
|
||
|
print_cert_graph(info, map, GOLEFT);
|
||
|
} else {
|
||
|
info->dbErrors[SubjectHasNoKeyForCert]++;
|
||
|
}
|
||
|
/* Now it is safe to output the subject id. */
|
||
|
if (direction == GOLEFT)
|
||
|
printnode(info, "Subject %5d <---- ", subjMap->index);
|
||
|
else /* direction == GOBOTH */
|
||
|
printnode(info, "Subject %5d ----> ", subjMap->index);
|
||
|
}
|
||
|
if (direction == GORIGHT || direction == GOBOTH) {
|
||
|
/* Okay, now output the nickname|smime for this subject. */
|
||
|
if (direction != GOBOTH) /* handled above */
|
||
|
printnode(info, "Subject %5d ----> ", subjMap->index);
|
||
|
if (subjMap->pNickname) {
|
||
|
node = subjMap->pNickname;
|
||
|
if (map_handle_is_ok(info, (void *)node, 0)) {
|
||
|
map = (certDBEntryMap *)node->appData;
|
||
|
/* going right here stops. */
|
||
|
print_nickname_graph(info, map, GORIGHT);
|
||
|
}
|
||
|
}
|
||
|
if (subjMap->pSMime) {
|
||
|
node = subjMap->pSMime;
|
||
|
if (map_handle_is_ok(info, (void *)node, 0)) {
|
||
|
map = (certDBEntryMap *)node->appData;
|
||
|
/* going right here stops. */
|
||
|
print_smime_graph(info, map, GORIGHT);
|
||
|
}
|
||
|
}
|
||
|
if (!subjMap->pNickname && !subjMap->pSMime) {
|
||
|
printnode(info, "******************* ", -1);
|
||
|
info->dbErrors[NoNicknameOrSMimeForSubject]++;
|
||
|
}
|
||
|
if (subjMap->pNickname && subjMap->pSMime) {
|
||
|
info->dbErrors[NicknameAndSMimeEntries]++;
|
||
|
}
|
||
|
}
|
||
|
if (direction != GORIGHT) { /* going right has only one cert */
|
||
|
if (opttype == certDBEntryTypeNickname)
|
||
|
printnode(info, "Nickname %5d ", optindex);
|
||
|
else if (opttype == certDBEntryTypeSMimeProfile)
|
||
|
printnode(info, "S/MIME %5d ", optindex);
|
||
|
for (i=1 /* 1st one already done */; i<subjMap->numCerts; i++) {
|
||
|
printnode(info, "\n", -1); /* start a new line */
|
||
|
node = subjMap->pCerts[i];
|
||
|
if (map_handle_is_ok(info, (void *)node, 0)) {
|
||
|
map = (certDBEntryMap *)node->appData;
|
||
|
/* going left here stops. */
|
||
|
print_cert_graph(info, map, GOLEFT);
|
||
|
printnode(info, "/", -1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Given a cert entry, print its unique identifer. If GORIGHT is specified,
|
||
|
* print the cert->subject->nickname|smime map, else just print
|
||
|
* the cert entry.
|
||
|
*/
|
||
|
void
|
||
|
print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, int direction)
|
||
|
{
|
||
|
certDBSubjectEntryMap *subjMap;
|
||
|
certDBEntryListNode *subjNode;
|
||
|
if (direction == GOLEFT) {
|
||
|
printnode(info, "Cert %5d <---- ", certMap->index);
|
||
|
/* only want cert entry, terminate here. */
|
||
|
return;
|
||
|
}
|
||
|
/* Keep going right then. */
|
||
|
printnode(info, "Cert %5d ----> ", certMap->index);
|
||
|
subjNode = certMap->pSubject;
|
||
|
if (map_handle_is_ok(info, (void *)subjNode, 0)) {
|
||
|
subjMap = (certDBSubjectEntryMap *)subjNode->appData;
|
||
|
print_subject_graph(info, subjMap, GORIGHT, -1, -1);
|
||
|
} else {
|
||
|
info->dbErrors[NoSubjectForCert]++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SECStatus
|
||
|
computeDBGraph(certDBArray *dbArray, dbDebugInfo *info)
|
||
|
{
|
||
|
PRCList *cElem, *sElem, *nElem, *mElem;
|
||
|
certDBEntryListNode *node;
|
||
|
certDBEntryMap *map;
|
||
|
certDBSubjectEntryMap *subjMap;
|
||
|
|
||
|
/* Graph is of this form:
|
||
|
*
|
||
|
* certs:
|
||
|
* cert ---> subject ---> (nickname|smime)
|
||
|
*
|
||
|
* subjects:
|
||
|
* cert <--- subject ---> (nickname|smime)
|
||
|
*
|
||
|
* nicknames and smime:
|
||
|
* cert <--- subject <--- (nickname|smime)
|
||
|
*/
|
||
|
|
||
|
/* Print cert graph. */
|
||
|
for (cElem = PR_LIST_HEAD(&dbArray->certs.link);
|
||
|
cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
|
||
|
/* Print graph of everything to right of cert entry. */
|
||
|
node = LISTNODE_CAST(cElem);
|
||
|
map = (certDBEntryMap *)node->appData;
|
||
|
print_cert_graph(info, map, GORIGHT);
|
||
|
printnode(info, "\n", -1);
|
||
|
}
|
||
|
printnode(info, "\n", -1);
|
||
|
|
||
|
/* Print subject graph. */
|
||
|
for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
|
||
|
sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
|
||
|
/* Print graph of everything to both sides of subject entry. */
|
||
|
node = LISTNODE_CAST(sElem);
|
||
|
subjMap = (certDBSubjectEntryMap *)node->appData;
|
||
|
print_subject_graph(info, subjMap, GOBOTH, -1, -1);
|
||
|
printnode(info, "\n", -1);
|
||
|
}
|
||
|
printnode(info, "\n", -1);
|
||
|
|
||
|
/* Print nickname graph. */
|
||
|
for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
|
||
|
nElem != &dbArray->nicknames.link; nElem = PR_NEXT_LINK(nElem)) {
|
||
|
/* Print graph of everything to left of nickname entry. */
|
||
|
node = LISTNODE_CAST(nElem);
|
||
|
map = (certDBEntryMap *)node->appData;
|
||
|
print_nickname_graph(info, map, GOLEFT);
|
||
|
printnode(info, "\n", -1);
|
||
|
}
|
||
|
printnode(info, "\n", -1);
|
||
|
|
||
|
/* Print smime graph. */
|
||
|
for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
|
||
|
mElem != &dbArray->smime.link; mElem = PR_NEXT_LINK(mElem)) {
|
||
|
/* Print graph of everything to left of smime entry. */
|
||
|
node = LISTNODE_CAST(mElem);
|
||
|
if (node == NULL) break;
|
||
|
map = (certDBEntryMap *)node->appData;
|
||
|
print_smime_graph(info, map, GOLEFT);
|
||
|
printnode(info, "\n", -1);
|
||
|
}
|
||
|
printnode(info, "\n", -1);
|
||
|
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* List the entries in the db, showing handles between entry types.
|
||
|
*/
|
||
|
void
|
||
|
verboseOutput(certDBArray *dbArray, dbDebugInfo *info)
|
||
|
{
|
||
|
int i, ref;
|
||
|
PRCList *elem;
|
||
|
certDBEntryListNode *node;
|
||
|
certDBEntryMap *map;
|
||
|
certDBSubjectEntryMap *smap;
|
||
|
certDBEntrySubject *subjectEntry;
|
||
|
|
||
|
/* List certs */
|
||
|
for (elem = PR_LIST_HEAD(&dbArray->certs.link);
|
||
|
elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) {
|
||
|
node = LISTNODE_CAST(elem);
|
||
|
map = (certDBEntryMap *)node->appData;
|
||
|
dumpCertEntry((certDBEntryCert*)&node->entry, map->index, info->out);
|
||
|
/* walk the cert handle to it's subject entry */
|
||
|
if (map_handle_is_ok(info, map->pSubject, -1)) {
|
||
|
smap = (certDBSubjectEntryMap *)map->pSubject->appData;
|
||
|
ref = smap->index;
|
||
|
PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
|
||
|
} else {
|
||
|
PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
|
||
|
}
|
||
|
}
|
||
|
/* List subjects */
|
||
|
for (elem = PR_LIST_HEAD(&dbArray->subjects.link);
|
||
|
elem != &dbArray->subjects.link; elem = PR_NEXT_LINK(elem)) {
|
||
|
int refs = 0;
|
||
|
node = LISTNODE_CAST(elem);
|
||
|
subjectEntry = (certDBEntrySubject *)&node->entry;
|
||
|
smap = (certDBSubjectEntryMap *)node->appData;
|
||
|
dumpSubjectEntry(subjectEntry, smap->index, info->out);
|
||
|
/* iterate over subject's certs */
|
||
|
for (i=0; i<smap->numCerts; i++) {
|
||
|
/* walk each subject handle to it's cert entries */
|
||
|
if (map_handle_is_ok(info, smap->pCerts[i], -1)) {
|
||
|
ref = ((certDBEntryMap *)smap->pCerts[i]->appData)->index;
|
||
|
PR_fprintf(info->out, "-->(%d. certificate %d)\n", i, ref);
|
||
|
} else {
|
||
|
PR_fprintf(info->out, "-->(%d. MISSING CERT ENTRY)\n", i);
|
||
|
}
|
||
|
}
|
||
|
if (subjectEntry->nickname) {
|
||
|
++refs;
|
||
|
/* walk each subject handle to it's nickname entry */
|
||
|
if (map_handle_is_ok(info, smap->pNickname, -1)) {
|
||
|
ref = ((certDBEntryMap *)smap->pNickname->appData)->index;
|
||
|
PR_fprintf(info->out, "-->(nickname %d)\n", ref);
|
||
|
} else {
|
||
|
PR_fprintf(info->out, "-->(MISSING NICKNAME ENTRY)\n");
|
||
|
}
|
||
|
}
|
||
|
if (subjectEntry->nemailAddrs &&
|
||
|
subjectEntry->emailAddrs &&
|
||
|
subjectEntry->emailAddrs[0] &&
|
||
|
subjectEntry->emailAddrs[0][0]) {
|
||
|
++refs;
|
||
|
/* walk each subject handle to it's smime entry */
|
||
|
if (map_handle_is_ok(info, smap->pSMime, -1)) {
|
||
|
ref = ((certDBEntryMap *)smap->pSMime->appData)->index;
|
||
|
PR_fprintf(info->out, "-->(s/mime %d)\n", ref);
|
||
|
} else {
|
||
|
PR_fprintf(info->out, "-->(MISSING S/MIME ENTRY)\n");
|
||
|
}
|
||
|
}
|
||
|
if (!refs) {
|
||
|
PR_fprintf(info->out, "-->(NO NICKNAME+S/MIME ENTRY)\n");
|
||
|
}
|
||
|
PR_fprintf(info->out, "\n\n");
|
||
|
}
|
||
|
for (elem = PR_LIST_HEAD(&dbArray->nicknames.link);
|
||
|
elem != &dbArray->nicknames.link; elem = PR_NEXT_LINK(elem)) {
|
||
|
node = LISTNODE_CAST(elem);
|
||
|
map = (certDBEntryMap *)node->appData;
|
||
|
dumpNicknameEntry((certDBEntryNickname*)&node->entry, map->index,
|
||
|
info->out);
|
||
|
if (map_handle_is_ok(info, map->pSubject, -1)) {
|
||
|
ref = ((certDBEntryMap *)map->pSubject->appData)->index;
|
||
|
PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
|
||
|
} else {
|
||
|
PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
|
||
|
}
|
||
|
}
|
||
|
for (elem = PR_LIST_HEAD(&dbArray->smime.link);
|
||
|
elem != &dbArray->smime.link; elem = PR_NEXT_LINK(elem)) {
|
||
|
node = LISTNODE_CAST(elem);
|
||
|
map = (certDBEntryMap *)node->appData;
|
||
|
dumpSMimeEntry((certDBEntrySMime*)&node->entry, map->index, info->out);
|
||
|
if (map_handle_is_ok(info, map->pSubject, -1)) {
|
||
|
ref = ((certDBEntryMap *)map->pSubject->appData)->index;
|
||
|
PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
|
||
|
} else {
|
||
|
PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
|
||
|
}
|
||
|
}
|
||
|
PR_fprintf(info->out, "\n\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/* A callback function, intended to be called from nsslowcert_TraverseDBEntries
|
||
|
* Builds a PRCList of DB entries of the specified type.
|
||
|
*/
|
||
|
SECStatus
|
||
|
SEC_GetCertDBEntryList(SECItem *dbdata, SECItem *dbkey,
|
||
|
certDBEntryType entryType, void *pdata)
|
||
|
{
|
||
|
certDBEntry * entry;
|
||
|
certDBEntryListNode * node;
|
||
|
PRCList * list = (PRCList *)pdata;
|
||
|
|
||
|
if (!dbdata || !dbkey || !pdata || !dbdata->data || !dbkey->data) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
entry = nsslowcert_DecodeAnyDBEntry(dbdata, dbkey, entryType, NULL);
|
||
|
if (!entry) {
|
||
|
return SECSuccess; /* skip it */
|
||
|
}
|
||
|
node = PORT_ArenaZNew(entry->common.arena, certDBEntryListNode);
|
||
|
if (!node) {
|
||
|
/* DestroyDBEntry(entry); */
|
||
|
PLArenaPool *arena = entry->common.arena;
|
||
|
PORT_Memset(&entry->common, 0, sizeof entry->common);
|
||
|
PORT_FreeArena(arena, PR_FALSE);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
node->entry = *entry; /* crude but effective. */
|
||
|
PR_INIT_CLIST(&node->link);
|
||
|
PR_INSERT_BEFORE(&node->link, list);
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
fillDBEntryArray(NSSLOWCERTCertDBHandle *handle, certDBEntryType type,
|
||
|
certDBEntryListNode *list)
|
||
|
{
|
||
|
PRCList *elem;
|
||
|
certDBEntryListNode *node;
|
||
|
certDBEntryMap *mnode;
|
||
|
certDBSubjectEntryMap *smnode;
|
||
|
PRArenaPool *arena;
|
||
|
int count = 0;
|
||
|
|
||
|
/* Initialize a dummy entry in the list. The list head will be the
|
||
|
* next element, so this element is skipped by for loops.
|
||
|
*/
|
||
|
PR_INIT_CLIST((PRCList *)list);
|
||
|
/* Collect all of the cert db entries for this type into a list. */
|
||
|
nsslowcert_TraverseDBEntries(handle, type, SEC_GetCertDBEntryList, list);
|
||
|
|
||
|
for (elem = PR_LIST_HEAD(&list->link);
|
||
|
elem != &list->link; elem = PR_NEXT_LINK(elem)) {
|
||
|
/* Iterate over the entries and ... */
|
||
|
node = (certDBEntryListNode *)elem;
|
||
|
if (type != certDBEntryTypeSubject) {
|
||
|
arena = PORT_NewArena(sizeof(*mnode));
|
||
|
mnode = PORT_ArenaZNew(arena, certDBEntryMap);
|
||
|
mnode->arena = arena;
|
||
|
/* ... assign a unique index number to each node, and ... */
|
||
|
mnode->index = count;
|
||
|
/* ... set the map pointer for the node. */
|
||
|
node->appData = (void *)mnode;
|
||
|
} else {
|
||
|
/* allocate some room for the cert pointers also */
|
||
|
arena = PORT_NewArena(sizeof(*smnode) + 20*sizeof(void *));
|
||
|
smnode = PORT_ArenaZNew(arena, certDBSubjectEntryMap);
|
||
|
smnode->arena = arena;
|
||
|
smnode->index = count;
|
||
|
node->appData = (void *)smnode;
|
||
|
}
|
||
|
count++;
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
freeDBEntryList(PRCList *list)
|
||
|
{
|
||
|
PRCList *next, *elem;
|
||
|
certDBEntryListNode *node;
|
||
|
certDBEntryMap *map;
|
||
|
|
||
|
for (elem = PR_LIST_HEAD(list); elem != list;) {
|
||
|
next = PR_NEXT_LINK(elem);
|
||
|
node = (certDBEntryListNode *)elem;
|
||
|
map = (certDBEntryMap *)node->appData;
|
||
|
PR_REMOVE_LINK(&node->link);
|
||
|
PORT_FreeArena(map->arena, PR_TRUE);
|
||
|
PORT_FreeArena(node->entry.common.arena, PR_TRUE);
|
||
|
elem = next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DBCK_DebugDB(NSSLOWCERTCertDBHandle *handle, PRFileDesc *out,
|
||
|
PRFileDesc *mailfile)
|
||
|
{
|
||
|
int i, nCertsFound, nSubjFound, nErr;
|
||
|
int nCerts, nSubjects, nSubjCerts, nNicknames, nSMime, nRevocation;
|
||
|
PRCList *elem;
|
||
|
char c;
|
||
|
dbDebugInfo info;
|
||
|
certDBArray dbArray;
|
||
|
|
||
|
PORT_Memset(&dbArray, 0, sizeof(dbArray));
|
||
|
PORT_Memset(&info, 0, sizeof(info));
|
||
|
info.verbose = (PRBool)(out != NULL);
|
||
|
info.dograph = info.verbose;
|
||
|
info.out = (out) ? out : PR_STDOUT;
|
||
|
info.graphfile = mailfile ? mailfile : PR_STDOUT;
|
||
|
|
||
|
/* Fill the array structure with cert/subject/nickname/smime entries. */
|
||
|
dbArray.numCerts = fillDBEntryArray(handle, certDBEntryTypeCert,
|
||
|
&dbArray.certs);
|
||
|
dbArray.numSubjects = fillDBEntryArray(handle, certDBEntryTypeSubject,
|
||
|
&dbArray.subjects);
|
||
|
dbArray.numNicknames = fillDBEntryArray(handle, certDBEntryTypeNickname,
|
||
|
&dbArray.nicknames);
|
||
|
dbArray.numSMime = fillDBEntryArray(handle, certDBEntryTypeSMimeProfile,
|
||
|
&dbArray.smime);
|
||
|
dbArray.numRevocation= fillDBEntryArray(handle, certDBEntryTypeRevocation,
|
||
|
&dbArray.revocation);
|
||
|
|
||
|
/* Compute the map between the database entries. */
|
||
|
mapSubjectEntries(&dbArray);
|
||
|
mapCertEntries(&dbArray);
|
||
|
computeDBGraph(&dbArray, &info);
|
||
|
|
||
|
/* Store the totals for later reference. */
|
||
|
nCerts = dbArray.numCerts;
|
||
|
nSubjects = dbArray.numSubjects;
|
||
|
nNicknames = dbArray.numNicknames;
|
||
|
nSMime = dbArray.numSMime;
|
||
|
nRevocation= dbArray.numRevocation;
|
||
|
nSubjCerts = 0;
|
||
|
for (elem = PR_LIST_HEAD(&dbArray.subjects.link);
|
||
|
elem != &dbArray.subjects.link; elem = PR_NEXT_LINK(elem)) {
|
||
|
certDBSubjectEntryMap *smap;
|
||
|
smap = (certDBSubjectEntryMap *)LISTNODE_CAST(elem)->appData;
|
||
|
nSubjCerts += smap->numCerts;
|
||
|
}
|
||
|
|
||
|
if (info.verbose) {
|
||
|
/* Dump the database contents. */
|
||
|
verboseOutput(&dbArray, &info);
|
||
|
}
|
||
|
|
||
|
freeDBEntryList(&dbArray.certs.link);
|
||
|
freeDBEntryList(&dbArray.subjects.link);
|
||
|
freeDBEntryList(&dbArray.nicknames.link);
|
||
|
freeDBEntryList(&dbArray.smime.link);
|
||
|
freeDBEntryList(&dbArray.revocation.link);
|
||
|
|
||
|
PR_fprintf(info.out, "\n");
|
||
|
PR_fprintf(info.out, "Database statistics:\n");
|
||
|
PR_fprintf(info.out, "N0: Found %4d Certificate entries.\n",
|
||
|
nCerts);
|
||
|
PR_fprintf(info.out, "N1: Found %4d Subject entries (unique DN's).\n",
|
||
|
nSubjects);
|
||
|
PR_fprintf(info.out, "N2: Found %4d Cert keys within Subject entries.\n",
|
||
|
nSubjCerts);
|
||
|
PR_fprintf(info.out, "N3: Found %4d Nickname entries.\n",
|
||
|
nNicknames);
|
||
|
PR_fprintf(info.out, "N4: Found %4d S/MIME entries.\n",
|
||
|
nSMime);
|
||
|
PR_fprintf(info.out, "N5: Found %4d CRL entries.\n",
|
||
|
nRevocation);
|
||
|
PR_fprintf(info.out, "\n");
|
||
|
|
||
|
nErr = 0;
|
||
|
for (i=0; i < NUM_ERROR_TYPES; i++) {
|
||
|
PR_fprintf(info.out, "E%d: Found %4d %s\n",
|
||
|
i, info.dbErrors[i], errResult[i]);
|
||
|
nErr += info.dbErrors[i];
|
||
|
}
|
||
|
PR_fprintf(info.out, "--------------\n Found %4d errors in database.\n",
|
||
|
nErr);
|
||
|
|
||
|
PR_fprintf(info.out, "\nCertificates:\n");
|
||
|
PR_fprintf(info.out, "N0 == N2 + E%d + E%d\n", NoSubjectForCert,
|
||
|
SubjectHasNoKeyForCert);
|
||
|
nCertsFound = nSubjCerts +
|
||
|
info.dbErrors[NoSubjectForCert] +
|
||
|
info.dbErrors[SubjectHasNoKeyForCert];
|
||
|
c = (nCertsFound == nCerts) ? '=' : '!';
|
||
|
PR_fprintf(info.out, "%d %c= %d + %d + %d\n", nCerts, c, nSubjCerts,
|
||
|
info.dbErrors[NoSubjectForCert],
|
||
|
info.dbErrors[SubjectHasNoKeyForCert]);
|
||
|
PR_fprintf(info.out, "\nSubjects:\n");
|
||
|
PR_fprintf(info.out,
|
||
|
"N1 == N3 + N4 + E%d + E%d + E%d + E%d + E%d - E%d - E%d - E%d\n",
|
||
|
NoNicknameOrSMimeForSubject,
|
||
|
WrongNicknameForSubject,
|
||
|
NoNicknameEntry,
|
||
|
WrongSMimeForSubject,
|
||
|
NoSMimeEntry,
|
||
|
NoSubjectForNickname,
|
||
|
NoSubjectForSMime,
|
||
|
NicknameAndSMimeEntries);
|
||
|
nSubjFound = nNicknames + nSMime +
|
||
|
info.dbErrors[NoNicknameOrSMimeForSubject] +
|
||
|
info.dbErrors[WrongNicknameForSubject] +
|
||
|
info.dbErrors[NoNicknameEntry] +
|
||
|
info.dbErrors[WrongSMimeForSubject] +
|
||
|
info.dbErrors[NoSMimeEntry] -
|
||
|
info.dbErrors[NoSubjectForNickname] -
|
||
|
info.dbErrors[NoSubjectForSMime] -
|
||
|
info.dbErrors[NicknameAndSMimeEntries];
|
||
|
c = (nSubjFound == nSubjects) ? '=' : '!';
|
||
|
PR_fprintf(info.out,
|
||
|
"%2d %c= %2d + %2d + %2d + %2d + %2d + %2d + %2d - %2d - %2d - %2d\n",
|
||
|
nSubjects, c, nNicknames, nSMime,
|
||
|
info.dbErrors[NoNicknameOrSMimeForSubject],
|
||
|
info.dbErrors[WrongNicknameForSubject],
|
||
|
info.dbErrors[NoNicknameEntry],
|
||
|
info.dbErrors[WrongSMimeForSubject],
|
||
|
info.dbErrors[NoSMimeEntry],
|
||
|
info.dbErrors[NoSubjectForNickname],
|
||
|
info.dbErrors[NoSubjectForSMime],
|
||
|
info.dbErrors[NicknameAndSMimeEntries]);
|
||
|
PR_fprintf(info.out, "\n");
|
||
|
}
|
||
|
|
||
|
#ifdef DORECOVER
|
||
|
#include "dbrecover.c"
|
||
|
#endif /* DORECOVER */
|
||
|
|
||
|
enum {
|
||
|
cmd_Debug = 0,
|
||
|
cmd_LongUsage,
|
||
|
cmd_Recover
|
||
|
};
|
||
|
|
||
|
enum {
|
||
|
opt_KeepAll = 0,
|
||
|
opt_CertDir,
|
||
|
opt_Dumpfile,
|
||
|
opt_InputDB,
|
||
|
opt_OutputDB,
|
||
|
opt_Mailfile,
|
||
|
opt_Prompt,
|
||
|
opt_KeepRedundant,
|
||
|
opt_KeepNoSMimeProfile,
|
||
|
opt_Verbose,
|
||
|
opt_KeepExpired
|
||
|
};
|
||
|
|
||
|
static secuCommandFlag dbck_commands[] =
|
||
|
{
|
||
|
{ /* cmd_Debug, */ 'D', PR_FALSE, 0, PR_FALSE },
|
||
|
{ /* cmd_LongUsage,*/ 'H', PR_FALSE, 0, PR_FALSE },
|
||
|
{ /* cmd_Recover, */ 'R', PR_FALSE, 0, PR_FALSE }
|
||
|
};
|
||
|
|
||
|
static secuCommandFlag dbck_options[] =
|
||
|
{
|
||
|
{ /* opt_KeepAll, */ 'a', PR_FALSE, 0, PR_FALSE },
|
||
|
{ /* opt_CertDir, */ 'd', PR_TRUE, 0, PR_FALSE },
|
||
|
{ /* opt_Dumpfile, */ 'f', PR_TRUE, 0, PR_FALSE },
|
||
|
{ /* opt_InputDB, */ 'i', PR_TRUE, 0, PR_FALSE },
|
||
|
{ /* opt_OutputDB, */ 'o', PR_TRUE, 0, PR_FALSE },
|
||
|
{ /* opt_Mailfile, */ 'm', PR_FALSE, 0, PR_FALSE },
|
||
|
{ /* opt_Prompt, */ 'p', PR_FALSE, 0, PR_FALSE },
|
||
|
{ /* opt_KeepRedundant, */ 'r', PR_FALSE, 0, PR_FALSE },
|
||
|
{ /* opt_KeepNoSMimeProfile,*/ 's', PR_FALSE, 0, PR_FALSE },
|
||
|
{ /* opt_Verbose, */ 'v', PR_FALSE, 0, PR_FALSE },
|
||
|
{ /* opt_KeepExpired, */ 'x', PR_FALSE, 0, PR_FALSE }
|
||
|
};
|
||
|
|
||
|
#define CERT_DB_FMT "%s/cert%s.db"
|
||
|
|
||
|
static char *
|
||
|
dbck_certdb_name_cb(void *arg, int dbVersion)
|
||
|
{
|
||
|
const char *configdir = (const char *)arg;
|
||
|
const char *dbver;
|
||
|
char *smpname = NULL;
|
||
|
char *dbname = NULL;
|
||
|
|
||
|
switch (dbVersion) {
|
||
|
case 8:
|
||
|
dbver = "8";
|
||
|
break;
|
||
|
case 7:
|
||
|
dbver = "7";
|
||
|
break;
|
||
|
case 6:
|
||
|
dbver = "6";
|
||
|
break;
|
||
|
case 5:
|
||
|
dbver = "5";
|
||
|
break;
|
||
|
case 4:
|
||
|
default:
|
||
|
dbver = "";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* make sure we return something allocated with PORT_ so we have properly
|
||
|
* matched frees at the end */
|
||
|
smpname = PR_smprintf(CERT_DB_FMT, configdir, dbver);
|
||
|
if (smpname) {
|
||
|
dbname = PORT_Strdup(smpname);
|
||
|
PR_smprintf_free(smpname);
|
||
|
}
|
||
|
return dbname;
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
main(int argc, char **argv)
|
||
|
{
|
||
|
NSSLOWCERTCertDBHandle *certHandle;
|
||
|
|
||
|
PRFileDesc *mailfile = NULL;
|
||
|
PRFileDesc *dumpfile = NULL;
|
||
|
|
||
|
char * pathname = 0;
|
||
|
char * fullname = 0;
|
||
|
char * newdbname = 0;
|
||
|
|
||
|
PRBool removeExpired, requireProfile, singleEntry;
|
||
|
SECStatus rv;
|
||
|
secuCommand dbck;
|
||
|
|
||
|
dbck.numCommands = sizeof(dbck_commands) / sizeof(secuCommandFlag);
|
||
|
dbck.numOptions = sizeof(dbck_options) / sizeof(secuCommandFlag);
|
||
|
dbck.commands = dbck_commands;
|
||
|
dbck.options = dbck_options;
|
||
|
|
||
|
progName = strrchr(argv[0], '/');
|
||
|
progName = progName ? progName+1 : argv[0];
|
||
|
|
||
|
rv = SECU_ParseCommandLine(argc, argv, progName, &dbck);
|
||
|
|
||
|
if (rv != SECSuccess)
|
||
|
Usage(progName);
|
||
|
|
||
|
if (dbck.commands[cmd_LongUsage].activated)
|
||
|
LongUsage(progName);
|
||
|
|
||
|
if (!dbck.commands[cmd_Debug].activated &&
|
||
|
!dbck.commands[cmd_Recover].activated) {
|
||
|
PR_fprintf(PR_STDERR, "Please specify -H, -D or -R.\n");
|
||
|
Usage(progName);
|
||
|
}
|
||
|
|
||
|
removeExpired = !(dbck.options[opt_KeepAll].activated ||
|
||
|
dbck.options[opt_KeepExpired].activated);
|
||
|
|
||
|
requireProfile = !(dbck.options[opt_KeepAll].activated ||
|
||
|
dbck.options[opt_KeepNoSMimeProfile].activated);
|
||
|
|
||
|
singleEntry = !(dbck.options[opt_KeepAll].activated ||
|
||
|
dbck.options[opt_KeepRedundant].activated);
|
||
|
|
||
|
if (dbck.options[opt_OutputDB].activated) {
|
||
|
newdbname = PL_strdup(dbck.options[opt_OutputDB].arg);
|
||
|
} else {
|
||
|
newdbname = PL_strdup("new_cert8.db");
|
||
|
}
|
||
|
|
||
|
/* Create a generic graph of the database. */
|
||
|
if (dbck.options[opt_Mailfile].activated) {
|
||
|
mailfile = PR_Open("./mailfile", PR_RDWR | PR_CREATE_FILE, 00660);
|
||
|
if (!mailfile) {
|
||
|
fprintf(stderr, "Unable to create mailfile.\n");
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Dump all debugging info while running. */
|
||
|
if (dbck.options[opt_Verbose].activated) {
|
||
|
if (dbck.options[opt_Dumpfile].activated) {
|
||
|
dumpfile = PR_Open(dbck.options[opt_Dumpfile].arg,
|
||
|
PR_RDWR | PR_CREATE_FILE, 00660);
|
||
|
if (!dumpfile) {
|
||
|
fprintf(stderr, "Unable to create dumpfile.\n");
|
||
|
return -1;
|
||
|
}
|
||
|
} else {
|
||
|
dumpfile = PR_STDOUT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Set the cert database directory. */
|
||
|
if (dbck.options[opt_CertDir].activated) {
|
||
|
SECU_ConfigDirectory(dbck.options[opt_CertDir].arg);
|
||
|
}
|
||
|
|
||
|
pathname = SECU_ConfigDirectory(NULL);
|
||
|
|
||
|
PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
|
||
|
rv = NSS_NoDB_Init(pathname);
|
||
|
if (rv != SECSuccess) {
|
||
|
fprintf(stderr, "NSS_NoDB_Init failed\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
certHandle = PORT_ZNew(NSSLOWCERTCertDBHandle);
|
||
|
if (!certHandle) {
|
||
|
SECU_PrintError(progName, "unable to get database handle");
|
||
|
return -1;
|
||
|
}
|
||
|
certHandle->ref = 1;
|
||
|
|
||
|
#ifdef NOTYET
|
||
|
/* Open the possibly corrupt database. */
|
||
|
if (dbck.options[opt_InputDB].activated) {
|
||
|
PRFileInfo fileInfo;
|
||
|
fullname = PR_smprintf("%s/%s", pathname,
|
||
|
dbck.options[opt_InputDB].arg);
|
||
|
if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
|
||
|
fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
|
||
|
return -1;
|
||
|
}
|
||
|
rv = CERT_OpenCertDBFilename(certHandle, fullname, PR_TRUE);
|
||
|
} else
|
||
|
#endif
|
||
|
{
|
||
|
/* Use the default. */
|
||
|
#ifdef NOTYET
|
||
|
fullname = SECU_CertDBNameCallback(NULL, CERT_DB_FILE_VERSION);
|
||
|
if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
|
||
|
fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
|
||
|
return -1;
|
||
|
}
|
||
|
#endif
|
||
|
rv = nsslowcert_OpenCertDB(certHandle,
|
||
|
PR_TRUE, /* readOnly */
|
||
|
NULL, /* rdb appName */
|
||
|
"", /* rdb prefix */
|
||
|
dbck_certdb_name_cb, /* namecb */
|
||
|
pathname, /* configDir */
|
||
|
PR_FALSE); /* volatile */
|
||
|
}
|
||
|
|
||
|
if (rv) {
|
||
|
SECU_PrintError(progName, "unable to open cert database");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (dbck.commands[cmd_Debug].activated) {
|
||
|
DBCK_DebugDB(certHandle, dumpfile, mailfile);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef DORECOVER
|
||
|
if (dbck.commands[cmd_Recover].activated) {
|
||
|
DBCK_ReconstructDBFromCerts(certHandle, newdbname,
|
||
|
dumpfile, removeExpired,
|
||
|
requireProfile, singleEntry,
|
||
|
dbck.options[opt_Prompt].activated);
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (mailfile)
|
||
|
PR_Close(mailfile);
|
||
|
if (dumpfile)
|
||
|
PR_Close(dumpfile);
|
||
|
if (certHandle) {
|
||
|
nsslowcert_ClosePermCertDB(certHandle);
|
||
|
PORT_Free(certHandle);
|
||
|
}
|
||
|
return -1;
|
||
|
}
|