2000-03-31 20:13:40 +00:00
|
|
|
/*
|
|
|
|
* 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 Netscape are
|
|
|
|
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
|
|
|
|
* Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the
|
|
|
|
* terms of the GNU General Public License Version 2 or later (the
|
|
|
|
* "GPL"), in which case the provisions of the GPL are applicable
|
|
|
|
* instead of those above. If you wish to allow use of your
|
|
|
|
* version of this file only under the terms of the GPL and not to
|
|
|
|
* allow others to use your version of this file under the MPL,
|
|
|
|
* indicate your decision by deleting the provisions above and
|
|
|
|
* replace them with the notice and other provisions required by
|
|
|
|
* the GPL. If you do not delete the provisions above, a recipient
|
|
|
|
* may use your version of this file under either the MPL or the
|
|
|
|
* GPL.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Permanent Certificate database handling code
|
|
|
|
*
|
2001-09-20 21:34:42 +00:00
|
|
|
* $Id: pcertdb.c,v 1.18 2001/09/20 21:34:39 relyea%netscape.com Exp $
|
2000-03-31 20:13:40 +00:00
|
|
|
*/
|
|
|
|
#include "prtime.h"
|
|
|
|
|
|
|
|
#include "cert.h"
|
|
|
|
#include "mcom_db.h"
|
|
|
|
#include "certdb.h"
|
|
|
|
#include "secitem.h"
|
|
|
|
#include "secder.h"
|
|
|
|
|
|
|
|
/* Call to PK11_FreeSlot below */
|
|
|
|
|
|
|
|
#include "secasn1.h"
|
|
|
|
#include "secerr.h"
|
2001-01-03 19:51:22 +00:00
|
|
|
#include "nssilock.h"
|
2000-03-31 20:13:40 +00:00
|
|
|
#include "prmon.h"
|
|
|
|
#include "nsslocks.h"
|
|
|
|
#include "base64.h"
|
|
|
|
#include "sechash.h"
|
|
|
|
#include "plhash.h"
|
2001-09-20 21:34:42 +00:00
|
|
|
#include "pk11func.h" /* sigh */
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
#include "cdbhdl.h"
|
|
|
|
|
2000-12-08 03:35:29 +00:00
|
|
|
/* forward declaration */
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByDERCertNoLocking(CERTCertDBHandle *handle, SECItem *derCert);
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
/*
|
|
|
|
* the following functions are wrappers for the db library that implement
|
|
|
|
* a global lock to make the database thread safe.
|
|
|
|
*/
|
2001-01-03 19:51:22 +00:00
|
|
|
static PZLock *dbLock = NULL;
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
certdb_InitDBLock(void)
|
|
|
|
{
|
|
|
|
if (dbLock == NULL) {
|
2001-01-03 19:51:22 +00:00
|
|
|
nss_InitLock(&dbLock, nssILockCertDB);
|
2000-03-31 20:13:40 +00:00
|
|
|
PORT_Assert(dbLock != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
certdb_Get(DB *db, DBT *key, DBT *data, unsigned int flags)
|
|
|
|
{
|
|
|
|
PRStatus prstat;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
PORT_Assert(dbLock != NULL);
|
2001-01-03 19:51:22 +00:00
|
|
|
PZ_Lock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
ret = (* db->get)(db, key, data, flags);
|
|
|
|
|
2001-01-03 19:51:22 +00:00
|
|
|
prstat = PZ_Unlock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
certdb_Put(DB *db, DBT *key, DBT *data, unsigned int flags)
|
|
|
|
{
|
|
|
|
PRStatus prstat;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
PORT_Assert(dbLock != NULL);
|
2001-01-03 19:51:22 +00:00
|
|
|
PZ_Lock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
ret = (* db->put)(db, key, data, flags);
|
|
|
|
|
2001-01-03 19:51:22 +00:00
|
|
|
prstat = PZ_Unlock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
certdb_Sync(DB *db, unsigned int flags)
|
|
|
|
{
|
|
|
|
PRStatus prstat;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
PORT_Assert(dbLock != NULL);
|
2001-01-03 19:51:22 +00:00
|
|
|
PZ_Lock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
ret = (* db->sync)(db, flags);
|
|
|
|
|
2001-01-03 19:51:22 +00:00
|
|
|
prstat = PZ_Unlock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
certdb_Del(DB *db, DBT *key, unsigned int flags)
|
|
|
|
{
|
|
|
|
PRStatus prstat;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
PORT_Assert(dbLock != NULL);
|
2001-01-03 19:51:22 +00:00
|
|
|
PZ_Lock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
ret = (* db->del)(db, key, flags);
|
|
|
|
|
2001-01-03 19:51:22 +00:00
|
|
|
prstat = PZ_Unlock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
certdb_Seq(DB *db, DBT *key, DBT *data, unsigned int flags)
|
|
|
|
{
|
|
|
|
PRStatus prstat;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
PORT_Assert(dbLock != NULL);
|
2001-01-03 19:51:22 +00:00
|
|
|
PZ_Lock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
ret = (* db->seq)(db, key, data, flags);
|
|
|
|
|
2001-01-03 19:51:22 +00:00
|
|
|
prstat = PZ_Unlock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
certdb_Close(DB *db)
|
|
|
|
{
|
|
|
|
PRStatus prstat;
|
|
|
|
|
|
|
|
PORT_Assert(dbLock != NULL);
|
2001-01-03 19:51:22 +00:00
|
|
|
PZ_Lock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
(* db->close)(db);
|
|
|
|
|
2001-01-03 19:51:22 +00:00
|
|
|
prstat = PZ_Unlock(dbLock);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* forward references */
|
|
|
|
static void CERT_DestroyCertificateNoLocking(CERTCertificate *cert);
|
|
|
|
static SECStatus AddCertToSPKDigestTable(CERTCertDBHandle *handle,
|
|
|
|
CERTCertificate *cert);
|
|
|
|
static SECStatus RemoveCertFromSPKDigestTable(CERTCertDBHandle *handle,
|
|
|
|
CERTCertificate *cert);
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
DeleteDBEntry(CERTCertDBHandle *handle, certDBEntryType type, SECItem *dbkey)
|
|
|
|
{
|
|
|
|
DBT key;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* init the database key */
|
|
|
|
key.data = dbkey->data;
|
|
|
|
key.size = dbkey->len;
|
|
|
|
|
|
|
|
dbkey->data[0] = (unsigned char)type;
|
|
|
|
|
|
|
|
/* delete entry from database */
|
|
|
|
ret = certdb_Del(handle->permCertDB, &key, 0 );
|
|
|
|
if ( ret != 0 ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = certdb_Sync(handle->permCertDB, 0);
|
|
|
|
if ( ret ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
ReadDBEntry(CERTCertDBHandle *handle, certDBEntryCommon *entry,
|
|
|
|
SECItem *dbkey, SECItem *dbentry, PRArenaPool *arena)
|
|
|
|
{
|
|
|
|
DBT data, key;
|
|
|
|
int ret;
|
|
|
|
unsigned char *buf;
|
|
|
|
|
|
|
|
/* init the database key */
|
|
|
|
key.data = dbkey->data;
|
|
|
|
key.size = dbkey->len;
|
|
|
|
|
|
|
|
dbkey->data[0] = (unsigned char)entry->type;
|
|
|
|
|
|
|
|
/* read entry from database */
|
|
|
|
ret = certdb_Get(handle->permCertDB, &key, &data, 0 );
|
|
|
|
if ( ret != 0 ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* validate the entry */
|
|
|
|
if ( data.size < SEC_DB_ENTRY_HEADER_LEN ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
buf = (unsigned char *)data.data;
|
|
|
|
if ( buf[0] != (unsigned char)CERT_DB_FILE_VERSION ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
if ( buf[1] != (unsigned char)entry->type ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy out header information */
|
|
|
|
entry->version = (unsigned int)buf[0];
|
|
|
|
entry->type = (certDBEntryType)buf[1];
|
|
|
|
entry->flags = (unsigned int)buf[2];
|
|
|
|
|
|
|
|
/* format body of entry for return to caller */
|
|
|
|
dbentry->len = data.size - SEC_DB_ENTRY_HEADER_LEN;
|
|
|
|
if ( dbentry->len ) {
|
|
|
|
dbentry->data = (unsigned char *)PORT_ArenaAlloc(arena, dbentry->len);
|
|
|
|
if ( dbentry->data == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Memcpy(dbentry->data, &buf[SEC_DB_ENTRY_HEADER_LEN],
|
|
|
|
dbentry->len);
|
|
|
|
} else {
|
|
|
|
dbentry->data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
** Implement low level database access
|
|
|
|
**/
|
|
|
|
static SECStatus
|
|
|
|
WriteDBEntry(CERTCertDBHandle *handle, certDBEntryCommon *entry,
|
|
|
|
SECItem *dbkey, SECItem *dbentry)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
DBT data, key;
|
|
|
|
unsigned char *buf;
|
|
|
|
|
|
|
|
data.data = dbentry->data;
|
|
|
|
data.size = dbentry->len;
|
|
|
|
|
|
|
|
buf = (unsigned char*)data.data;
|
|
|
|
|
|
|
|
buf[0] = (unsigned char)entry->version;
|
|
|
|
buf[1] = (unsigned char)entry->type;
|
|
|
|
buf[2] = (unsigned char)entry->flags;
|
|
|
|
|
|
|
|
key.data = dbkey->data;
|
|
|
|
key.size = dbkey->len;
|
|
|
|
|
|
|
|
dbkey->data[0] = (unsigned char)entry->type;
|
|
|
|
|
|
|
|
/* put the record into the database now */
|
|
|
|
ret = certdb_Put(handle->permCertDB, &key, &data, 0);
|
|
|
|
|
|
|
|
if ( ret != 0 ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = certdb_Sync( handle->permCertDB, 0 );
|
|
|
|
|
|
|
|
if ( ret ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* encode a database cert record
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
EncodeDBCertEntry(certDBEntryCert *entry, PRArenaPool *arena, SECItem *dbitem)
|
|
|
|
{
|
|
|
|
unsigned int nnlen;
|
|
|
|
unsigned char *buf;
|
|
|
|
char *nn;
|
|
|
|
char zbuf = 0;
|
|
|
|
|
|
|
|
if ( entry->nickname ) {
|
|
|
|
nn = entry->nickname;
|
|
|
|
} else {
|
|
|
|
nn = &zbuf;
|
|
|
|
}
|
|
|
|
nnlen = PORT_Strlen(nn) + 1;
|
|
|
|
|
|
|
|
/* allocate space for encoded database record, including space
|
|
|
|
* for low level header
|
|
|
|
*/
|
|
|
|
dbitem->len = entry->derCert.len + nnlen + DB_CERT_ENTRY_HEADER_LEN +
|
|
|
|
SEC_DB_ENTRY_HEADER_LEN;
|
|
|
|
|
|
|
|
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
|
|
|
|
if ( dbitem->data == NULL) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill in database record */
|
|
|
|
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
|
|
|
|
|
|
|
|
buf[0] = ( entry->trust.sslFlags >> 8 ) & 0xff;
|
|
|
|
buf[1] = entry->trust.sslFlags & 0xff;
|
|
|
|
buf[2] = ( entry->trust.emailFlags >> 8 ) & 0xff;
|
|
|
|
buf[3] = entry->trust.emailFlags & 0xff;
|
|
|
|
buf[4] = ( entry->trust.objectSigningFlags >> 8 ) & 0xff;
|
|
|
|
buf[5] = entry->trust.objectSigningFlags & 0xff;
|
|
|
|
buf[6] = ( entry->derCert.len >> 8 ) & 0xff;
|
|
|
|
buf[7] = entry->derCert.len & 0xff;
|
|
|
|
buf[8] = ( nnlen >> 8 ) & 0xff;
|
|
|
|
buf[9] = nnlen & 0xff;
|
|
|
|
|
|
|
|
PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN], entry->derCert.data,
|
|
|
|
entry->derCert.len);
|
|
|
|
|
|
|
|
PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN + entry->derCert.len],
|
|
|
|
nn, nnlen);
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* encode a database key for a cert record
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
EncodeDBCertKey(SECItem *certKey, PRArenaPool *arena, SECItem *dbkey)
|
|
|
|
{
|
|
|
|
dbkey->len = certKey->len + SEC_DB_KEY_HEADER_LEN;
|
|
|
|
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
|
|
|
|
if ( dbkey->data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
|
|
|
|
certKey->data, certKey->len);
|
|
|
|
dbkey->data[0] = certDBEntryTypeCert;
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
EncodeDBGenericKey(SECItem *certKey, PRArenaPool *arena, SECItem *dbkey,
|
|
|
|
certDBEntryType entryType)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* we only allow _one_ KRL key!
|
|
|
|
*/
|
|
|
|
if (entryType == certDBEntryTypeKeyRevocation) {
|
|
|
|
dbkey->len = SEC_DB_KEY_HEADER_LEN;
|
|
|
|
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
|
|
|
|
if ( dbkey->data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
dbkey->data[0] = (unsigned char) entryType;
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbkey->len = certKey->len + SEC_DB_KEY_HEADER_LEN;
|
|
|
|
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
|
|
|
|
if ( dbkey->data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
|
|
|
|
certKey->data, certKey->len);
|
|
|
|
dbkey->data[0] = (unsigned char) entryType;
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
DecodeDBCertEntry(certDBEntryCert *entry, SECItem *dbentry)
|
|
|
|
{
|
|
|
|
unsigned int nnlen;
|
|
|
|
int headerlen;
|
|
|
|
int lenoff;
|
|
|
|
|
|
|
|
/* allow updates of old versions of the database */
|
|
|
|
switch ( entry->common.version ) {
|
|
|
|
case 5:
|
|
|
|
headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
|
|
|
|
lenoff = 3;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
/* should not get here */
|
|
|
|
PORT_Assert(0);
|
|
|
|
headerlen = DB_CERT_V6_ENTRY_HEADER_LEN;
|
|
|
|
lenoff = 3;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
headerlen = DB_CERT_ENTRY_HEADER_LEN;
|
|
|
|
lenoff = 6;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* better not get here */
|
|
|
|
PORT_Assert(0);
|
|
|
|
headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
|
|
|
|
lenoff = 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is record long enough for header? */
|
|
|
|
if ( dbentry->len < headerlen ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is database entry correct length? */
|
|
|
|
entry->derCert.len = ( ( dbentry->data[lenoff] << 8 ) |
|
|
|
|
dbentry->data[lenoff+1] );
|
|
|
|
nnlen = ( ( dbentry->data[lenoff+2] << 8 ) | dbentry->data[lenoff+3] );
|
|
|
|
if ( ( entry->derCert.len + nnlen + headerlen )
|
|
|
|
!= dbentry->len) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy the dercert */
|
|
|
|
entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
|
|
|
|
entry->derCert.len);
|
|
|
|
if ( entry->derCert.data == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->derCert.data, &dbentry->data[headerlen],
|
|
|
|
entry->derCert.len);
|
|
|
|
|
|
|
|
/* copy the nickname */
|
|
|
|
if ( nnlen > 1 ) {
|
|
|
|
entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena, nnlen);
|
|
|
|
if ( entry->nickname == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->nickname,
|
|
|
|
&dbentry->data[headerlen +
|
|
|
|
entry->derCert.len],
|
|
|
|
nnlen);
|
|
|
|
} else {
|
|
|
|
entry->nickname = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( entry->common.version < 7 ) {
|
|
|
|
/* allow updates of v5 db */
|
|
|
|
entry->trust.sslFlags = dbentry->data[0];
|
|
|
|
entry->trust.emailFlags = dbentry->data[1];
|
|
|
|
entry->trust.objectSigningFlags = dbentry->data[2];
|
|
|
|
} else {
|
|
|
|
entry->trust.sslFlags = ( dbentry->data[0] << 8 ) | dbentry->data[1];
|
|
|
|
entry->trust.emailFlags = ( dbentry->data[2] << 8 ) | dbentry->data[3];
|
|
|
|
entry->trust.objectSigningFlags =
|
|
|
|
( dbentry->data[4] << 8 ) | dbentry->data[5];
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new certDBEntryCert from existing data
|
|
|
|
*/
|
|
|
|
static certDBEntryCert *
|
|
|
|
NewDBCertEntry(SECItem *derCert, char *nickname,
|
|
|
|
CERTCertTrust *trust, int flags)
|
|
|
|
{
|
|
|
|
certDBEntryCert *entry;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
int nnlen;
|
|
|
|
|
|
|
|
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
|
|
|
|
|
|
|
|
if ( !arena ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntryCert *)PORT_ArenaZAlloc(arena, sizeof(certDBEntryCert));
|
|
|
|
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill in the dbCert */
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeCert;
|
|
|
|
entry->common.version = CERT_DB_FILE_VERSION;
|
|
|
|
entry->common.flags = flags;
|
|
|
|
|
|
|
|
if ( trust ) {
|
|
|
|
entry->trust = *trust;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, derCert->len);
|
|
|
|
if ( !entry->derCert.data ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry->derCert.len = derCert->len;
|
|
|
|
PORT_Memcpy(entry->derCert.data, derCert->data, derCert->len);
|
|
|
|
|
|
|
|
nnlen = ( nickname ? strlen(nickname) + 1 : 0 );
|
|
|
|
|
|
|
|
if ( nnlen ) {
|
|
|
|
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
|
|
|
|
if ( !entry->nickname ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->nickname, nickname, nnlen);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
entry->nickname = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(entry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
|
|
|
|
/* allocation error, free arena and return */
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode a version 4 DBCert from the byte stream database format
|
|
|
|
* and construct a current database entry struct
|
|
|
|
*/
|
|
|
|
static certDBEntryCert *
|
|
|
|
DecodeV4DBCertEntry(unsigned char *buf, int len)
|
|
|
|
{
|
|
|
|
certDBEntryCert *entry;
|
|
|
|
int certlen;
|
|
|
|
int nnlen;
|
|
|
|
PRArenaPool *arena;
|
|
|
|
|
|
|
|
/* make sure length is at least long enough for the header */
|
|
|
|
if ( len < DBCERT_V4_HEADER_LEN ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get other lengths */
|
|
|
|
certlen = buf[3] << 8 | buf[4];
|
|
|
|
nnlen = buf[5] << 8 | buf[6];
|
|
|
|
|
|
|
|
/* make sure DB entry is the right size */
|
|
|
|
if ( ( certlen + nnlen + DBCERT_V4_HEADER_LEN ) != len ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate arena */
|
|
|
|
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
|
|
|
|
|
|
|
|
if ( !arena ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate structure and members */
|
|
|
|
entry = (certDBEntryCert *) PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
|
|
|
|
|
|
|
|
if ( !entry ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, certlen);
|
|
|
|
if ( !entry->derCert.data ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry->derCert.len = certlen;
|
|
|
|
|
|
|
|
if ( nnlen ) {
|
|
|
|
entry->nickname = (char *) PORT_ArenaAlloc(arena, nnlen);
|
|
|
|
if ( !entry->nickname ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
entry->nickname = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.version = CERT_DB_FILE_VERSION;
|
|
|
|
entry->common.type = certDBEntryTypeCert;
|
|
|
|
entry->common.flags = 0;
|
|
|
|
entry->trust.sslFlags = buf[0];
|
|
|
|
entry->trust.emailFlags = buf[1];
|
|
|
|
entry->trust.objectSigningFlags = buf[2];
|
|
|
|
|
|
|
|
PORT_Memcpy(entry->derCert.data, &buf[DBCERT_V4_HEADER_LEN], certlen);
|
|
|
|
PORT_Memcpy(entry->nickname, &buf[DBCERT_V4_HEADER_LEN + certlen], nnlen);
|
|
|
|
|
2001-02-10 01:44:34 +00:00
|
|
|
if (PORT_Strcmp(entry->nickname,"Server-Cert") == 0) {
|
|
|
|
entry->trust.sslFlags |= CERTDB_USER;
|
|
|
|
}
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
return(entry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a Certificate database entry into byte stream suitable for
|
|
|
|
* the database
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
WriteDBCertEntry(CERTCertDBHandle *handle, certDBEntryCert *entry)
|
|
|
|
{
|
|
|
|
SECItem dbitem, dbkey;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
SECItem tmpitem;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBCertEntry(entry, tmparena, &dbitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get the database key and format it */
|
|
|
|
rv = CERT_KeyFromDERCert(tmparena, &entry->derCert, &tmpitem);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBCertKey(&tmpitem, tmparena, &dbkey);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now write it to the database */
|
|
|
|
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* delete a certificate entry
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
DeleteDBCertEntry(CERTCertDBHandle *handle, SECItem *certKey)
|
|
|
|
{
|
|
|
|
SECItem dbkey;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBCertKey(certKey, arena, &dbkey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DeleteDBEntry(handle, certDBEntryTypeCert, &dbkey);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a certificate entry
|
|
|
|
*/
|
|
|
|
static certDBEntryCert *
|
|
|
|
ReadDBCertEntry(CERTCertDBHandle *handle, SECItem *certKey)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
certDBEntryCert *entry;
|
|
|
|
SECItem dbkey;
|
|
|
|
SECItem dbentry;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeCert;
|
|
|
|
|
|
|
|
rv = EncodeDBCertKey(certKey, tmparena, &dbkey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DecodeDBCertEntry(entry, &dbentry);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(entry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* encode a database cert record
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
EncodeDBCrlEntry(certDBEntryRevocation *entry, PRArenaPool *arena, SECItem *dbitem)
|
|
|
|
{
|
|
|
|
unsigned int nnlen = 0;
|
|
|
|
unsigned char *buf;
|
|
|
|
|
|
|
|
if (entry->url) {
|
|
|
|
nnlen = PORT_Strlen(entry->url) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate space for encoded database record, including space
|
|
|
|
* for low level header
|
|
|
|
*/
|
|
|
|
dbitem->len = entry->derCrl.len + nnlen
|
|
|
|
+ SEC_DB_ENTRY_HEADER_LEN + DB_CRL_ENTRY_HEADER_LEN;
|
|
|
|
|
|
|
|
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
|
|
|
|
if ( dbitem->data == NULL) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill in database record */
|
|
|
|
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
|
|
|
|
|
|
|
|
buf[0] = ( entry->derCrl.len >> 8 ) & 0xff;
|
|
|
|
buf[1] = entry->derCrl.len & 0xff;
|
|
|
|
buf[2] = ( nnlen >> 8 ) & 0xff;
|
|
|
|
buf[3] = nnlen & 0xff;
|
|
|
|
|
|
|
|
PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN], entry->derCrl.data,
|
|
|
|
entry->derCrl.len);
|
|
|
|
|
|
|
|
if (nnlen != 0) {
|
|
|
|
PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
|
|
|
|
entry->url, nnlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
DecodeDBCrlEntry(certDBEntryRevocation *entry, SECItem *dbentry)
|
|
|
|
{
|
|
|
|
unsigned int nnlen;
|
|
|
|
|
|
|
|
/* is record long enough for header? */
|
|
|
|
if ( dbentry->len < DB_CRL_ENTRY_HEADER_LEN ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is database entry correct length? */
|
|
|
|
entry->derCrl.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
|
|
|
|
nnlen = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
|
|
|
|
if ( ( entry->derCrl.len + nnlen + DB_CRL_ENTRY_HEADER_LEN )
|
|
|
|
!= dbentry->len) {
|
2001-06-20 23:14:35 +00:00
|
|
|
/* CRL entry is greater than 64 K. Hack to make this continue to work */
|
|
|
|
if (dbentry->len >= (0xffff - DB_CRL_ENTRY_HEADER_LEN) - nnlen) {
|
|
|
|
entry->derCrl.len =
|
|
|
|
(dbentry->len - DB_CRL_ENTRY_HEADER_LEN) - nnlen;
|
|
|
|
} else {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
2000-03-31 20:13:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* copy the dercert */
|
|
|
|
entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
|
|
|
|
entry->derCrl.len);
|
|
|
|
if ( entry->derCrl.data == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->derCrl.data, &dbentry->data[DB_CRL_ENTRY_HEADER_LEN],
|
|
|
|
entry->derCrl.len);
|
|
|
|
|
|
|
|
/* copy the url */
|
|
|
|
entry->url = NULL;
|
|
|
|
if (nnlen != 0) {
|
|
|
|
entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, nnlen);
|
|
|
|
if ( entry->url == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->url,
|
|
|
|
&dbentry->data[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
|
|
|
|
nnlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new certDBEntryRevocation from existing data
|
|
|
|
*/
|
|
|
|
static certDBEntryRevocation *
|
|
|
|
NewDBCrlEntry(SECItem *derCrl, char * url, certDBEntryType crlType, int flags)
|
|
|
|
{
|
|
|
|
certDBEntryRevocation *entry;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
int nnlen;
|
|
|
|
|
|
|
|
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
|
|
|
|
|
|
|
|
if ( !arena ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntryRevocation*)
|
|
|
|
PORT_ArenaZAlloc(arena, sizeof(certDBEntryRevocation));
|
|
|
|
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill in the dbRevolcation */
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = crlType;
|
|
|
|
entry->common.version = CERT_DB_FILE_VERSION;
|
|
|
|
entry->common.flags = flags;
|
|
|
|
|
|
|
|
|
|
|
|
entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(arena, derCrl->len);
|
|
|
|
if ( !entry->derCrl.data ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (url) {
|
|
|
|
nnlen = PORT_Strlen(url) + 1;
|
|
|
|
entry->url = (char *)PORT_ArenaAlloc(arena, nnlen);
|
|
|
|
if ( !entry->url ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->url, url, nnlen);
|
|
|
|
} else {
|
|
|
|
entry->url = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
entry->derCrl.len = derCrl->len;
|
|
|
|
PORT_Memcpy(entry->derCrl.data, derCrl->data, derCrl->len);
|
|
|
|
|
|
|
|
return(entry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
|
|
|
|
/* allocation error, free arena and return */
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
WriteDBCrlEntry(CERTCertDBHandle *handle, certDBEntryRevocation *entry )
|
|
|
|
{
|
|
|
|
SECItem dbkey;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
SECItem tmpitem,encodedEntry;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get the database key and format it */
|
|
|
|
rv = CERT_KeyFromDERCrl(tmparena, &entry->derCrl, &tmpitem);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBCrlEntry(entry, tmparena, &encodedEntry);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBGenericKey(&tmpitem, tmparena, &dbkey, entry->common.type);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now write it to the database */
|
|
|
|
rv = WriteDBEntry(handle, &entry->common, &dbkey, &encodedEntry);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* delete a crl entry
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
DeleteDBCrlEntry(CERTCertDBHandle *handle, SECItem *crlKey,
|
|
|
|
certDBEntryType crlType)
|
|
|
|
{
|
|
|
|
SECItem dbkey;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBGenericKey(crlKey, arena, &dbkey, crlType);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DeleteDBEntry(handle, crlType, &dbkey);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a certificate entry
|
|
|
|
*/
|
|
|
|
static certDBEntryRevocation *
|
|
|
|
ReadDBCrlEntry(CERTCertDBHandle *handle, SECItem *certKey,
|
|
|
|
certDBEntryType crlType)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
certDBEntryRevocation *entry;
|
|
|
|
SECItem dbkey;
|
|
|
|
SECItem dbentry;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntryRevocation *)
|
|
|
|
PORT_ArenaAlloc(arena, sizeof(certDBEntryRevocation));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = crlType;
|
|
|
|
|
|
|
|
rv = EncodeDBGenericKey(certKey, tmparena, &dbkey, crlType);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DecodeDBCrlEntry(entry, &dbentry);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(entry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* destroy a database entry
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
DestroyDBEntry(certDBEntry *entry)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = entry->common.arena;
|
|
|
|
|
|
|
|
/* Zero out the entry struct, so that any further attempts to use it
|
|
|
|
* will cause an exception (e.g. null pointer reference). */
|
|
|
|
PORT_Memset(&entry->common, 0, sizeof entry->common);
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a database nickname record
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
EncodeDBNicknameEntry(certDBEntryNickname *entry, PRArenaPool *arena,
|
|
|
|
SECItem *dbitem)
|
|
|
|
{
|
|
|
|
unsigned char *buf;
|
|
|
|
|
|
|
|
/* allocate space for encoded database record, including space
|
|
|
|
* for low level header
|
|
|
|
*/
|
|
|
|
dbitem->len = entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN +
|
|
|
|
SEC_DB_ENTRY_HEADER_LEN;
|
|
|
|
|
|
|
|
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
|
|
|
|
if ( dbitem->data == NULL) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill in database record */
|
|
|
|
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
|
|
|
|
|
|
|
|
buf[0] = ( entry->subjectName.len >> 8 ) & 0xff;
|
|
|
|
buf[1] = entry->subjectName.len & 0xff;
|
|
|
|
|
|
|
|
PORT_Memcpy(&buf[DB_NICKNAME_ENTRY_HEADER_LEN], entry->subjectName.data,
|
|
|
|
entry->subjectName.len);
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a database key for a nickname record
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
EncodeDBNicknameKey(char *nickname, PRArenaPool *arena,
|
|
|
|
SECItem *dbkey)
|
|
|
|
{
|
|
|
|
unsigned int nnlen;
|
|
|
|
|
|
|
|
nnlen = PORT_Strlen(nickname) + 1; /* includes null */
|
|
|
|
|
|
|
|
/* now get the database key and format it */
|
|
|
|
dbkey->len = nnlen + SEC_DB_KEY_HEADER_LEN;
|
|
|
|
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
|
|
|
|
if ( dbkey->data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], nickname, nnlen);
|
|
|
|
dbkey->data[0] = certDBEntryTypeNickname;
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
2001-01-08 19:24:23 +00:00
|
|
|
DecodeDBNicknameEntry(certDBEntryNickname *entry, SECItem *dbentry,
|
|
|
|
char *nickname)
|
2000-03-31 20:13:40 +00:00
|
|
|
{
|
|
|
|
/* is record long enough for header? */
|
|
|
|
if ( dbentry->len < DB_NICKNAME_ENTRY_HEADER_LEN ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is database entry correct length? */
|
|
|
|
entry->subjectName.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
|
|
|
|
if (( entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN ) !=
|
|
|
|
dbentry->len ){
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy the certkey */
|
|
|
|
entry->subjectName.data =
|
|
|
|
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
|
|
|
|
entry->subjectName.len);
|
|
|
|
if ( entry->subjectName.data == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->subjectName.data,
|
|
|
|
&dbentry->data[DB_NICKNAME_ENTRY_HEADER_LEN],
|
|
|
|
entry->subjectName.len);
|
|
|
|
|
2001-01-08 19:24:23 +00:00
|
|
|
entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena,
|
|
|
|
PORT_Strlen(nickname)+1);
|
|
|
|
if ( entry->nickname ) {
|
|
|
|
PORT_Strcpy(entry->nickname, nickname);
|
|
|
|
}
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create a new nickname entry
|
|
|
|
*/
|
|
|
|
static certDBEntryNickname *
|
|
|
|
NewDBNicknameEntry(char *nickname, SECItem *subjectName, unsigned int flags)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
certDBEntryNickname *entry;
|
|
|
|
int nnlen;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
|
|
|
|
sizeof(certDBEntryNickname));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* init common fields */
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeNickname;
|
|
|
|
entry->common.version = CERT_DB_FILE_VERSION;
|
|
|
|
entry->common.flags = flags;
|
|
|
|
|
|
|
|
/* copy the nickname */
|
|
|
|
nnlen = PORT_Strlen(nickname) + 1;
|
|
|
|
|
|
|
|
entry->nickname = (char*)PORT_ArenaAlloc(arena, nnlen);
|
|
|
|
if ( entry->nickname == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Memcpy(entry->nickname, nickname, nnlen);
|
|
|
|
|
|
|
|
rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(entry);
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* delete a nickname entry
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
DeleteDBNicknameEntry(CERTCertDBHandle *handle, char *nickname)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
SECItem dbkey;
|
|
|
|
|
|
|
|
if ( nickname == NULL ) {
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBNicknameKey(nickname, arena, &dbkey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DeleteDBEntry(handle, certDBEntryTypeNickname, &dbkey);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a nickname entry
|
|
|
|
*/
|
|
|
|
static certDBEntryNickname *
|
|
|
|
ReadDBNicknameEntry(CERTCertDBHandle *handle, char *nickname)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
certDBEntryNickname *entry;
|
|
|
|
SECItem dbkey;
|
|
|
|
SECItem dbentry;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
|
|
|
|
sizeof(certDBEntryNickname));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeNickname;
|
|
|
|
|
|
|
|
rv = EncodeDBNicknameKey(nickname, tmparena, &dbkey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is record long enough for header? */
|
|
|
|
if ( dbentry.len < DB_NICKNAME_ENTRY_HEADER_LEN ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
2001-01-08 19:24:23 +00:00
|
|
|
rv = DecodeDBNicknameEntry(entry, &dbentry, nickname);
|
2000-03-31 20:13:40 +00:00
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(entry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a nickname entry into byte stream suitable for
|
|
|
|
* the database
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
WriteDBNicknameEntry(CERTCertDBHandle *handle, certDBEntryNickname *entry)
|
|
|
|
{
|
|
|
|
SECItem dbitem, dbkey;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBNicknameEntry(entry, tmparena, &dbitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBNicknameKey(entry->nickname, tmparena, &dbkey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now write it to the database */
|
|
|
|
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
return(SECFailure);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a database smime record
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
EncodeDBSMimeEntry(certDBEntrySMime *entry, PRArenaPool *arena,
|
|
|
|
SECItem *dbitem)
|
|
|
|
{
|
|
|
|
unsigned char *buf;
|
|
|
|
|
|
|
|
/* allocate space for encoded database record, including space
|
|
|
|
* for low level header
|
|
|
|
*/
|
|
|
|
dbitem->len = entry->subjectName.len + entry->smimeOptions.len +
|
|
|
|
entry->optionsDate.len +
|
|
|
|
DB_SMIME_ENTRY_HEADER_LEN + SEC_DB_ENTRY_HEADER_LEN;
|
|
|
|
|
|
|
|
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
|
|
|
|
if ( dbitem->data == NULL) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill in database record */
|
|
|
|
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
|
|
|
|
|
|
|
|
buf[0] = ( entry->subjectName.len >> 8 ) & 0xff;
|
|
|
|
buf[1] = entry->subjectName.len & 0xff;
|
|
|
|
buf[2] = ( entry->smimeOptions.len >> 8 ) & 0xff;
|
|
|
|
buf[3] = entry->smimeOptions.len & 0xff;
|
|
|
|
buf[4] = ( entry->optionsDate.len >> 8 ) & 0xff;
|
|
|
|
buf[5] = entry->optionsDate.len & 0xff;
|
|
|
|
|
|
|
|
/* if no smime options, then there should not be an options date either */
|
|
|
|
PORT_Assert( ! ( ( entry->smimeOptions.len == 0 ) &&
|
|
|
|
( entry->optionsDate.len != 0 ) ) );
|
|
|
|
|
|
|
|
PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN], entry->subjectName.data,
|
|
|
|
entry->subjectName.len);
|
|
|
|
if ( entry->smimeOptions.len ) {
|
|
|
|
PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN+entry->subjectName.len],
|
|
|
|
entry->smimeOptions.data,
|
|
|
|
entry->smimeOptions.len);
|
|
|
|
PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len +
|
|
|
|
entry->smimeOptions.len],
|
|
|
|
entry->optionsDate.data,
|
|
|
|
entry->optionsDate.len);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a database key for a SMIME record
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
EncodeDBSMimeKey(char *emailAddr, PRArenaPool *arena,
|
|
|
|
SECItem *dbkey)
|
|
|
|
{
|
|
|
|
unsigned int addrlen;
|
|
|
|
|
|
|
|
addrlen = PORT_Strlen(emailAddr) + 1; /* includes null */
|
|
|
|
|
|
|
|
/* now get the database key and format it */
|
|
|
|
dbkey->len = addrlen + SEC_DB_KEY_HEADER_LEN;
|
|
|
|
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
|
|
|
|
if ( dbkey->data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], emailAddr, addrlen);
|
|
|
|
dbkey->data[0] = certDBEntryTypeSMimeProfile;
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode a database SMIME record
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
|
|
|
|
{
|
|
|
|
/* is record long enough for header? */
|
|
|
|
if ( dbentry->len < DB_SMIME_ENTRY_HEADER_LEN ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is database entry correct length? */
|
|
|
|
entry->subjectName.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
|
|
|
|
entry->smimeOptions.len = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
|
|
|
|
entry->optionsDate.len = ( ( dbentry->data[4] << 8 ) | dbentry->data[5] );
|
|
|
|
if (( entry->subjectName.len + entry->smimeOptions.len +
|
|
|
|
entry->optionsDate.len + DB_SMIME_ENTRY_HEADER_LEN ) != dbentry->len){
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy the subject name */
|
|
|
|
entry->subjectName.data =
|
|
|
|
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
|
|
|
|
entry->subjectName.len);
|
|
|
|
if ( entry->subjectName.data == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->subjectName.data,
|
|
|
|
&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN],
|
|
|
|
entry->subjectName.len);
|
|
|
|
|
|
|
|
/* copy the smime options */
|
|
|
|
if ( entry->smimeOptions.len ) {
|
|
|
|
entry->smimeOptions.data =
|
|
|
|
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
|
|
|
|
entry->smimeOptions.len);
|
|
|
|
if ( entry->smimeOptions.data == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->smimeOptions.data,
|
|
|
|
&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
|
|
|
|
entry->subjectName.len],
|
|
|
|
entry->smimeOptions.len);
|
|
|
|
}
|
|
|
|
if ( entry->optionsDate.len ) {
|
|
|
|
entry->optionsDate.data =
|
|
|
|
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
|
|
|
|
entry->optionsDate.len);
|
|
|
|
if ( entry->optionsDate.data == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->optionsDate.data,
|
|
|
|
&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
|
|
|
|
entry->subjectName.len +
|
|
|
|
entry->smimeOptions.len],
|
|
|
|
entry->optionsDate.len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* both options and options date must either exist or not exist */
|
|
|
|
if ( ( ( entry->optionsDate.len == 0 ) ||
|
|
|
|
( entry->smimeOptions.len == 0 ) ) &&
|
|
|
|
entry->smimeOptions.len != entry->optionsDate.len ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->emailAddr = (char *)PORT_Alloc(PORT_Strlen(emailAddr)+1);
|
|
|
|
if ( entry->emailAddr ) {
|
|
|
|
PORT_Strcpy(entry->emailAddr, emailAddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create a new SMIME entry
|
|
|
|
*/
|
|
|
|
static certDBEntrySMime *
|
|
|
|
NewDBSMimeEntry(char *emailAddr, SECItem *subjectName, SECItem *smimeOptions,
|
|
|
|
SECItem *optionsDate, unsigned int flags)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
certDBEntrySMime *entry;
|
|
|
|
int addrlen;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
|
|
|
|
sizeof(certDBEntrySMime));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* init common fields */
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeSMimeProfile;
|
|
|
|
entry->common.version = CERT_DB_FILE_VERSION;
|
|
|
|
entry->common.flags = flags;
|
|
|
|
|
|
|
|
/* copy the email addr */
|
|
|
|
addrlen = PORT_Strlen(emailAddr) + 1;
|
|
|
|
|
|
|
|
entry->emailAddr = (char*)PORT_ArenaAlloc(arena, addrlen);
|
|
|
|
if ( entry->emailAddr == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Memcpy(entry->emailAddr, emailAddr, addrlen);
|
|
|
|
|
|
|
|
/* copy the subject name */
|
|
|
|
rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy the smime options */
|
|
|
|
if ( smimeOptions ) {
|
|
|
|
rv = SECITEM_CopyItem(arena, &entry->smimeOptions, smimeOptions);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PORT_Assert(optionsDate == NULL);
|
|
|
|
entry->smimeOptions.data = NULL;
|
|
|
|
entry->smimeOptions.len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy the options date */
|
|
|
|
if ( optionsDate ) {
|
|
|
|
rv = SECITEM_CopyItem(arena, &entry->optionsDate, optionsDate);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PORT_Assert(smimeOptions == NULL);
|
|
|
|
entry->optionsDate.data = NULL;
|
|
|
|
entry->optionsDate.len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(entry);
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* delete a SMIME entry
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
DeleteDBSMimeEntry(CERTCertDBHandle *handle, char *emailAddr)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
SECItem dbkey;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBSMimeKey(emailAddr, arena, &dbkey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DeleteDBEntry(handle, certDBEntryTypeSMimeProfile, &dbkey);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a SMIME entry
|
|
|
|
*/
|
|
|
|
static certDBEntrySMime *
|
|
|
|
ReadDBSMimeEntry(CERTCertDBHandle *handle, char *emailAddr)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
certDBEntrySMime *entry;
|
|
|
|
SECItem dbkey;
|
|
|
|
SECItem dbentry;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
|
|
|
|
sizeof(certDBEntrySMime));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeSMimeProfile;
|
|
|
|
|
|
|
|
rv = EncodeDBSMimeKey(emailAddr, tmparena, &dbkey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is record long enough for header? */
|
|
|
|
if ( dbentry.len < DB_SMIME_ENTRY_HEADER_LEN ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DecodeDBSMimeEntry(entry, &dbentry, emailAddr);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(entry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a SMIME entry into byte stream suitable for
|
|
|
|
* the database
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
WriteDBSMimeEntry(CERTCertDBHandle *handle, certDBEntrySMime *entry)
|
|
|
|
{
|
|
|
|
SECItem dbitem, dbkey;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBSMimeEntry(entry, tmparena, &dbitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBSMimeKey(entry->emailAddr, tmparena, &dbkey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now write it to the database */
|
|
|
|
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
return(SECFailure);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a database subject record
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
EncodeDBSubjectEntry(certDBEntrySubject *entry, PRArenaPool *arena,
|
|
|
|
SECItem *dbitem)
|
|
|
|
{
|
|
|
|
unsigned char *buf;
|
|
|
|
int len;
|
|
|
|
unsigned int ncerts;
|
|
|
|
unsigned int i;
|
|
|
|
unsigned char *tmpbuf;
|
|
|
|
unsigned int nnlen = 0;
|
|
|
|
unsigned int eaddrlen = 0;
|
|
|
|
int keyidoff;
|
|
|
|
SECItem *certKeys;
|
|
|
|
SECItem *keyIDs;
|
|
|
|
|
|
|
|
if ( entry->nickname ) {
|
|
|
|
nnlen = PORT_Strlen(entry->nickname) + 1;
|
|
|
|
}
|
|
|
|
if ( entry->emailAddr ) {
|
|
|
|
eaddrlen = PORT_Strlen(entry->emailAddr) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ncerts = entry->ncerts;
|
|
|
|
|
|
|
|
/* compute the length of the entry */
|
|
|
|
keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
|
|
|
|
len = keyidoff + 4 * ncerts;
|
|
|
|
for ( i = 0; i < ncerts; i++ ) {
|
|
|
|
len += entry->certKeys[i].len;
|
|
|
|
len += entry->keyIDs[i].len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate space for encoded database record, including space
|
|
|
|
* for low level header
|
|
|
|
*/
|
|
|
|
dbitem->len = len + SEC_DB_ENTRY_HEADER_LEN;
|
|
|
|
|
|
|
|
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
|
|
|
|
if ( dbitem->data == NULL) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill in database record */
|
|
|
|
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
|
|
|
|
|
|
|
|
buf[0] = ( ncerts >> 8 ) & 0xff;
|
|
|
|
buf[1] = ncerts & 0xff;
|
|
|
|
buf[2] = ( nnlen >> 8 ) & 0xff;
|
|
|
|
buf[3] = nnlen & 0xff;
|
|
|
|
buf[4] = ( eaddrlen >> 8 ) & 0xff;
|
|
|
|
buf[5] = eaddrlen & 0xff;
|
|
|
|
|
|
|
|
PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen);
|
|
|
|
PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN+nnlen], entry->emailAddr,
|
|
|
|
eaddrlen);
|
|
|
|
|
|
|
|
for ( i = 0; i < ncerts; i++ ) {
|
|
|
|
|
|
|
|
certKeys = entry->certKeys;
|
|
|
|
keyIDs = entry->keyIDs;
|
|
|
|
|
|
|
|
buf[keyidoff+i*2] = ( certKeys[i].len >> 8 ) & 0xff;
|
|
|
|
buf[keyidoff+1+i*2] = certKeys[i].len & 0xff;
|
|
|
|
buf[keyidoff+ncerts*2+i*2] = ( keyIDs[i].len >> 8 ) & 0xff;
|
|
|
|
buf[keyidoff+1+ncerts*2+i*2] = keyIDs[i].len & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* temp pointer used to stuff certkeys and keyids into the buffer */
|
|
|
|
tmpbuf = &buf[keyidoff+ncerts*4];
|
|
|
|
|
|
|
|
for ( i = 0; i < ncerts; i++ ) {
|
|
|
|
certKeys = entry->certKeys;
|
|
|
|
PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
|
|
|
|
tmpbuf = tmpbuf + certKeys[i].len;
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( i = 0; i < ncerts; i++ ) {
|
|
|
|
keyIDs = entry->keyIDs;
|
|
|
|
PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
|
|
|
|
tmpbuf = tmpbuf + keyIDs[i].len;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Assert(tmpbuf == &buf[len]);
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a database key for a subject record
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
EncodeDBSubjectKey(SECItem *derSubject, PRArenaPool *arena,
|
|
|
|
SECItem *dbkey)
|
|
|
|
{
|
|
|
|
dbkey->len = derSubject->len + SEC_DB_KEY_HEADER_LEN;
|
|
|
|
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
|
|
|
|
if ( dbkey->data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], derSubject->data,
|
|
|
|
derSubject->len);
|
|
|
|
dbkey->data[0] = certDBEntryTypeSubject;
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
DecodeDBSubjectEntry(certDBEntrySubject *entry, SECItem *dbentry,
|
|
|
|
SECItem *derSubject)
|
|
|
|
{
|
|
|
|
unsigned int ncerts;
|
|
|
|
PRArenaPool *arena;
|
|
|
|
unsigned int len, itemlen;
|
|
|
|
unsigned char *tmpbuf;
|
|
|
|
unsigned int i;
|
|
|
|
SECStatus rv;
|
|
|
|
unsigned int keyidoff;
|
|
|
|
unsigned int nnlen, eaddrlen;
|
|
|
|
|
|
|
|
arena = entry->common.arena;
|
|
|
|
|
|
|
|
rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is record long enough for header? */
|
|
|
|
if ( dbentry->len < DB_SUBJECT_ENTRY_HEADER_LEN ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->ncerts = ncerts = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
|
|
|
|
nnlen = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
|
|
|
|
eaddrlen = ( ( dbentry->data[4] << 8 ) | dbentry->data[5] );
|
|
|
|
if ( dbentry->len < ( ncerts * 4 + DB_SUBJECT_ENTRY_HEADER_LEN +
|
|
|
|
nnlen + eaddrlen) ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena,
|
|
|
|
sizeof(SECItem) * ncerts);
|
|
|
|
entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena,
|
|
|
|
sizeof(SECItem) * ncerts);
|
|
|
|
|
|
|
|
if ( ( entry->certKeys == NULL ) || ( entry->keyIDs == NULL ) ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( nnlen > 1 ) { /* null terminator is stored */
|
|
|
|
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
|
|
|
|
if ( entry->nickname == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->nickname,
|
|
|
|
&dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN],
|
|
|
|
nnlen);
|
|
|
|
} else {
|
|
|
|
entry->nickname = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( eaddrlen > 1 ) { /* null terminator is stored */
|
|
|
|
entry->emailAddr = (char *)PORT_ArenaAlloc(arena, eaddrlen);
|
|
|
|
if ( entry->emailAddr == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->emailAddr,
|
|
|
|
&dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN+nnlen],
|
|
|
|
eaddrlen);
|
|
|
|
} else {
|
|
|
|
entry->emailAddr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* collect the lengths of the certKeys and keyIDs, and total the
|
|
|
|
* overall length.
|
|
|
|
*/
|
|
|
|
keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
|
|
|
|
len = keyidoff + 4 * ncerts;
|
|
|
|
|
|
|
|
tmpbuf = &dbentry->data[0];
|
|
|
|
|
|
|
|
for ( i = 0; i < ncerts; i++ ) {
|
|
|
|
|
|
|
|
itemlen = ( tmpbuf[keyidoff + 2*i] << 8 ) | tmpbuf[keyidoff + 1 + 2*i] ;
|
|
|
|
len += itemlen;
|
|
|
|
entry->certKeys[i].len = itemlen;
|
|
|
|
|
|
|
|
itemlen = ( tmpbuf[keyidoff + 2*ncerts + 2*i] << 8 ) |
|
|
|
|
tmpbuf[keyidoff + 1 + 2*ncerts + 2*i] ;
|
|
|
|
len += itemlen;
|
|
|
|
entry->keyIDs[i].len = itemlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is database entry correct length? */
|
|
|
|
if ( len != dbentry->len ){
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmpbuf = &tmpbuf[keyidoff + 4*ncerts];
|
|
|
|
for ( i = 0; i < ncerts; i++ ) {
|
|
|
|
entry->certKeys[i].data =
|
|
|
|
(unsigned char *)PORT_ArenaAlloc(arena, entry->certKeys[i].len);
|
|
|
|
if ( entry->certKeys[i].data == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->certKeys[i].data, tmpbuf, entry->certKeys[i].len);
|
|
|
|
tmpbuf = &tmpbuf[entry->certKeys[i].len];
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( i = 0; i < ncerts; i++ ) {
|
|
|
|
entry->keyIDs[i].data =
|
|
|
|
(unsigned char *)PORT_ArenaAlloc(arena, entry->keyIDs[i].len);
|
|
|
|
if ( entry->keyIDs[i].data == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, entry->keyIDs[i].len);
|
|
|
|
tmpbuf = &tmpbuf[entry->keyIDs[i].len];
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Assert(tmpbuf == &dbentry->data[dbentry->len]);
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create a new subject entry with a single cert
|
|
|
|
*/
|
|
|
|
static certDBEntrySubject *
|
|
|
|
NewDBSubjectEntry(SECItem *derSubject, SECItem *certKey,
|
|
|
|
SECItem *keyID, char *nickname, char *emailAddr,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
certDBEntrySubject *entry;
|
|
|
|
SECStatus rv;
|
|
|
|
unsigned int nnlen;
|
|
|
|
unsigned int eaddrlen;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
|
|
|
|
sizeof(certDBEntrySubject));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* init common fields */
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeSubject;
|
|
|
|
entry->common.version = CERT_DB_FILE_VERSION;
|
|
|
|
entry->common.flags = flags;
|
|
|
|
|
|
|
|
/* copy the subject */
|
|
|
|
rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->ncerts = 1;
|
|
|
|
/* copy nickname */
|
|
|
|
if ( nickname && ( *nickname != '\0' ) ) {
|
|
|
|
nnlen = PORT_Strlen(nickname) + 1;
|
|
|
|
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
|
|
|
|
if ( entry->nickname == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Memcpy(entry->nickname, nickname, nnlen);
|
|
|
|
} else {
|
|
|
|
entry->nickname = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy email addr */
|
|
|
|
if ( emailAddr && ( *emailAddr != '\0' ) ) {
|
|
|
|
emailAddr = CERT_FixupEmailAddr(emailAddr);
|
|
|
|
if ( emailAddr == NULL ) {
|
|
|
|
entry->emailAddr = NULL;
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
eaddrlen = PORT_Strlen(emailAddr) + 1;
|
|
|
|
entry->emailAddr = (char *)PORT_ArenaAlloc(arena, eaddrlen);
|
|
|
|
if ( entry->emailAddr == NULL ) {
|
|
|
|
PORT_Free(emailAddr);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Memcpy(entry->emailAddr, emailAddr, eaddrlen);
|
|
|
|
PORT_Free(emailAddr);
|
|
|
|
} else {
|
|
|
|
entry->emailAddr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate space for certKeys and keyIDs */
|
|
|
|
entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
|
|
|
|
entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
|
|
|
|
if ( ( entry->certKeys == NULL ) || ( entry->keyIDs == NULL ) ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy the certKey and keyID */
|
|
|
|
rv = SECITEM_CopyItem(arena, &entry->certKeys[0], certKey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
rv = SECITEM_CopyItem(arena, &entry->keyIDs[0], keyID);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(entry);
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* delete a subject entry
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
DeleteDBSubjectEntry(CERTCertDBHandle *handle, SECItem *derSubject)
|
|
|
|
{
|
|
|
|
SECItem dbkey;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBSubjectKey(derSubject, arena, &dbkey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DeleteDBEntry(handle, certDBEntryTypeSubject, &dbkey);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the subject entry
|
|
|
|
*/
|
|
|
|
static certDBEntrySubject *
|
|
|
|
ReadDBSubjectEntry(CERTCertDBHandle *handle, SECItem *derSubject)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
certDBEntrySubject *entry;
|
|
|
|
SECItem dbkey;
|
|
|
|
SECItem dbentry;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
|
|
|
|
sizeof(certDBEntrySubject));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeSubject;
|
|
|
|
|
|
|
|
rv = EncodeDBSubjectKey(derSubject, tmparena, &dbkey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DecodeDBSubjectEntry(entry, &dbentry, derSubject);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(entry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a subject name entry into byte stream suitable for
|
|
|
|
* the database
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
WriteDBSubjectEntry(CERTCertDBHandle *handle, certDBEntrySubject *entry)
|
|
|
|
{
|
|
|
|
SECItem dbitem, dbkey;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBSubjectEntry(entry, tmparena, &dbitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBSubjectKey(&entry->derSubject, tmparena, &dbkey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now write it to the database */
|
|
|
|
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
return(SECFailure);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
UpdateSubjectWithEmailAddr(CERTCertificate *cert, char *emailAddr)
|
|
|
|
{
|
|
|
|
CERTSubjectList *subjectList;
|
|
|
|
PRBool save = PR_FALSE, delold = PR_FALSE;
|
|
|
|
certDBEntrySubject *entry;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
emailAddr = CERT_FixupEmailAddr(emailAddr);
|
|
|
|
if ( emailAddr == NULL ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
subjectList = cert->subjectList;
|
|
|
|
PORT_Assert(subjectList != NULL);
|
|
|
|
|
|
|
|
if ( subjectList->emailAddr ) {
|
|
|
|
if ( PORT_Strcmp(subjectList->emailAddr, emailAddr) != 0 ) {
|
|
|
|
save = PR_TRUE;
|
|
|
|
delold = PR_TRUE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
save = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( delold ) {
|
|
|
|
/* delete the old smime entry, because this cert now has a new
|
|
|
|
* smime entry pointing to it
|
|
|
|
*/
|
|
|
|
PORT_Assert(save);
|
|
|
|
PORT_Assert(subjectList->emailAddr != NULL);
|
|
|
|
DeleteDBSMimeEntry(cert->dbhandle, subjectList->emailAddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( save ) {
|
|
|
|
unsigned int len;
|
|
|
|
|
|
|
|
entry = subjectList->entry;
|
|
|
|
|
|
|
|
PORT_Assert(entry != NULL);
|
|
|
|
len = PORT_Strlen(emailAddr) + 1;
|
|
|
|
entry->emailAddr = (char *)PORT_ArenaAlloc(entry->common.arena, len);
|
|
|
|
if ( entry->emailAddr == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->emailAddr, emailAddr, len);
|
|
|
|
|
|
|
|
/* delete the subject entry */
|
|
|
|
DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
|
|
|
|
|
|
|
|
/* write the new one */
|
|
|
|
rv = WriteDBSubjectEntry(cert->dbhandle, entry);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Free(emailAddr);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
PORT_Free(emailAddr);
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* writes a nickname to an existing subject entry that does not currently
|
|
|
|
* have one
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
AddNicknameToSubject(CERTCertificate *cert, char *nickname)
|
|
|
|
{
|
|
|
|
CERTSubjectList *subjectList;
|
|
|
|
certDBEntrySubject *entry;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
if ( nickname == NULL ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
subjectList = cert->subjectList;
|
|
|
|
PORT_Assert(subjectList != NULL);
|
|
|
|
if ( subjectList == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = subjectList->entry;
|
|
|
|
PORT_Assert(entry != NULL);
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Assert(entry->nickname == NULL);
|
|
|
|
if ( entry->nickname != NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->nickname = (nickname) ? PORT_ArenaStrdup(entry->common.arena, nickname) : NULL;
|
|
|
|
|
|
|
|
if ( entry->nickname == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* delete the subject entry */
|
|
|
|
DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
|
|
|
|
|
|
|
|
/* write the new one */
|
|
|
|
rv = WriteDBSubjectEntry(cert->dbhandle, entry);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create a new version entry
|
|
|
|
*/
|
|
|
|
static certDBEntryVersion *
|
|
|
|
NewDBVersionEntry(unsigned int flags)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
certDBEntryVersion *entry;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntryVersion *)PORT_ArenaAlloc(arena,
|
|
|
|
sizeof(certDBEntryVersion));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeVersion;
|
|
|
|
entry->common.version = CERT_DB_FILE_VERSION;
|
|
|
|
entry->common.flags = flags;
|
|
|
|
|
|
|
|
return(entry);
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the version entry
|
|
|
|
*/
|
|
|
|
static certDBEntryVersion *
|
|
|
|
ReadDBVersionEntry(CERTCertDBHandle *handle)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
certDBEntryVersion *entry;
|
|
|
|
SECItem dbkey;
|
|
|
|
SECItem dbentry;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntryVersion *)PORT_ArenaAlloc(arena,
|
|
|
|
sizeof(certDBEntryVersion));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeVersion;
|
|
|
|
|
|
|
|
/* now get the database key and format it */
|
|
|
|
dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
|
|
|
|
dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
|
|
|
|
if ( dbkey.data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
|
|
|
|
SEC_DB_VERSION_KEY_LEN);
|
|
|
|
|
|
|
|
ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(entry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a version entry into byte stream suitable for
|
|
|
|
* the database
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
WriteDBVersionEntry(CERTCertDBHandle *handle, certDBEntryVersion *entry)
|
|
|
|
{
|
|
|
|
SECItem dbitem, dbkey;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate space for encoded database record, including space
|
|
|
|
* for low level header
|
|
|
|
*/
|
|
|
|
dbitem.len = SEC_DB_ENTRY_HEADER_LEN;
|
|
|
|
|
|
|
|
dbitem.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbitem.len);
|
|
|
|
if ( dbitem.data == NULL) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now get the database key and format it */
|
|
|
|
dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
|
|
|
|
dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
|
|
|
|
if ( dbkey.data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
|
|
|
|
SEC_DB_VERSION_KEY_LEN);
|
|
|
|
|
|
|
|
/* now write it to the database */
|
|
|
|
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create a new version entry
|
|
|
|
*/
|
|
|
|
static certDBEntryContentVersion *
|
|
|
|
NewDBContentVersionEntry(unsigned int flags)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
certDBEntryContentVersion *entry;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntryContentVersion *)
|
|
|
|
PORT_ArenaAlloc(arena, sizeof(certDBEntryContentVersion));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeContentVersion;
|
|
|
|
entry->common.version = CERT_DB_FILE_VERSION;
|
|
|
|
entry->common.flags = flags;
|
|
|
|
|
|
|
|
entry->contentVersion = CERT_DB_CONTENT_VERSION;
|
|
|
|
|
|
|
|
return(entry);
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the version entry
|
|
|
|
*/
|
|
|
|
static certDBEntryContentVersion *
|
|
|
|
ReadDBContentVersionEntry(CERTCertDBHandle *handle)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
certDBEntryContentVersion *entry;
|
|
|
|
SECItem dbkey;
|
|
|
|
SECItem dbentry;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = (certDBEntryContentVersion *)
|
|
|
|
PORT_ArenaAlloc(arena, sizeof(certDBEntryContentVersion));
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry->common.arena = arena;
|
|
|
|
entry->common.type = certDBEntryTypeContentVersion;
|
|
|
|
|
|
|
|
/* now get the database key and format it */
|
|
|
|
dbkey.len = SEC_DB_CONTENT_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
|
|
|
|
dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
|
|
|
|
if ( dbkey.data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_CONTENT_VERSION_KEY,
|
|
|
|
SEC_DB_CONTENT_VERSION_KEY_LEN);
|
|
|
|
|
|
|
|
dbentry.len = 0;
|
|
|
|
dbentry.data = NULL;
|
|
|
|
|
|
|
|
ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
|
|
|
|
|
|
|
|
if ( dbentry.len != 1 ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->contentVersion = dbentry.data[0];
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(entry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode a version entry into byte stream suitable for
|
|
|
|
* the database
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
WriteDBContentVersionEntry(CERTCertDBHandle *handle,
|
|
|
|
certDBEntryContentVersion *entry)
|
|
|
|
{
|
|
|
|
SECItem dbitem, dbkey;
|
|
|
|
PRArenaPool *tmparena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( tmparena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate space for encoded database record, including space
|
|
|
|
* for low level header
|
|
|
|
*/
|
|
|
|
dbitem.len = SEC_DB_ENTRY_HEADER_LEN + 1;
|
|
|
|
|
|
|
|
dbitem.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbitem.len);
|
|
|
|
if ( dbitem.data == NULL) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbitem.data[SEC_DB_ENTRY_HEADER_LEN] = entry->contentVersion;
|
|
|
|
|
|
|
|
/* now get the database key and format it */
|
|
|
|
dbkey.len = SEC_DB_CONTENT_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
|
|
|
|
dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
|
|
|
|
if ( dbkey.data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_CONTENT_VERSION_KEY,
|
|
|
|
SEC_DB_CONTENT_VERSION_KEY_LEN);
|
|
|
|
|
|
|
|
/* now write it to the database */
|
|
|
|
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( tmparena ) {
|
|
|
|
PORT_FreeArena(tmparena, PR_FALSE);
|
|
|
|
}
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* delete a content version entry
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
DeleteDBContentVersionEntry(CERTCertDBHandle *handle)
|
|
|
|
{
|
|
|
|
SECItem dbkey;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now get the database key and format it */
|
|
|
|
dbkey.len = SEC_DB_CONTENT_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
|
|
|
|
dbkey.data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey.len);
|
|
|
|
if ( dbkey.data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_CONTENT_VERSION_KEY,
|
|
|
|
SEC_DB_CONTENT_VERSION_KEY_LEN);
|
|
|
|
|
|
|
|
rv = DeleteDBEntry(handle, certDBEntryTypeContentVersion, &dbkey);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Routines and datastructures to manage the list of certificates for a
|
|
|
|
* particular subject name.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new certificate subject list. If entry exists, then populate
|
|
|
|
* the list with the entries from the permanent database.
|
|
|
|
*/
|
|
|
|
static CERTSubjectList *
|
|
|
|
NewSubjectList(certDBEntrySubject *entry)
|
|
|
|
{
|
|
|
|
PRArenaPool *permarena;
|
|
|
|
unsigned int i;
|
|
|
|
CERTSubjectList *subjectList;
|
|
|
|
CERTSubjectNode *node;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
permarena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( permarena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
subjectList = (CERTSubjectList *)PORT_ArenaAlloc(permarena,
|
|
|
|
sizeof(CERTSubjectList));
|
|
|
|
if ( subjectList == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
subjectList->arena = permarena;
|
|
|
|
subjectList->ncerts = 0;
|
|
|
|
subjectList->head = NULL;
|
|
|
|
subjectList->tail = NULL;
|
|
|
|
subjectList->entry = entry;
|
|
|
|
subjectList->emailAddr = NULL;
|
|
|
|
if ( entry ) {
|
|
|
|
|
|
|
|
/* initialize the list with certs from database entry */
|
|
|
|
for ( i = 0; i < entry->ncerts; i++ ) {
|
|
|
|
/* Init the node */
|
|
|
|
node = (CERTSubjectNode *)PORT_ArenaAlloc(permarena,
|
|
|
|
sizeof(CERTSubjectNode));
|
|
|
|
if ( node == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy certKey and keyID to node */
|
|
|
|
rv = SECITEM_CopyItem(permarena, &node->certKey,
|
|
|
|
&entry->certKeys[i]);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
rv = SECITEM_CopyItem(permarena, &node->keyID,
|
|
|
|
&entry->keyIDs[i]);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the certs are already in order, so just add them
|
|
|
|
* to the tail.
|
|
|
|
*/
|
|
|
|
node->next = NULL;
|
|
|
|
if ( subjectList->tail == NULL ) {
|
|
|
|
/* first in list */
|
|
|
|
subjectList->head = node;
|
|
|
|
subjectList->tail = node;
|
|
|
|
node->prev = NULL;
|
|
|
|
} else {
|
|
|
|
/* add to end of list */
|
|
|
|
node->prev = subjectList->tail;
|
|
|
|
subjectList->tail = node;
|
|
|
|
node->prev->next = node;
|
|
|
|
}
|
|
|
|
subjectList->ncerts++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(subjectList);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
PORT_FreeArena(permarena, PR_FALSE);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the Subject entry in the temp database. It it is not in the
|
|
|
|
* temp database, then get it from the perm DB. It its not there either,
|
|
|
|
* then create a new one.
|
|
|
|
*/
|
|
|
|
static CERTSubjectList *
|
|
|
|
FindSubjectList(CERTCertDBHandle *handle, SECItem *subject, PRBool create)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECItem keyitem;
|
|
|
|
SECStatus rv;
|
|
|
|
DBT namekey;
|
|
|
|
DBT tmpdata;
|
|
|
|
int ret;
|
|
|
|
CERTSubjectList *subjectList = NULL;
|
|
|
|
certDBEntrySubject *entry;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBSubjectKey(subject, arena, &keyitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
namekey.data = keyitem.data;
|
|
|
|
namekey.size = keyitem.len;
|
|
|
|
|
|
|
|
/* lookup in the temporary database */
|
|
|
|
ret = certdb_Get(handle->tempCertDB, &namekey, &tmpdata, 0);
|
|
|
|
|
|
|
|
/* error accessing the database */
|
|
|
|
if ( ret < 0 ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ret == 0 ) { /* found in temp database */
|
|
|
|
if ( tmpdata.size != sizeof(CERTCertificate *) ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy pointer out of database */
|
|
|
|
PORT_Memcpy(&subjectList, tmpdata.data, tmpdata.size);
|
|
|
|
} else { /* not found in temporary database */
|
|
|
|
entry = ReadDBSubjectEntry(handle, subject);
|
|
|
|
if ( entry || create ) {
|
|
|
|
/* decode or create new subject list */
|
|
|
|
subjectList = NewSubjectList(entry);
|
|
|
|
|
|
|
|
/* put it in the temp database */
|
|
|
|
if ( subjectList ) {
|
|
|
|
tmpdata.data = (unsigned char *)(&subjectList);
|
|
|
|
tmpdata.size = sizeof(subjectList);
|
|
|
|
ret = certdb_Put(handle->tempCertDB, &namekey,
|
|
|
|
&tmpdata, R_NOOVERWRITE);
|
|
|
|
if ( ret ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
loser:
|
|
|
|
subjectList = NULL;
|
|
|
|
|
|
|
|
done:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(subjectList);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a temp cert to the temp subject list
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
AddTempCertToSubjectList(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
CERTSubjectList *subjectList;
|
|
|
|
CERTSubjectNode *node, *newnode;
|
|
|
|
CERTCertificate *cmpcert;
|
|
|
|
PRBool newer;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
PORT_Assert(cert->isperm == PR_FALSE);
|
|
|
|
PORT_Assert(cert->subjectList == NULL);
|
|
|
|
|
|
|
|
subjectList = FindSubjectList(cert->dbhandle, &cert->derSubject, PR_TRUE);
|
|
|
|
|
|
|
|
if ( subjectList == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
newnode = (CERTSubjectNode*)PORT_ArenaAlloc(subjectList->arena,
|
|
|
|
sizeof(CERTSubjectNode));
|
|
|
|
/* copy certKey and keyID to node */
|
|
|
|
rv = SECITEM_CopyItem(subjectList->arena, &newnode->certKey,
|
|
|
|
&cert->certKey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
rv = SECITEM_CopyItem(subjectList->arena, &newnode->keyID,
|
|
|
|
&cert->subjectKeyID);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
node = subjectList->head;
|
|
|
|
|
|
|
|
if ( node ) {
|
|
|
|
/* list is not empty */
|
|
|
|
while ( node ) {
|
|
|
|
cmpcert = CERT_FindCertByKeyNoLocking(cert->dbhandle,
|
|
|
|
&node->certKey);
|
|
|
|
if ( cmpcert ) {
|
|
|
|
|
|
|
|
newer = CERT_IsNewer(cert, cmpcert);
|
|
|
|
CERT_DestroyCertificateNoLocking(cmpcert);
|
|
|
|
if ( newer ) {
|
|
|
|
/* insert before this cert */
|
|
|
|
newnode->next = node;
|
|
|
|
newnode->prev = node->prev;
|
|
|
|
if ( newnode->prev ) {
|
|
|
|
newnode->prev->next = newnode;
|
|
|
|
} else {
|
|
|
|
/* at the head of the list */
|
|
|
|
subjectList->head = newnode;
|
|
|
|
}
|
|
|
|
node->prev = newnode;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
/* if we get here, we add the node to the end of the list */
|
|
|
|
newnode->prev = subjectList->tail;
|
|
|
|
newnode->next = NULL;
|
|
|
|
subjectList->tail->next = newnode;
|
|
|
|
subjectList->tail = newnode;
|
|
|
|
} else {
|
|
|
|
/* this is a new/empty list */
|
|
|
|
newnode->next = NULL;
|
|
|
|
newnode->prev = NULL;
|
|
|
|
subjectList->head = newnode;
|
|
|
|
subjectList->tail = newnode;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
subjectList->ncerts++;
|
|
|
|
cert->subjectList = subjectList;
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the node in a subjectList that belongs a cert
|
|
|
|
*/
|
|
|
|
static CERTSubjectNode *
|
|
|
|
FindCertSubjectNode(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
CERTSubjectList *subjectList;
|
|
|
|
CERTSubjectNode *node = NULL;
|
|
|
|
|
|
|
|
PORT_Assert(cert->subjectList);
|
|
|
|
|
|
|
|
subjectList = cert->subjectList;
|
|
|
|
|
|
|
|
if ( subjectList ) {
|
|
|
|
node = subjectList->head;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ( node ) {
|
|
|
|
if ( SECITEM_CompareItem(&node->certKey, &cert->certKey) == SECEqual ){
|
|
|
|
return(node);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove a temp cert from the temp subject list
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
RemoveTempCertFromSubjectList(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
CERTSubjectList *subjectList;
|
|
|
|
CERTSubjectNode *node;
|
|
|
|
SECItem keyitem;
|
|
|
|
DBT namekey;
|
|
|
|
SECStatus rv;
|
|
|
|
int ret;
|
|
|
|
CERTCertDBHandle *handle;
|
|
|
|
|
|
|
|
PORT_Assert(cert->subjectList);
|
|
|
|
|
|
|
|
/* don't remove perm certs */
|
|
|
|
if ( cert->isperm ) {
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
subjectList = cert->subjectList;
|
|
|
|
|
|
|
|
node = FindCertSubjectNode(cert);
|
|
|
|
|
|
|
|
if ( node ) {
|
|
|
|
/* found it, unlink it */
|
|
|
|
if ( node->next ) {
|
|
|
|
node->next->prev = node->prev;
|
|
|
|
} else {
|
|
|
|
/* removing from tail of list */
|
|
|
|
subjectList->tail = node->prev;
|
|
|
|
}
|
|
|
|
if ( node->prev ) {
|
|
|
|
node->prev->next = node->next;
|
|
|
|
} else {
|
|
|
|
/* removing from head of list */
|
|
|
|
subjectList->head = node->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
subjectList->ncerts--;
|
|
|
|
|
|
|
|
/* dont need to free the node, because it is from subjectList
|
|
|
|
* arena.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* remove reference from cert */
|
|
|
|
cert->subjectList = NULL;
|
|
|
|
|
|
|
|
/* if the list is now empty, remove the list from the db and free it */
|
|
|
|
if ( subjectList->head == NULL ) {
|
|
|
|
PORT_Assert(subjectList->ncerts == 0);
|
|
|
|
rv = EncodeDBSubjectKey(&cert->derSubject, subjectList->arena,
|
|
|
|
&keyitem);
|
|
|
|
if ( rv == SECSuccess ) {
|
|
|
|
namekey.data = keyitem.data;
|
|
|
|
namekey.size = keyitem.len;
|
|
|
|
|
|
|
|
handle = cert->dbhandle;
|
|
|
|
|
|
|
|
ret = certdb_Del(handle->tempCertDB, &namekey, 0);
|
|
|
|
/* keep going if it fails */
|
|
|
|
|
|
|
|
if ( cert->dbnickname ) {
|
|
|
|
rv = SEC_DeleteTempNickname(handle, cert->dbnickname);
|
|
|
|
} else if ( cert->nickname ) {
|
|
|
|
rv = SEC_DeleteTempNickname(handle, cert->nickname);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* keep going if it fails */
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(subjectList->arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Assert(cert->subjectList == NULL);
|
|
|
|
|
|
|
|
if ( cert->subjectList != NULL ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* cert is no longer a perm cert, but will remain a temp cert
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
RemovePermSubjectNode(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
CERTSubjectList *subjectList;
|
|
|
|
certDBEntrySubject *entry;
|
|
|
|
unsigned int i;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
PORT_Assert(cert->isperm);
|
|
|
|
if ( !cert->isperm ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
subjectList = cert->subjectList;
|
|
|
|
PORT_Assert(subjectList);
|
|
|
|
if ( subjectList == NULL ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
entry = subjectList->entry;
|
|
|
|
PORT_Assert(entry);
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Assert(entry->ncerts);
|
|
|
|
rv = SECFailure;
|
|
|
|
|
|
|
|
if ( entry->ncerts > 1 ) {
|
|
|
|
for ( i = 0; i < entry->ncerts; i++ ) {
|
|
|
|
if ( SECITEM_CompareItem(&entry->certKeys[i], &cert->certKey) ==
|
|
|
|
SECEqual ) {
|
|
|
|
/* copy rest of list forward one entry */
|
|
|
|
for ( i = i + 1; i < entry->ncerts; i++ ) {
|
|
|
|
entry->certKeys[i-1] = entry->certKeys[i];
|
|
|
|
entry->keyIDs[i-1] = entry->keyIDs[i];
|
|
|
|
}
|
|
|
|
entry->ncerts--;
|
|
|
|
DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
|
|
|
|
rv = WriteDBSubjectEntry(cert->dbhandle, entry);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* no entries left, delete the perm entry in the DB */
|
|
|
|
if ( subjectList->entry->emailAddr ) {
|
|
|
|
/* if the subject had an email record, then delete it too */
|
|
|
|
DeleteDBSMimeEntry(cert->dbhandle, subjectList->entry->emailAddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
DestroyDBEntry((certDBEntry *)subjectList->entry);
|
|
|
|
subjectList->entry = NULL;
|
|
|
|
DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add a cert to the perm subject list
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
AddPermSubjectNode(CERTCertificate *cert, char *nickname)
|
|
|
|
{
|
|
|
|
CERTSubjectList *subjectList;
|
|
|
|
certDBEntrySubject *entry;
|
|
|
|
SECItem *newCertKeys, *newKeyIDs;
|
|
|
|
int i;
|
|
|
|
SECStatus rv;
|
|
|
|
CERTCertificate *cmpcert;
|
|
|
|
unsigned int nnlen;
|
|
|
|
int ncerts;
|
|
|
|
|
|
|
|
subjectList = cert->subjectList;
|
|
|
|
|
|
|
|
PORT_Assert(subjectList);
|
|
|
|
if ( subjectList == NULL ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = subjectList->entry;
|
|
|
|
|
|
|
|
if ( entry ) {
|
|
|
|
ncerts = entry->ncerts;
|
|
|
|
|
|
|
|
if ( nickname && entry->nickname ) {
|
|
|
|
/* nicknames must be the same */
|
|
|
|
PORT_Assert(PORT_Strcmp(nickname, entry->nickname) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ( entry->nickname == NULL ) && ( nickname != NULL ) ) {
|
|
|
|
/* copy nickname into the entry */
|
|
|
|
nnlen = PORT_Strlen(nickname) + 1;
|
|
|
|
entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena,
|
|
|
|
nnlen);
|
|
|
|
if ( entry->nickname == NULL ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
PORT_Memcpy(entry->nickname, nickname, nnlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* a DB entry already exists, so add this cert */
|
|
|
|
newCertKeys = (SECItem *)PORT_ArenaAlloc(entry->common.arena,
|
|
|
|
sizeof(SECItem) *
|
|
|
|
( ncerts + 1 ) );
|
|
|
|
newKeyIDs = (SECItem *)PORT_ArenaAlloc(entry->common.arena,
|
|
|
|
sizeof(SECItem) *
|
|
|
|
( ncerts + 1 ) );
|
|
|
|
|
|
|
|
if ( ( newCertKeys == NULL ) || ( newKeyIDs == NULL ) ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( i = 0; i < ncerts; i++ ) {
|
|
|
|
cmpcert = CERT_FindCertByKeyNoLocking(cert->dbhandle,
|
|
|
|
&entry->certKeys[i]);
|
|
|
|
PORT_Assert(cmpcert);
|
|
|
|
|
|
|
|
if ( CERT_IsNewer(cert, cmpcert) ) {
|
|
|
|
/* insert before cmpcert */
|
|
|
|
rv = SECITEM_CopyItem(entry->common.arena, &newCertKeys[i],
|
|
|
|
&cert->certKey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
rv = SECITEM_CopyItem(entry->common.arena, &newKeyIDs[i],
|
|
|
|
&cert->subjectKeyID);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
/* copy the rest of the entry */
|
|
|
|
for ( ; i < ncerts; i++ ) {
|
|
|
|
newCertKeys[i+1] = entry->certKeys[i];
|
|
|
|
newKeyIDs[i+1] = entry->keyIDs[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update certKeys and keyIDs */
|
|
|
|
entry->certKeys = newCertKeys;
|
|
|
|
entry->keyIDs = newKeyIDs;
|
|
|
|
|
|
|
|
/* increment count */
|
|
|
|
entry->ncerts++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* copy this cert entry */
|
|
|
|
newCertKeys[i] = entry->certKeys[i];
|
|
|
|
newKeyIDs[i] = entry->keyIDs[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( entry->ncerts == ncerts ) {
|
|
|
|
/* insert new one at end */
|
|
|
|
rv = SECITEM_CopyItem(entry->common.arena, &newCertKeys[ncerts],
|
|
|
|
&cert->certKey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
rv = SECITEM_CopyItem(entry->common.arena, &newKeyIDs[ncerts],
|
|
|
|
&cert->subjectKeyID);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update certKeys and keyIDs */
|
|
|
|
entry->certKeys = newCertKeys;
|
|
|
|
entry->keyIDs = newKeyIDs;
|
|
|
|
|
|
|
|
/* increment count */
|
|
|
|
entry->ncerts++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* need to make a new DB entry */
|
|
|
|
entry = NewDBSubjectEntry(&cert->derSubject, &cert->certKey,
|
|
|
|
&cert->subjectKeyID, nickname,
|
|
|
|
NULL, 0);
|
|
|
|
cert->subjectList->entry = entry;
|
|
|
|
}
|
|
|
|
if ( entry ) {
|
|
|
|
DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
|
|
|
|
rv = WriteDBSubjectEntry(cert->dbhandle, entry);
|
|
|
|
} else {
|
|
|
|
rv = SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SECStatus
|
2001-01-30 21:02:28 +00:00
|
|
|
__CERT_TraversePermCertsForSubject(CERTCertDBHandle *handle,
|
|
|
|
SECItem *derSubject,
|
2000-03-31 20:13:40 +00:00
|
|
|
CERTCertCallback cb, void *cbarg)
|
|
|
|
{
|
|
|
|
certDBEntrySubject *entry;
|
|
|
|
int i;
|
|
|
|
CERTCertificate *cert;
|
|
|
|
SECStatus rv = SECSuccess;
|
|
|
|
|
|
|
|
entry = ReadDBSubjectEntry(handle, derSubject);
|
|
|
|
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
for( i = 0; i < entry->ncerts; i++ ) {
|
|
|
|
cert = CERT_FindCertByKey(handle, &entry->certKeys[i]);
|
|
|
|
rv = (* cb)(cert, cbarg);
|
|
|
|
CERT_DestroyCertificate(cert);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
2001-01-30 21:02:28 +00:00
|
|
|
SECStatus
|
|
|
|
CERT_TraversePermCertsForSubject(CERTCertDBHandle *handle, SECItem *derSubject,
|
|
|
|
CERTCertCallback cb, void *cbarg)
|
|
|
|
{
|
|
|
|
return(__CERT_TraversePermCertsForSubject(handle, derSubject, cb, cbarg));
|
|
|
|
}
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
int
|
|
|
|
CERT_NumPermCertsForSubject(CERTCertDBHandle *handle, SECItem *derSubject)
|
|
|
|
{
|
|
|
|
certDBEntrySubject *entry;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
entry = ReadDBSubjectEntry(handle, derSubject);
|
|
|
|
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = entry->ncerts;
|
|
|
|
|
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
2001-01-30 21:02:28 +00:00
|
|
|
__CERT_TraversePermCertsForNickname(CERTCertDBHandle *handle, char *nickname,
|
2000-03-31 20:13:40 +00:00
|
|
|
CERTCertCallback cb, void *cbarg)
|
|
|
|
{
|
|
|
|
certDBEntryNickname *nnentry = NULL;
|
|
|
|
certDBEntrySMime *smentry = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
SECItem *derSubject = NULL;
|
|
|
|
|
|
|
|
nnentry = ReadDBNicknameEntry(handle, nickname);
|
|
|
|
if ( nnentry ) {
|
|
|
|
derSubject = &nnentry->subjectName;
|
|
|
|
} else {
|
|
|
|
smentry = ReadDBSMimeEntry(handle, nickname);
|
|
|
|
if ( smentry ) {
|
|
|
|
derSubject = &smentry->subjectName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( derSubject ) {
|
|
|
|
rv = CERT_TraversePermCertsForSubject(handle, derSubject,
|
|
|
|
cb, cbarg);
|
|
|
|
} else {
|
|
|
|
rv = SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( nnentry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)nnentry);
|
|
|
|
}
|
|
|
|
if ( smentry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)smentry);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
2001-01-30 21:02:28 +00:00
|
|
|
SECStatus
|
|
|
|
CERT_TraversePermCertsForNickname(CERTCertDBHandle *handle, char *nickname,
|
|
|
|
CERTCertCallback cb, void *cbarg)
|
|
|
|
{
|
|
|
|
return(__CERT_TraversePermCertsForNickname(handle, nickname, cb, cbarg));
|
|
|
|
}
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
int
|
|
|
|
CERT_NumPermCertsForNickname(CERTCertDBHandle *handle, char *nickname)
|
|
|
|
{
|
|
|
|
certDBEntryNickname *entry;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
entry = ReadDBNicknameEntry(handle, nickname);
|
|
|
|
|
|
|
|
if ( entry ) {
|
|
|
|
ret = CERT_NumPermCertsForSubject(handle, &entry->subjectName);
|
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
} else {
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
CERT_NumCertsForCertSubject(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if ( cert->subjectList ) {
|
|
|
|
ret = cert->subjectList->ncerts;
|
|
|
|
}
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
CERT_NumPermCertsForCertSubject(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if ( cert->subjectList ) {
|
|
|
|
if ( cert->subjectList->entry ) {
|
|
|
|
ret = cert->subjectList->entry->ncerts;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
CERT_TraverseCertsForSubject(CERTCertDBHandle *handle,
|
|
|
|
CERTSubjectList *subjectList,
|
|
|
|
CERTCertCallback cb, void *cbarg)
|
|
|
|
{
|
|
|
|
CERTSubjectNode *node;
|
|
|
|
CERTCertificate *cert;
|
|
|
|
SECStatus rv = SECSuccess;
|
|
|
|
|
|
|
|
CERT_LockDB(handle);
|
|
|
|
|
|
|
|
node = subjectList->head;
|
|
|
|
while ( node ) {
|
|
|
|
|
|
|
|
cert = CERT_FindCertByKeyNoLocking(handle, &node->certKey);
|
|
|
|
|
|
|
|
PORT_Assert(cert != NULL);
|
|
|
|
|
|
|
|
if ( cert != NULL ) {
|
|
|
|
rv = (* cb)(cert, cbarg);
|
|
|
|
CERT_DestroyCertificateNoLocking(cert);
|
|
|
|
if ( rv == SECFailure ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given a cert, find the cert with the same subject name that
|
|
|
|
* has the given key usage. If the given cert has the correct keyUsage, then
|
|
|
|
* return it, otherwise search the list in order.
|
|
|
|
*/
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByUsage(CERTCertificate *basecert, unsigned int requiredKeyUsage)
|
|
|
|
{
|
|
|
|
CERTSubjectNode *node;
|
|
|
|
CERTCertificate *cert;
|
|
|
|
CERTSubjectList *subjectList;
|
|
|
|
|
|
|
|
if ( ( basecert->keyUsage & requiredKeyUsage ) == requiredKeyUsage ) {
|
|
|
|
return(CERT_DupCertificate(basecert));
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_LockDB(basecert->dbhandle);
|
|
|
|
|
|
|
|
subjectList = basecert->subjectList;
|
|
|
|
|
|
|
|
node = subjectList->head;
|
|
|
|
while ( node ) {
|
|
|
|
|
|
|
|
cert = CERT_FindCertByKeyNoLocking(basecert->dbhandle, &node->certKey);
|
|
|
|
|
|
|
|
PORT_Assert(cert != NULL);
|
|
|
|
|
|
|
|
if ( cert != NULL ) {
|
|
|
|
if ( ( cert->keyUsage & requiredKeyUsage ) ==
|
|
|
|
requiredKeyUsage ) {
|
|
|
|
CERT_UnlockDB(basecert->dbhandle);
|
|
|
|
return(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_DestroyCertificateNoLocking(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_UnlockDB(basecert->dbhandle);
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add a nickname to a cert that doesn't have one
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
AddNicknameToPermCert(CERTCertificate *cert, char *nickname)
|
|
|
|
{
|
|
|
|
certDBEntryCert *entry;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
PORT_Assert(cert->isperm);
|
|
|
|
if ( !cert->isperm ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = cert->dbEntry;
|
|
|
|
PORT_Assert(entry != NULL);
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
|
|
|
|
|
|
|
|
rv = WriteDBCertEntry(cert->dbhandle, entry);
|
|
|
|
if ( rv ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
cert->nickname = PORT_ArenaStrdup(cert->arena, nickname);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add a nickname to a cert that is already in the perm database, but doesn't
|
|
|
|
* have one yet (it is probably an e-mail cert).
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
CERT_AddPermNickname(CERTCertificate *cert, char *nickname)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
CERT_LockDB(cert->dbhandle);
|
|
|
|
|
|
|
|
PORT_Assert(cert->nickname == NULL);
|
|
|
|
PORT_Assert(cert->isperm);
|
|
|
|
PORT_Assert(cert->subjectList != NULL);
|
|
|
|
PORT_Assert(cert->subjectList->entry != NULL);
|
|
|
|
|
|
|
|
if ( cert->nickname != NULL ) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( cert->subjectList == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( cert->subjectList->entry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( cert->subjectList->entry->nickname == NULL ) {
|
|
|
|
/* no nickname for subject */
|
|
|
|
rv = AddNicknameToSubject(cert, nickname);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
rv = AddNicknameToPermCert(cert, nickname);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
rv = SEC_AddTempNickname(cert->dbhandle, nickname,
|
|
|
|
&cert->derSubject);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* subject already has a nickname */
|
|
|
|
rv = AddNicknameToPermCert(cert, cert->subjectList->entry->nickname);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
CERT_UnlockDB(cert->dbhandle);
|
|
|
|
return(SECSuccess);
|
|
|
|
loser:
|
|
|
|
CERT_UnlockDB(cert->dbhandle);
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
static certDBEntryCert *
|
|
|
|
AddCertToPermDB(CERTCertDBHandle *handle, CERTCertificate *cert,
|
|
|
|
char *nickname, CERTCertTrust *trust)
|
|
|
|
{
|
|
|
|
certDBEntryCert *certEntry = NULL;
|
|
|
|
certDBEntryNickname *nicknameEntry = NULL;
|
|
|
|
certDBEntrySubject *subjectEntry = NULL;
|
|
|
|
int state = 0;
|
|
|
|
SECStatus rv;
|
|
|
|
PRBool donnentry = PR_FALSE;
|
|
|
|
|
|
|
|
if ( nickname ) {
|
|
|
|
donnentry = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( cert->subjectList != NULL ) {
|
|
|
|
if ( cert->subjectList->entry != NULL ) {
|
|
|
|
if ( cert->subjectList->entry->ncerts > 0 ) {
|
|
|
|
/* of other certs with same subject exist, then they already
|
|
|
|
* have a nickname, so don't add a new one.
|
|
|
|
*/
|
|
|
|
donnentry = PR_FALSE;
|
|
|
|
nickname = cert->subjectList->entry->nickname;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
certEntry = NewDBCertEntry(&cert->derCert, nickname, trust, 0);
|
|
|
|
if ( certEntry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( donnentry ) {
|
|
|
|
nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
|
|
|
|
if ( nicknameEntry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = WriteDBCertEntry(handle, certEntry);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
state = 1;
|
|
|
|
|
|
|
|
if ( nicknameEntry ) {
|
|
|
|
rv = WriteDBNicknameEntry(handle, nicknameEntry);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
state = 2;
|
|
|
|
|
|
|
|
/* add to or create new subject entry */
|
|
|
|
if ( cert->subjectList ) {
|
|
|
|
rv = AddPermSubjectNode(cert, nickname);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* make a new subject entry - this case is only used when updating
|
|
|
|
* an old version of the database. This is OK because the oldnickname
|
|
|
|
* db format didn't allow multiple certs with the same subject.
|
|
|
|
*/
|
|
|
|
subjectEntry = NewDBSubjectEntry(&cert->derSubject, &cert->certKey,
|
|
|
|
&cert->subjectKeyID, nickname,
|
|
|
|
NULL, 0);
|
|
|
|
if ( subjectEntry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
rv = WriteDBSubjectEntry(handle, subjectEntry);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
state = 3;
|
|
|
|
|
|
|
|
if ( nicknameEntry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)nicknameEntry);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( subjectEntry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)subjectEntry);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(certEntry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
/* don't leave partial entry in the database */
|
|
|
|
if ( state > 0 ) {
|
|
|
|
rv = DeleteDBCertEntry(handle, &cert->certKey);
|
|
|
|
}
|
|
|
|
if ( ( state > 1 ) && donnentry ) {
|
|
|
|
rv = DeleteDBNicknameEntry(handle, nickname);
|
|
|
|
}
|
|
|
|
if ( state > 2 ) {
|
|
|
|
rv = DeleteDBSubjectEntry(handle, &cert->derSubject);
|
|
|
|
}
|
|
|
|
if ( certEntry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)certEntry);
|
|
|
|
}
|
|
|
|
if ( nicknameEntry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)nicknameEntry);
|
|
|
|
}
|
|
|
|
if ( subjectEntry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)subjectEntry);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE - Version 6 DB did not go out to the real world in a release,
|
|
|
|
* so we can remove this function in a later release.
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
UpdateV6DB(CERTCertDBHandle *handle, DB *updatedb)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
DBT key, data;
|
|
|
|
unsigned char *buf, *tmpbuf = NULL;
|
|
|
|
certDBEntryType type;
|
|
|
|
certDBEntryNickname *nnEntry = NULL;
|
|
|
|
certDBEntrySubject *subjectEntry = NULL;
|
|
|
|
certDBEntrySMime *emailEntry = NULL;
|
|
|
|
char *nickname;
|
|
|
|
char *emailAddr;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sequence through the old database and copy all of the entries
|
|
|
|
* to the new database. Subject name entries will have the new
|
|
|
|
* fields inserted into them (with zero length).
|
|
|
|
*/
|
|
|
|
ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
|
|
|
|
if ( ret ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
buf = (unsigned char *)data.data;
|
|
|
|
|
|
|
|
if ( data.size >= 3 ) {
|
|
|
|
if ( buf[0] == 6 ) { /* version number */
|
|
|
|
type = (certDBEntryType)buf[1];
|
|
|
|
if ( type == certDBEntryTypeSubject ) {
|
|
|
|
/* expando subjecto entrieo */
|
|
|
|
tmpbuf = (unsigned char *)PORT_Alloc(data.size + 4);
|
|
|
|
if ( tmpbuf ) {
|
|
|
|
/* copy header stuff */
|
|
|
|
PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN + 2);
|
|
|
|
/* insert 4 more bytes of zero'd header */
|
|
|
|
PORT_Memset(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 2],
|
|
|
|
0, 4);
|
|
|
|
/* copy rest of the data */
|
|
|
|
PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
|
|
|
|
&buf[SEC_DB_ENTRY_HEADER_LEN + 2],
|
|
|
|
data.size - (SEC_DB_ENTRY_HEADER_LEN + 2));
|
|
|
|
|
|
|
|
data.data = (void *)tmpbuf;
|
|
|
|
data.size += 4;
|
|
|
|
buf = tmpbuf;
|
|
|
|
}
|
|
|
|
} else if ( type == certDBEntryTypeCert ) {
|
|
|
|
/* expando certo entrieo */
|
|
|
|
tmpbuf = (unsigned char *)PORT_Alloc(data.size + 3);
|
|
|
|
if ( tmpbuf ) {
|
|
|
|
/* copy header stuff */
|
|
|
|
PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN);
|
|
|
|
|
|
|
|
/* copy trust flage, setting msb's to 0 */
|
|
|
|
tmpbuf[SEC_DB_ENTRY_HEADER_LEN] = 0;
|
|
|
|
tmpbuf[SEC_DB_ENTRY_HEADER_LEN+1] =
|
|
|
|
buf[SEC_DB_ENTRY_HEADER_LEN];
|
|
|
|
tmpbuf[SEC_DB_ENTRY_HEADER_LEN+2] = 0;
|
|
|
|
tmpbuf[SEC_DB_ENTRY_HEADER_LEN+3] =
|
|
|
|
buf[SEC_DB_ENTRY_HEADER_LEN+1];
|
|
|
|
tmpbuf[SEC_DB_ENTRY_HEADER_LEN+4] = 0;
|
|
|
|
tmpbuf[SEC_DB_ENTRY_HEADER_LEN+5] =
|
|
|
|
buf[SEC_DB_ENTRY_HEADER_LEN+2];
|
|
|
|
|
|
|
|
/* copy rest of the data */
|
|
|
|
PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
|
|
|
|
&buf[SEC_DB_ENTRY_HEADER_LEN + 3],
|
|
|
|
data.size - (SEC_DB_ENTRY_HEADER_LEN + 3));
|
|
|
|
|
|
|
|
data.data = (void *)tmpbuf;
|
|
|
|
data.size += 3;
|
|
|
|
buf = tmpbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update the record version number */
|
|
|
|
buf[0] = CERT_DB_FILE_VERSION;
|
|
|
|
|
|
|
|
/* copy to the new database */
|
|
|
|
ret = certdb_Put(handle->permCertDB, &key, &data, 0);
|
|
|
|
if ( tmpbuf ) {
|
|
|
|
PORT_Free(tmpbuf);
|
|
|
|
tmpbuf = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
|
|
|
|
|
|
|
|
ret = certdb_Sync(handle->permCertDB, 0);
|
|
|
|
|
|
|
|
ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
|
|
|
|
if ( ret ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
buf = (unsigned char *)data.data;
|
|
|
|
|
|
|
|
if ( data.size >= 3 ) {
|
|
|
|
if ( buf[0] == CERT_DB_FILE_VERSION ) { /* version number */
|
|
|
|
type = (certDBEntryType)buf[1];
|
|
|
|
if ( type == certDBEntryTypeNickname ) {
|
|
|
|
nickname = &((char *)key.data)[1];
|
|
|
|
|
|
|
|
/* get the matching nickname entry in the new DB */
|
|
|
|
nnEntry = ReadDBNicknameEntry(handle, nickname);
|
|
|
|
if ( nnEntry == NULL ) {
|
|
|
|
goto endloop;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find the subject entry pointed to by nickname */
|
|
|
|
subjectEntry = ReadDBSubjectEntry(handle,
|
|
|
|
&nnEntry->subjectName);
|
|
|
|
if ( subjectEntry == NULL ) {
|
|
|
|
goto endloop;
|
|
|
|
}
|
|
|
|
|
|
|
|
subjectEntry->nickname =
|
|
|
|
(char *)PORT_ArenaAlloc(subjectEntry->common.arena,
|
|
|
|
key.size - 1);
|
|
|
|
if ( subjectEntry->nickname ) {
|
|
|
|
PORT_Memcpy(subjectEntry->nickname, nickname,
|
|
|
|
key.size - 1);
|
|
|
|
rv = WriteDBSubjectEntry(handle, subjectEntry);
|
|
|
|
}
|
|
|
|
} else if ( type == certDBEntryTypeSMimeProfile ) {
|
|
|
|
emailAddr = &((char *)key.data)[1];
|
|
|
|
|
|
|
|
/* get the matching smime entry in the new DB */
|
|
|
|
emailEntry = ReadDBSMimeEntry(handle, emailAddr);
|
|
|
|
if ( emailEntry == NULL ) {
|
|
|
|
goto endloop;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find the subject entry pointed to by nickname */
|
|
|
|
subjectEntry = ReadDBSubjectEntry(handle,
|
|
|
|
&emailEntry->subjectName);
|
|
|
|
if ( subjectEntry == NULL ) {
|
|
|
|
goto endloop;
|
|
|
|
}
|
|
|
|
|
|
|
|
subjectEntry->nickname =
|
|
|
|
(char *)PORT_ArenaAlloc(subjectEntry->common.arena,
|
|
|
|
key.size - 1);
|
|
|
|
if ( subjectEntry->emailAddr ) {
|
|
|
|
PORT_Memcpy(subjectEntry->emailAddr, emailAddr,
|
|
|
|
key.size - 1);
|
|
|
|
rv = WriteDBSubjectEntry(handle, subjectEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
endloop:
|
|
|
|
if ( subjectEntry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)subjectEntry);
|
|
|
|
subjectEntry = NULL;
|
|
|
|
}
|
|
|
|
if ( nnEntry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)nnEntry);
|
|
|
|
nnEntry = NULL;
|
|
|
|
}
|
|
|
|
if ( emailEntry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)emailEntry);
|
|
|
|
emailEntry = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
|
|
|
|
|
|
|
|
ret = certdb_Sync(handle->permCertDB, 0);
|
|
|
|
|
|
|
|
(* updatedb->close)(updatedb);
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
updateV5Callback(CERTCertificate *cert, SECItem *k, void *pdata)
|
|
|
|
{
|
|
|
|
CERTCertDBHandle *handle;
|
|
|
|
certDBEntryCert *entry;
|
|
|
|
CERTCertTrust *trust;
|
|
|
|
|
|
|
|
handle = (CERTCertDBHandle *)pdata;
|
|
|
|
trust = &cert->dbEntry->trust;
|
|
|
|
|
|
|
|
/* SSL user certs can be used for email if they have an email addr */
|
|
|
|
if ( cert->emailAddr && ( trust->sslFlags & CERTDB_USER ) &&
|
|
|
|
( trust->emailFlags == 0 ) ) {
|
|
|
|
trust->emailFlags = CERTDB_USER;
|
|
|
|
}
|
2001-02-10 01:44:34 +00:00
|
|
|
/* servers didn't set the user flags on the server cert.. */
|
|
|
|
if (PORT_Strcmp(cert->dbEntry->nickname,"Server-Cert") == 0) {
|
|
|
|
trust->sslFlags |= CERTDB_USER;
|
|
|
|
}
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
entry = AddCertToPermDB(handle, cert, cert->dbEntry->nickname,
|
|
|
|
&cert->dbEntry->trust);
|
|
|
|
if ( entry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
UpdateV5DB(CERTCertDBHandle *handle, DB *updatedb)
|
|
|
|
{
|
|
|
|
CERTCertDBHandle updatehandle;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
updatehandle.permCertDB = updatedb;
|
2001-01-03 19:51:22 +00:00
|
|
|
updatehandle.dbMon = PZ_NewMonitor(nssILockCertDB);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
rv = SEC_TraversePermCerts(&updatehandle, updateV5Callback,
|
|
|
|
(void *)handle);
|
|
|
|
|
2001-01-03 19:51:22 +00:00
|
|
|
PZ_DestroyMonitor(updatehandle.dbMon);
|
2001-03-07 17:34:44 +00:00
|
|
|
|
|
|
|
(* updatedb->close)(updatedb);
|
|
|
|
return(SECSuccess);
|
2000-03-31 20:13:40 +00:00
|
|
|
}
|
|
|
|
|
2001-02-10 01:44:34 +00:00
|
|
|
static PRBool
|
|
|
|
isV4DB(DB *db) {
|
|
|
|
DBT key,data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
key.data = "Version";
|
|
|
|
key.size = 7;
|
|
|
|
|
|
|
|
ret = (*db->get)(db, &key, &data, 0);
|
|
|
|
if (ret) {
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((data.size == 1) && (*(unsigned char *)data.data <= 4)) {
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
static SECStatus
|
|
|
|
UpdateV4DB(CERTCertDBHandle *handle, DB *updatedb)
|
|
|
|
{
|
|
|
|
DBT key, data;
|
|
|
|
certDBEntryCert *entry, *entry2;
|
|
|
|
SECItem derSubject;
|
|
|
|
int ret;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
CERTCertificate *cert;
|
|
|
|
|
|
|
|
ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
|
|
|
|
|
|
|
|
if ( ret ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if (arena == NULL) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
if ( data.size != 1 ) { /* skip version number */
|
|
|
|
|
|
|
|
/* decode the old DB entry */
|
|
|
|
entry = (certDBEntryCert *)DecodeV4DBCertEntry((unsigned char*)data.data, data.size);
|
|
|
|
derSubject.data = NULL;
|
|
|
|
|
|
|
|
if ( entry ) {
|
|
|
|
cert = CERT_DecodeDERCertificate(&entry->derCert, PR_TRUE,
|
|
|
|
entry->nickname);
|
|
|
|
|
|
|
|
if ( cert != NULL ) {
|
|
|
|
/* add to new database */
|
|
|
|
entry2 = AddCertToPermDB(handle, cert, entry->nickname,
|
|
|
|
&entry->trust);
|
|
|
|
|
|
|
|
CERT_DestroyCertificate(cert);
|
|
|
|
if ( entry2 ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)entry2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
(* updatedb->close)(updatedb);
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* return true if a database key conflict exists
|
|
|
|
*/
|
|
|
|
PRBool
|
|
|
|
SEC_CertDBKeyConflict(SECItem *derCert, CERTCertDBHandle *handle)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
DBT tmpdata;
|
|
|
|
DBT namekey;
|
|
|
|
int ret;
|
|
|
|
SECItem keyitem;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECItem derKey;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get the db key of the cert */
|
|
|
|
rv = CERT_KeyFromDERCert(arena, derCert, &derKey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBCertKey(&derKey, arena, &keyitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
namekey.data = keyitem.data;
|
|
|
|
namekey.size = keyitem.len;
|
|
|
|
|
|
|
|
/* lookup in the temporary database */
|
|
|
|
ret = certdb_Get(handle->tempCertDB, &namekey, &tmpdata, 0);
|
|
|
|
|
|
|
|
if ( ret == 0 ) { /* found in temp database */
|
|
|
|
goto loser;
|
|
|
|
} else { /* not found in temporary database */
|
|
|
|
ret = certdb_Get(handle->permCertDB, &namekey, &tmpdata, 0);
|
|
|
|
if ( ret == 0 ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
|
|
|
|
return(PR_FALSE);
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(PR_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NOTDEF
|
|
|
|
/*
|
|
|
|
* return true if a subject name conflict exists
|
|
|
|
* NOTE: caller must have already made sure that this exact cert
|
|
|
|
* doesn't exist in the DB
|
|
|
|
*/
|
|
|
|
PRBool
|
|
|
|
SEC_CertSubjectConflict(SECItem *derCert, CERTCertDBHandle *handle)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
DBT tmpdata;
|
|
|
|
DBT namekey;
|
|
|
|
int ret;
|
|
|
|
SECItem keyitem;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECItem derName;
|
|
|
|
|
|
|
|
derName.data = NULL;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get the subject name of the cert */
|
|
|
|
rv = CERT_NameFromDERCert(derCert, &derName);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBSubjectKey(&derName, arena, &keyitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
namekey.data = keyitem.data;
|
|
|
|
namekey.size = keyitem.len;
|
|
|
|
|
|
|
|
/* lookup in the temporary database */
|
|
|
|
ret = certdb_Get(handle->tempCertDB, &namekey, &tmpdata, 0);
|
|
|
|
|
|
|
|
if ( ret == 0 ) { /* found in temp database */
|
|
|
|
return(PR_TRUE);
|
|
|
|
} else { /* not found in temporary database */
|
|
|
|
ret = certdb_Get(handle->permCertDB, &namekey, &tmpdata, 0);
|
|
|
|
if ( ret == 0 ) {
|
|
|
|
return(PR_TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
PORT_Free(derName.data);
|
|
|
|
|
|
|
|
return(PR_FALSE);
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
if ( derName.data ) {
|
|
|
|
PORT_Free(derName.data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(PR_TRUE);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* return true if a nickname conflict exists
|
|
|
|
* NOTE: caller must have already made sure that this exact cert
|
|
|
|
* doesn't exist in the DB
|
|
|
|
*/
|
|
|
|
PRBool
|
|
|
|
SEC_CertNicknameConflict(char *nickname, SECItem *derSubject,
|
|
|
|
CERTCertDBHandle *handle)
|
|
|
|
{
|
|
|
|
PRBool rv;
|
|
|
|
certDBEntryNickname *entry;
|
|
|
|
|
|
|
|
if ( nickname == NULL ) {
|
|
|
|
return(PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = ReadDBNicknameEntry(handle, nickname);
|
|
|
|
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
/* no entry for this nickname, so no conflict */
|
|
|
|
return(PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = PR_TRUE;
|
|
|
|
if ( SECITEM_CompareItem(derSubject, &entry->subjectName) == SECEqual ) {
|
|
|
|
/* if subject names are the same, then no conflict */
|
|
|
|
rv = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open the certificate database and index databases. Create them if
|
|
|
|
* they are not there or bad.
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
SEC_OpenPermCertDB(CERTCertDBHandle *handle, PRBool readOnly,
|
|
|
|
CERTDBNameFunc namecb, void *cbarg)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
int openflags;
|
|
|
|
certDBEntryVersion *versionEntry = NULL;
|
|
|
|
DB *updatedb = NULL;
|
|
|
|
char *tmpname;
|
|
|
|
char *certdbname;
|
|
|
|
PRBool updated = PR_FALSE;
|
|
|
|
PRBool forceUpdate = PR_FALSE;
|
|
|
|
|
|
|
|
certdbname = (* namecb)(cbarg, CERT_DB_FILE_VERSION);
|
|
|
|
if ( certdbname == NULL ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( readOnly ) {
|
|
|
|
openflags = O_RDONLY;
|
|
|
|
} else {
|
|
|
|
openflags = O_RDWR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* first open the permanent file based database.
|
|
|
|
*/
|
|
|
|
handle->permCertDB = dbopen( certdbname, openflags, 0600, DB_HASH, 0 );
|
|
|
|
|
|
|
|
/* check for correct version number */
|
|
|
|
if ( handle->permCertDB ) {
|
|
|
|
versionEntry = ReadDBVersionEntry(handle);
|
|
|
|
|
|
|
|
if ( versionEntry == NULL ) {
|
|
|
|
/* no version number */
|
|
|
|
certdb_Close(handle->permCertDB);
|
|
|
|
handle->permCertDB = 0;
|
|
|
|
} else if ( versionEntry->common.version != CERT_DB_FILE_VERSION ) {
|
|
|
|
/* wrong version number, can't update in place */
|
|
|
|
DestroyDBEntry((certDBEntry *)versionEntry);
|
2000-10-02 23:23:50 +00:00
|
|
|
PORT_Free(certdbname);
|
2000-03-31 20:13:40 +00:00
|
|
|
return(SECFailure);
|
2001-08-24 21:15:52 +00:00
|
|
|
} else {
|
|
|
|
DestroyDBEntry((certDBEntry *)versionEntry);
|
|
|
|
versionEntry = NULL;
|
2000-03-31 20:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* if first open fails, try to create a new DB */
|
|
|
|
if ( handle->permCertDB == NULL ) {
|
|
|
|
|
|
|
|
/* don't create if readonly */
|
|
|
|
if ( readOnly ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle->permCertDB = dbopen(certdbname,
|
|
|
|
O_RDWR | O_CREAT | O_TRUNC,
|
|
|
|
0600, DB_HASH, 0);
|
|
|
|
|
|
|
|
/* if create fails then we lose */
|
|
|
|
if ( handle->permCertDB == 0 ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
versionEntry = NewDBVersionEntry(0);
|
|
|
|
if ( versionEntry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = WriteDBVersionEntry(handle, versionEntry);
|
|
|
|
|
|
|
|
DestroyDBEntry((certDBEntry *)versionEntry);
|
|
|
|
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* try to upgrade old db here */
|
|
|
|
tmpname = (* namecb)(cbarg, 6); /* get v6 db name */
|
|
|
|
if ( tmpname ) {
|
|
|
|
updatedb = dbopen( tmpname, O_RDONLY, 0600, DB_HASH, 0 );
|
|
|
|
PORT_Free(tmpname);
|
|
|
|
if ( updatedb ) {
|
|
|
|
rv = UpdateV6DB(handle, updatedb);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
updated = PR_TRUE;
|
|
|
|
} else { /* no v6 db, so try v5 db */
|
|
|
|
tmpname = (* namecb)(cbarg, 5); /* get v5 db name */
|
|
|
|
if ( tmpname ) {
|
|
|
|
updatedb = dbopen( tmpname, O_RDONLY, 0600, DB_HASH, 0 );
|
|
|
|
PORT_Free(tmpname);
|
|
|
|
if ( updatedb ) {
|
|
|
|
rv = UpdateV5DB(handle, updatedb);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
updated = PR_TRUE;
|
|
|
|
} else { /* no v5 db, so try v4 db */
|
|
|
|
/* try to upgrade v4 db */
|
|
|
|
tmpname = (* namecb)(cbarg, 4); /* get v4 db name */
|
|
|
|
if ( tmpname ) {
|
|
|
|
updatedb = dbopen( tmpname, O_RDONLY, 0600,
|
|
|
|
DB_HASH, 0 );
|
|
|
|
PORT_Free(tmpname);
|
|
|
|
if ( updatedb ) {
|
2001-02-10 01:44:34 +00:00
|
|
|
/* NES has v5 db's with v4 db names! */
|
|
|
|
if (isV4DB(updatedb)) {
|
|
|
|
rv = UpdateV4DB(handle, updatedb);
|
|
|
|
} else {
|
|
|
|
rv = UpdateV5DB(handle, updatedb);
|
|
|
|
}
|
2000-03-31 20:13:40 +00:00
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
forceUpdate = PR_TRUE;
|
|
|
|
updated = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize the database with our well known certificates
|
|
|
|
* or in the case of update, just fall down to CERT_AddNewCerts()
|
|
|
|
* below.
|
|
|
|
* Note - if we are updating a really old database, then we try
|
|
|
|
* to push all of the certs into it.
|
|
|
|
*/
|
|
|
|
if ( ( !updated ) || forceUpdate ) {
|
|
|
|
rv = CERT_InitCertDB(handle);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = CERT_AddNewCerts(handle);
|
2000-10-02 23:23:50 +00:00
|
|
|
|
|
|
|
PORT_Free(certdbname);
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
return (SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
|
|
|
|
if ( handle->permCertDB ) {
|
|
|
|
certdb_Close(handle->permCertDB);
|
|
|
|
handle->permCertDB = 0;
|
|
|
|
}
|
|
|
|
|
2000-10-02 23:23:50 +00:00
|
|
|
PORT_Free(certdbname);
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* delete all DB records associated with a particular certificate
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
DeletePermCert(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
SECStatus ret;
|
|
|
|
|
|
|
|
ret = SECSuccess;
|
|
|
|
|
|
|
|
rv = DeleteDBCertEntry(cert->dbhandle, &cert->certKey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
ret = SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( cert->nickname ) {
|
|
|
|
rv = DeleteDBNicknameEntry(cert->dbhandle, cert->nickname);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
ret = SECFailure;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = RemovePermSubjectNode(cert);
|
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete a certificate from the permanent database.
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
SEC_DeletePermCertificate(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
if ( !cert->isperm ) {
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
CERT_LockDB(cert->dbhandle);
|
|
|
|
/* delete the records from the permanent database */
|
|
|
|
rv = DeletePermCert(cert);
|
|
|
|
|
|
|
|
/* no longer permanent */
|
|
|
|
cert->isperm = PR_FALSE;
|
|
|
|
|
|
|
|
/* get rid of dbcert and stuff pointing to it */
|
|
|
|
DestroyDBEntry((certDBEntry *)cert->dbEntry);
|
|
|
|
cert->dbEntry = NULL;
|
|
|
|
cert->trust = NULL;
|
|
|
|
|
|
|
|
/* delete it from the temporary database too. It will remain in
|
|
|
|
* memory until all references go away.
|
|
|
|
*/
|
|
|
|
if (cert->slot) {
|
|
|
|
/* If it's owned by a PKCS #11 slot, don't deleted if from the temp DB just
|
|
|
|
* yet... rv inherited from DeletePermCert (as if anyone checks the return
|
|
|
|
* code from this function anyway. */
|
|
|
|
CERT_DestroyCertificateNoLocking(cert);
|
|
|
|
rv = SECSuccess;
|
|
|
|
} else {
|
|
|
|
rv = CERT_DeleteTempCertificate(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_UnlockDB(cert->dbhandle);
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup a certificate in the databases.
|
|
|
|
*/
|
|
|
|
certDBEntryCert *
|
|
|
|
SEC_FindPermCertByKey(CERTCertDBHandle *handle, SECItem *key)
|
|
|
|
{
|
|
|
|
return(ReadDBCertEntry(handle, key));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup a certificate in the database by name
|
|
|
|
*/
|
|
|
|
certDBEntryCert *
|
|
|
|
SEC_FindPermCertByName(CERTCertDBHandle *handle, SECItem *derSubject)
|
|
|
|
{
|
|
|
|
certDBEntrySubject *subjectEntry;
|
|
|
|
certDBEntryCert *certEntry;
|
|
|
|
|
|
|
|
subjectEntry = ReadDBSubjectEntry(handle, derSubject);
|
|
|
|
|
|
|
|
if ( subjectEntry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
certEntry = ReadDBCertEntry(handle, &subjectEntry->certKeys[0]);
|
|
|
|
DestroyDBEntry((certDBEntry *)subjectEntry);
|
|
|
|
|
|
|
|
return(certEntry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup a certificate in the database by nickname
|
|
|
|
*/
|
|
|
|
certDBEntryCert *
|
|
|
|
SEC_FindPermCertByNickname(CERTCertDBHandle *handle, char *nickname)
|
|
|
|
{
|
|
|
|
certDBEntryNickname *nicknameEntry;
|
|
|
|
certDBEntryCert *certEntry;
|
|
|
|
|
|
|
|
nicknameEntry = ReadDBNicknameEntry(handle, nickname);
|
|
|
|
|
|
|
|
if ( nicknameEntry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
certEntry = SEC_FindPermCertByName(handle, &nicknameEntry->subjectName);
|
|
|
|
DestroyDBEntry((certDBEntry *)nicknameEntry);
|
|
|
|
|
|
|
|
return(certEntry);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Traverse all of the entries in the database of a particular type
|
|
|
|
* call the given function for each one.
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
SEC_TraverseDBEntries(CERTCertDBHandle *handle,
|
|
|
|
certDBEntryType type,
|
|
|
|
SECStatus (* callback)(SECItem *data, SECItem *key,
|
|
|
|
certDBEntryType type, void *pdata),
|
|
|
|
void *udata )
|
|
|
|
{
|
|
|
|
DBT data;
|
|
|
|
DBT key;
|
|
|
|
SECStatus rv;
|
|
|
|
int ret;
|
|
|
|
SECItem dataitem;
|
|
|
|
SECItem keyitem;
|
|
|
|
unsigned char *buf;
|
|
|
|
unsigned char *keybuf;
|
|
|
|
|
|
|
|
ret = certdb_Seq(handle->permCertDB, &key, &data, R_FIRST);
|
|
|
|
|
|
|
|
if ( ret ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
buf = (unsigned char *)data.data;
|
|
|
|
|
|
|
|
if ( buf[1] == (unsigned char)type ) {
|
|
|
|
dataitem.len = data.size;
|
|
|
|
dataitem.data = buf;
|
|
|
|
dataitem.type = siBuffer;
|
|
|
|
keyitem.len = key.size - SEC_DB_KEY_HEADER_LEN;
|
|
|
|
keybuf = (unsigned char *)key.data;
|
|
|
|
keyitem.data = &keybuf[SEC_DB_KEY_HEADER_LEN];
|
|
|
|
keyitem.type = siBuffer;
|
|
|
|
|
|
|
|
rv = (* callback)(&dataitem, &keyitem, type, udata);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while ( certdb_Seq(handle->permCertDB, &key, &data, R_NEXT) == 0 );
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
PermCertCallback certfunc;
|
|
|
|
CERTCertDBHandle *handle;
|
|
|
|
void *data;
|
|
|
|
} PermCertCallbackState;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* traversal callback to decode certs and call callers callback
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
certcallback(SECItem *dbdata, SECItem *dbkey, certDBEntryType type, void *data)
|
|
|
|
{
|
|
|
|
PermCertCallbackState *mystate;
|
|
|
|
SECStatus rv;
|
|
|
|
certDBEntryCert entry;
|
|
|
|
SECItem entryitem;
|
|
|
|
CERTCertificate *cert;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
mystate = (PermCertCallbackState *)data;
|
|
|
|
entry.common.version = (unsigned int)dbdata->data[0];
|
|
|
|
entry.common.type = (certDBEntryType)dbdata->data[1];
|
|
|
|
entry.common.flags = (unsigned int)dbdata->data[2];
|
|
|
|
entry.common.arena = arena;
|
|
|
|
|
|
|
|
entryitem.len = dbdata->len - SEC_DB_ENTRY_HEADER_LEN;
|
|
|
|
entryitem.data = &dbdata->data[SEC_DB_ENTRY_HEADER_LEN];
|
|
|
|
|
|
|
|
rv = DecodeDBCertEntry(&entry, &entryitem);
|
|
|
|
if (rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
entry.derCert.type = siBuffer;
|
|
|
|
|
|
|
|
cert = CERT_DecodeDERCertificate(&entry.derCert, PR_FALSE,
|
|
|
|
entry.nickname);
|
|
|
|
cert->dbEntry = &entry;
|
|
|
|
cert->trust = &entry.trust;
|
|
|
|
cert->dbhandle = mystate->handle;
|
|
|
|
|
|
|
|
if ( CERT_IsCACert(cert, NULL) ||
|
|
|
|
(( cert->trust->sslFlags & CERTDB_VALID_CA ) ||
|
|
|
|
( cert->trust->emailFlags & CERTDB_VALID_CA ) ||
|
|
|
|
( cert->trust->objectSigningFlags & CERTDB_VALID_CA)) ) {
|
|
|
|
cert->nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = (* mystate->certfunc)(cert, dbkey, mystate->data);
|
|
|
|
|
|
|
|
/* arena destroyed by SEC_DestroyCert */
|
|
|
|
CERT_DestroyCertificateNoLocking(cert);
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Traverse all of the certificates in the permanent database and
|
|
|
|
* call the given function for each one; expect the caller to have lock.
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
TraversePermCertsNoLocking(CERTCertDBHandle *handle,
|
|
|
|
SECStatus (* certfunc)(CERTCertificate *cert,
|
|
|
|
SECItem *k,
|
|
|
|
void *pdata),
|
|
|
|
void *udata )
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
PermCertCallbackState mystate;
|
|
|
|
|
|
|
|
mystate.certfunc = certfunc;
|
|
|
|
mystate.handle = handle;
|
|
|
|
mystate.data = udata;
|
|
|
|
rv = SEC_TraverseDBEntries(handle, certDBEntryTypeCert, certcallback,
|
|
|
|
(void *)&mystate);
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Traverse all of the certificates in the permanent database and
|
|
|
|
* call the given function for each one.
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
SEC_TraversePermCerts(CERTCertDBHandle *handle,
|
|
|
|
SECStatus (* certfunc)(CERTCertificate *cert, SECItem *k,
|
|
|
|
void *pdata),
|
|
|
|
void *udata )
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
CERT_LockDB(handle);
|
|
|
|
rv = TraversePermCertsNoLocking(handle, certfunc, udata);
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Close the database
|
|
|
|
*/
|
|
|
|
void
|
2001-01-30 21:02:28 +00:00
|
|
|
__CERT_ClosePermCertDB(CERTCertDBHandle *handle)
|
2000-03-31 20:13:40 +00:00
|
|
|
{
|
|
|
|
if ( handle ) {
|
|
|
|
if ( handle->permCertDB ) {
|
|
|
|
if ( handle->statusConfig ) {
|
|
|
|
PORT_Assert(handle->statusConfig->statusDestroy != NULL);
|
|
|
|
(void) (* handle->statusConfig->statusDestroy)(handle->statusConfig);
|
|
|
|
handle->statusConfig = NULL; /* Destroy frees the structure */
|
|
|
|
PORT_Assert(handle->statusConfig == NULL);
|
|
|
|
}
|
|
|
|
certdb_Close( handle->permCertDB );
|
|
|
|
handle->permCertDB = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2001-01-30 21:02:28 +00:00
|
|
|
void
|
|
|
|
CERT_ClosePermCertDB(CERTCertDBHandle *handle)
|
|
|
|
{
|
|
|
|
__CERT_ClosePermCertDB(handle);
|
|
|
|
}
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
/*
|
|
|
|
* Get the trust attributes from a certificate
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
CERT_GetCertTrust(CERTCertificate *cert, CERTCertTrust *trust)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
CERT_LockCertTrust(cert);
|
|
|
|
|
|
|
|
if ( cert->trust == NULL ) {
|
|
|
|
rv = SECFailure;
|
|
|
|
} else {
|
|
|
|
*trust = *cert->trust;
|
|
|
|
rv = SECSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_UnlockCertTrust(cert);
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
2000-09-06 22:10:07 +00:00
|
|
|
static char *
|
|
|
|
cert_parseNickname(char *nickname)
|
|
|
|
{
|
|
|
|
char *cp;
|
|
|
|
|
|
|
|
for (cp=nickname; *cp && *cp != ':'; cp++);
|
|
|
|
|
2001-04-11 22:28:11 +00:00
|
|
|
if (*cp == ':') return cp+1;
|
2000-09-06 22:10:07 +00:00
|
|
|
return nickname;
|
|
|
|
}
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
/*
|
|
|
|
* Change the trust attributes of a certificate and make them permanent
|
|
|
|
* in the database.
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
CERT_ChangeCertTrust(CERTCertDBHandle *handle, CERTCertificate *cert,
|
|
|
|
CERTCertTrust *trust)
|
|
|
|
{
|
|
|
|
certDBEntryCert *entry;
|
|
|
|
int rv;
|
|
|
|
SECStatus ret;
|
|
|
|
|
|
|
|
CERT_LockDB(handle);
|
|
|
|
CERT_LockCertTrust(cert);
|
|
|
|
/* only set the trust on permanent certs */
|
|
|
|
if ( cert->trust == NULL ) {
|
|
|
|
ret = SECFailure;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
*cert->trust = *trust;
|
|
|
|
if ( cert->dbEntry == NULL ) {
|
|
|
|
ret = SECSuccess; /* not in permanent database */
|
2000-09-06 22:10:07 +00:00
|
|
|
if ((cert->slot) && PK11_IsReadOnly(cert->slot)) {
|
|
|
|
char *nickname = cert_parseNickname(cert->nickname);
|
|
|
|
ret = CERT_AddTempCertToPerm(cert, nickname, trust);
|
|
|
|
}
|
2000-03-31 20:13:40 +00:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = cert->dbEntry;
|
|
|
|
entry->trust = *trust;
|
|
|
|
|
|
|
|
rv = WriteDBCertEntry(handle, entry);
|
|
|
|
if ( rv ) {
|
|
|
|
ret = SECFailure;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = SECSuccess;
|
|
|
|
|
|
|
|
done:
|
|
|
|
CERT_UnlockCertTrust(cert);
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname,
|
|
|
|
CERTCertTrust *trust)
|
|
|
|
{
|
|
|
|
char *oldnn;
|
|
|
|
certDBEntryCert *entry;
|
|
|
|
SECStatus rv;
|
|
|
|
PRBool conflict;
|
|
|
|
SECStatus ret;
|
|
|
|
|
|
|
|
PORT_Assert(cert->dbhandle);
|
|
|
|
|
|
|
|
CERT_LockDB(cert->dbhandle);
|
|
|
|
|
|
|
|
PORT_Assert(cert->istemp);
|
|
|
|
PORT_Assert(!cert->isperm);
|
|
|
|
PORT_Assert(!cert->dbEntry);
|
|
|
|
|
|
|
|
/* don't add a conflicting nickname */
|
|
|
|
conflict = SEC_CertNicknameConflict(nickname, &cert->derSubject,
|
|
|
|
cert->dbhandle);
|
|
|
|
if ( conflict ) {
|
|
|
|
ret = SECFailure;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* save old nickname so that we can delete it */
|
|
|
|
oldnn = cert->nickname;
|
|
|
|
|
|
|
|
entry = AddCertToPermDB(cert->dbhandle, cert, nickname, trust);
|
|
|
|
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
ret = SECFailure;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
cert->nickname = (entry->nickname) ? PORT_ArenaStrdup(cert->arena,entry->nickname) : NULL;
|
|
|
|
cert->trust = &entry->trust;
|
|
|
|
cert->isperm = PR_TRUE;
|
|
|
|
cert->dbEntry = entry;
|
|
|
|
|
|
|
|
if ( nickname && oldnn && ( PORT_Strcmp(nickname, oldnn) != 0 ) ) {
|
|
|
|
/* only delete the old one if they are not the same */
|
|
|
|
/* delete old nickname from temp database */
|
|
|
|
rv = SEC_DeleteTempNickname(cert->dbhandle, oldnn);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
/* do we care?? */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* add new nickname to temp database */
|
|
|
|
if ( cert->nickname ) {
|
|
|
|
rv = SEC_AddTempNickname(cert->dbhandle, cert->nickname,
|
|
|
|
&cert->derSubject);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
ret = SECFailure;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = SECSuccess;
|
|
|
|
done:
|
|
|
|
CERT_UnlockDB(cert->dbhandle);
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open the certificate database and index databases. Create them if
|
|
|
|
* they are not there or bad.
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
CERT_OpenCertDB(CERTCertDBHandle *handle, PRBool readOnly,
|
|
|
|
CERTDBNameFunc namecb, void *cbarg)
|
|
|
|
{
|
|
|
|
int rv;
|
2001-02-28 22:50:12 +00:00
|
|
|
#define DBM_DEFAULT 0
|
|
|
|
static const HASHINFO hashInfo = {
|
|
|
|
DBM_DEFAULT, /* bucket size */
|
|
|
|
DBM_DEFAULT, /* fill factor */
|
|
|
|
DBM_DEFAULT, /* number of elements */
|
|
|
|
256 * 1024, /* bytes to cache */
|
|
|
|
DBM_DEFAULT, /* hash function */
|
|
|
|
DBM_DEFAULT /* byte order */
|
|
|
|
};
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
certdb_InitDBLock();
|
|
|
|
|
2001-01-03 19:51:22 +00:00
|
|
|
handle->dbMon = PZ_NewMonitor(nssILockCertDB);
|
2000-03-31 20:13:40 +00:00
|
|
|
PORT_Assert(handle->dbMon != NULL);
|
|
|
|
|
|
|
|
handle->spkDigestInfo = NULL;
|
|
|
|
handle->statusConfig = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open the memory resident decoded cert database.
|
|
|
|
*/
|
2001-02-28 22:50:12 +00:00
|
|
|
handle->tempCertDB = dbopen(0, O_RDWR | O_CREAT, 0600, DB_HASH, &hashInfo);
|
2000-03-31 20:13:40 +00:00
|
|
|
if ( !handle->tempCertDB ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = SEC_OpenPermCertDB(handle, readOnly, namecb, cbarg);
|
|
|
|
if ( rv ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
|
|
|
|
if ( handle->tempCertDB ) {
|
|
|
|
certdb_Close(handle->tempCertDB);
|
|
|
|
handle->tempCertDB = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
certDBFilenameCallback(void *arg, int dbVersion)
|
|
|
|
{
|
2000-10-02 23:23:50 +00:00
|
|
|
return(PORT_Strdup((char *)arg));
|
2000-03-31 20:13:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
CERT_OpenCertDBFilename(CERTCertDBHandle *handle, char *certdbname,
|
|
|
|
PRBool readOnly)
|
|
|
|
{
|
|
|
|
return(CERT_OpenCertDB(handle, readOnly, certDBFilenameCallback,
|
|
|
|
(void *)certdbname));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a nickname to the temp database
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
SEC_AddTempNickname(CERTCertDBHandle *handle, char *nickname,
|
|
|
|
SECItem *subjectName)
|
|
|
|
{
|
|
|
|
DBT namekey;
|
|
|
|
int ret;
|
|
|
|
SECItem nameitem;
|
|
|
|
SECStatus rv;
|
|
|
|
DBT keydata;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECItem tmpitem;
|
|
|
|
|
|
|
|
PORT_Assert(nickname != NULL);
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBNicknameKey(nickname, arena, &nameitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
namekey.data = nameitem.data;
|
|
|
|
namekey.size = nameitem.len;
|
|
|
|
|
|
|
|
/* see if an entry already exists */
|
|
|
|
ret = certdb_Get(handle->tempCertDB, &namekey, &keydata, 0);
|
|
|
|
|
|
|
|
if ( ret == 0 ) {
|
|
|
|
/* found in temp database */
|
|
|
|
tmpitem.data = (unsigned char*)keydata.data;
|
|
|
|
tmpitem.len = keydata.size;
|
|
|
|
|
|
|
|
if ( SECITEM_CompareItem(subjectName, &tmpitem) == SECEqual ) {
|
|
|
|
/* same subject name */
|
|
|
|
goto done;
|
|
|
|
} else {
|
|
|
|
/* different subject name is an error */
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
keydata.data = subjectName->data;
|
|
|
|
keydata.size = subjectName->len;
|
|
|
|
|
|
|
|
/* put into temp byname index */
|
|
|
|
ret = certdb_Put(handle->tempCertDB, &namekey, &keydata, R_NOOVERWRITE);
|
|
|
|
|
|
|
|
if ( ret ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
SEC_DeleteTempNickname(CERTCertDBHandle *handle, char *nickname)
|
|
|
|
{
|
|
|
|
DBT namekey;
|
|
|
|
SECStatus rv;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECItem nameitem;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
PORT_Assert(nickname != NULL);
|
|
|
|
if ( nickname == NULL ) {
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* format a database key based on the nickname */
|
|
|
|
if ( nickname ) {
|
|
|
|
rv = EncodeDBNicknameKey(nickname, arena, &nameitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
namekey.data = nameitem.data;
|
|
|
|
namekey.size = nameitem.len;
|
|
|
|
|
|
|
|
ret = certdb_Del(handle->tempCertDB, &namekey, 0);
|
|
|
|
if ( ret ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode a certificate and enter it into the temporary certificate database.
|
|
|
|
* Deal with nicknames correctly
|
|
|
|
*
|
|
|
|
* nickname is only used if isperm == PR_TRUE
|
|
|
|
*
|
|
|
|
* This is the private entry point, and locking is optional
|
|
|
|
*/
|
|
|
|
static CERTCertificate *
|
|
|
|
NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert, char *nickname,
|
|
|
|
PRBool isperm, PRBool copyDER, PRBool lockdb)
|
|
|
|
|
|
|
|
{
|
|
|
|
DBT key;
|
|
|
|
DBT data;
|
|
|
|
int status;
|
|
|
|
CERTCertificate *cert = NULL;
|
|
|
|
PRBool promoteError = PR_TRUE;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECItem keyitem;
|
|
|
|
SECStatus rv;
|
2000-12-08 03:35:29 +00:00
|
|
|
|
|
|
|
if ( lockdb ) {
|
|
|
|
CERT_LockDB(handle);
|
|
|
|
}
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
if ( isperm == PR_FALSE ) {
|
2000-12-08 03:35:29 +00:00
|
|
|
cert = CERT_FindCertByDERCertNoLocking(handle, derCert);
|
2000-03-31 20:13:40 +00:00
|
|
|
if ( cert ) {
|
2000-12-08 03:35:29 +00:00
|
|
|
goto winner;
|
2000-03-31 20:13:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nickname = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
cert = CERT_DecodeDERCertificate(derCert, copyDER, nickname );
|
|
|
|
|
|
|
|
if ( cert == NULL ) {
|
|
|
|
/* We want to save the decoding error here */
|
|
|
|
promoteError = PR_FALSE;
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
cert->dbhandle = handle;
|
|
|
|
|
|
|
|
/* only save pointer to cert in database */
|
|
|
|
data.data = &cert;
|
|
|
|
data.size = sizeof(cert);
|
|
|
|
|
|
|
|
/* if this is a perm cert, then it is already in the subject db */
|
|
|
|
if ( isperm == PR_FALSE ) {
|
|
|
|
/* enter into the subject index */
|
|
|
|
rv = AddTempCertToSubjectList(cert);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Since it's not a perm cert, add it to the key hash lookup; if it
|
|
|
|
* is permanent it will either already be there or will get put there
|
|
|
|
* later along with the rest of the perm certs. A failure of the
|
|
|
|
* addition does not seem to warrant failing this whole function,
|
|
|
|
* so we intentionally ignore the returned status.
|
|
|
|
*/
|
|
|
|
(void) AddCertToSPKDigestTable(handle, cert);
|
|
|
|
} else {
|
|
|
|
cert->subjectList = FindSubjectList(cert->dbhandle, &cert->derSubject,
|
|
|
|
PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBCertKey(&cert->certKey, arena, &keyitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
key.data = keyitem.data;
|
|
|
|
key.size = keyitem.len;
|
|
|
|
|
|
|
|
/* enter into main db */
|
|
|
|
status = certdb_Put(handle->tempCertDB, &key, &data, R_NOOVERWRITE);
|
|
|
|
if ( status ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( cert->nickname ) {
|
|
|
|
status = SEC_AddTempNickname(handle, cert->nickname,
|
|
|
|
&cert->derSubject);
|
|
|
|
if ( status ) {
|
|
|
|
promoteError = PR_FALSE;
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cert->isperm = isperm;
|
|
|
|
cert->istemp = PR_TRUE;
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
|
2000-12-08 03:35:29 +00:00
|
|
|
winner:
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
if ( lockdb ) {
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(cert);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( cert ) {
|
|
|
|
CERT_DestroyCertificateNoLocking(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( promoteError ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( lockdb ) {
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode a certificate and enter it into the temporary certificate database.
|
|
|
|
* Deal with nicknames correctly
|
|
|
|
*
|
|
|
|
* nickname is only used if isperm == PR_TRUE
|
|
|
|
*
|
|
|
|
* This is the public entry point and does locking.
|
|
|
|
*/
|
|
|
|
CERTCertificate *
|
2001-01-30 21:02:28 +00:00
|
|
|
__CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert,
|
2000-03-31 20:13:40 +00:00
|
|
|
char *nickname, PRBool isperm, PRBool copyDER)
|
|
|
|
{
|
|
|
|
return( NewTempCertificate(handle, derCert, nickname, isperm, copyDER,
|
|
|
|
PR_TRUE) );
|
|
|
|
}
|
|
|
|
|
2001-01-30 21:02:28 +00:00
|
|
|
CERTCertificate *
|
|
|
|
CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert,
|
|
|
|
char *nickname, PRBool isperm, PRBool copyDER)
|
|
|
|
{
|
|
|
|
return( __CERT_NewTempCertificate(handle, derCert, nickname,
|
|
|
|
isperm, copyDER) );
|
|
|
|
}
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
/*
|
|
|
|
* Decode a permanent certificate and enter it into the temporary certificate
|
|
|
|
* database.
|
|
|
|
*/
|
|
|
|
static CERTCertificate *
|
|
|
|
SEC_AddPermCertToTemp(CERTCertDBHandle *handle, certDBEntryCert *entry)
|
|
|
|
{
|
|
|
|
CERTCertificate *cert;
|
|
|
|
|
|
|
|
/* we already hold the lock */
|
|
|
|
cert = NewTempCertificate(handle, &entry->derCert, entry->nickname,
|
|
|
|
PR_TRUE, PR_TRUE, PR_FALSE);
|
|
|
|
if ( !cert ) {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
cert->dbEntry = entry;
|
|
|
|
|
|
|
|
cert->trust = &entry->trust;
|
|
|
|
|
|
|
|
return(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
CERT_DeleteTempCertificate(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
DBT nameKey;
|
|
|
|
CERTCertDBHandle *handle;
|
|
|
|
SECItem keyitem;
|
|
|
|
PRArenaPool *arena;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
handle = cert->dbhandle;
|
|
|
|
|
|
|
|
if ( !cert->istemp ) {
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cert->slot) {
|
|
|
|
PK11_FreeSlot(cert->slot);
|
|
|
|
cert->slot = NULL;
|
|
|
|
cert->pkcs11ID = CK_INVALID_KEY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* delete from subject list (also takes care of nickname) */
|
|
|
|
rv = RemoveTempCertFromSubjectList(cert);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !cert->isperm ) {
|
|
|
|
/*
|
|
|
|
* Remove the cert from the subject public key digest table,
|
|
|
|
* though we do not care if the removal fails (perhaps meaning
|
|
|
|
* the cert wasn't even there).
|
|
|
|
*/
|
|
|
|
(void) RemoveCertFromSPKDigestTable(handle, cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBCertKey(&cert->certKey, arena, &keyitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
nameKey.data = keyitem.data;
|
|
|
|
nameKey.size = keyitem.len;
|
|
|
|
/* delete the cert */
|
|
|
|
ret = certdb_Del(handle->tempCertDB, &nameKey, 0);
|
|
|
|
if ( ret ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
cert->istemp = PR_FALSE;
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup a certificate in the databases.
|
|
|
|
*/
|
|
|
|
static CERTCertificate *
|
|
|
|
FindCertByKey(CERTCertDBHandle *handle, SECItem *certKey, PRBool lockdb)
|
|
|
|
{
|
|
|
|
DBT tmpdata;
|
|
|
|
int ret;
|
|
|
|
SECItem keyitem;
|
|
|
|
DBT key;
|
|
|
|
SECStatus rv;
|
|
|
|
CERTCertificate *cert = NULL;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
certDBEntryCert *entry;
|
|
|
|
PRBool locked = PR_FALSE;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBCertKey(certKey, arena, &keyitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
key.data = keyitem.data;
|
|
|
|
key.size = keyitem.len;
|
|
|
|
|
|
|
|
if ( lockdb ) {
|
|
|
|
locked = PR_TRUE;
|
|
|
|
CERT_LockDB(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* lookup in the temporary database */
|
|
|
|
ret = certdb_Get( handle->tempCertDB, &key, &tmpdata, 0 );
|
|
|
|
|
|
|
|
/* error accessing the database */
|
|
|
|
if ( ret < 0 ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ret == 0 ) { /* found in temp database */
|
|
|
|
if ( tmpdata.size != sizeof(CERTCertificate *) ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Memcpy(&cert, tmpdata.data, tmpdata.size);
|
|
|
|
CERT_LockCertRefCount(cert);
|
|
|
|
cert->referenceCount++;
|
|
|
|
CERT_UnlockCertRefCount(cert);
|
|
|
|
}
|
|
|
|
if ( ret != 0 ) {
|
|
|
|
/* not found in temporary database */
|
|
|
|
|
|
|
|
/* find in perm database */
|
|
|
|
entry = SEC_FindPermCertByKey(handle, certKey);
|
|
|
|
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
cert = SEC_AddPermCertToTemp(handle, entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( locked ) {
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup a certificate in the databases, with locking
|
|
|
|
*/
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByKey(CERTCertDBHandle *handle, SECItem *certKey)
|
|
|
|
{
|
|
|
|
return(FindCertByKey(handle, certKey, PR_TRUE));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup a certificate in the databases without locking
|
|
|
|
*/
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByKeyNoLocking(CERTCertDBHandle *handle, SECItem *certKey)
|
|
|
|
{
|
|
|
|
return(FindCertByKey(handle, certKey, PR_FALSE));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate a key from an issuerAndSerialNumber, and find the
|
|
|
|
* associated cert in the database.
|
|
|
|
*/
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByIssuerAndSN(CERTCertDBHandle *handle, CERTIssuerAndSN *issuerAndSN)
|
|
|
|
{
|
|
|
|
SECItem certKey;
|
|
|
|
CERTCertificate *cert;
|
|
|
|
|
|
|
|
certKey.len = issuerAndSN->serialNumber.len + issuerAndSN->derIssuer.len;
|
|
|
|
certKey.data = (unsigned char*)PORT_Alloc(certKey.len);
|
|
|
|
|
|
|
|
if ( certKey.data == NULL ) {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy the serialNumber */
|
|
|
|
PORT_Memcpy(certKey.data, issuerAndSN->serialNumber.data,
|
|
|
|
issuerAndSN->serialNumber.len);
|
|
|
|
|
|
|
|
/* copy the issuer */
|
|
|
|
PORT_Memcpy( &certKey.data[issuerAndSN->serialNumber.len],
|
|
|
|
issuerAndSN->derIssuer.data, issuerAndSN->derIssuer.len);
|
|
|
|
|
|
|
|
cert = CERT_FindCertByKey(handle, &certKey);
|
|
|
|
|
|
|
|
PORT_Free(certKey.data);
|
|
|
|
|
|
|
|
return(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup a certificate in the database by name
|
|
|
|
*/
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByName(CERTCertDBHandle *handle, SECItem *name)
|
|
|
|
{
|
|
|
|
CERTCertificate *cert = NULL;
|
|
|
|
CERTSubjectList *subjectList;
|
|
|
|
|
|
|
|
CERT_LockDB(handle);
|
|
|
|
|
|
|
|
subjectList = FindSubjectList(handle, name, PR_FALSE);
|
|
|
|
|
|
|
|
if ( subjectList ) {
|
|
|
|
PORT_Assert(subjectList->head);
|
|
|
|
cert = CERT_FindCertByKeyNoLocking(handle,
|
|
|
|
&subjectList->head->certKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
|
|
|
|
return(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup a certificate in the database by name and key ID
|
|
|
|
*/
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByKeyID(CERTCertDBHandle *handle, SECItem *name, SECItem *keyID)
|
|
|
|
{
|
|
|
|
CERTCertificate *cert = NULL;
|
|
|
|
CERTSubjectList *subjectList;
|
|
|
|
CERTSubjectNode *node;
|
|
|
|
|
|
|
|
CERT_LockDB(handle);
|
|
|
|
|
|
|
|
/* find the list of certs for the given subject */
|
|
|
|
subjectList = FindSubjectList(handle, name, PR_FALSE);
|
|
|
|
|
|
|
|
if ( subjectList ) {
|
|
|
|
PORT_Assert(subjectList->head);
|
|
|
|
node = subjectList->head;
|
|
|
|
|
|
|
|
/* walk through the certs until we find one with a matching key ID */
|
|
|
|
while ( node ) {
|
|
|
|
if ( SECITEM_CompareItem(keyID, &node->keyID) == SECEqual ) {
|
|
|
|
cert = CERT_FindCertByKeyNoLocking(handle, &node->certKey);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
|
|
|
|
return(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* look up a cert by its nickname string
|
|
|
|
*/
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByNickname(CERTCertDBHandle *handle, char *nickname)
|
|
|
|
{
|
|
|
|
DBT tmpdata;
|
|
|
|
DBT namekey;
|
|
|
|
CERTCertificate *cert;
|
|
|
|
SECStatus rv;
|
|
|
|
int ret;
|
|
|
|
SECItem keyitem;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
certDBEntryCert *entry;
|
|
|
|
|
|
|
|
PORT_Assert(nickname != NULL);
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBNicknameKey(nickname, arena, &keyitem);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
namekey.data = keyitem.data;
|
|
|
|
namekey.size = keyitem.len;
|
|
|
|
|
|
|
|
/* lookup in the temporary database */
|
|
|
|
ret = certdb_Get(handle->tempCertDB, &namekey, &tmpdata, 0);
|
|
|
|
|
|
|
|
/* error accessing the database */
|
|
|
|
if ( ret < 0 ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ret == 0 ) { /* found in temp database */
|
|
|
|
SECItem nameitem;
|
|
|
|
|
|
|
|
nameitem.len = tmpdata.size;
|
|
|
|
nameitem.data = (unsigned char *)PORT_Alloc(tmpdata.size);
|
|
|
|
if ( nameitem.data == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(nameitem.data, tmpdata.data, nameitem.len);
|
|
|
|
cert = CERT_FindCertByName(handle, &nameitem);
|
|
|
|
PORT_Free(nameitem.data);
|
|
|
|
} else { /* not found in temporary database */
|
|
|
|
|
|
|
|
CERT_LockDB(handle);
|
|
|
|
|
|
|
|
entry = SEC_FindPermCertByNickname(handle, nickname);
|
|
|
|
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
cert = SEC_AddPermCertToTemp(handle, entry);
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
|
|
|
|
return(cert);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* look for the given DER certificate in the database
|
|
|
|
*/
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByDERCert(CERTCertDBHandle *handle, SECItem *derCert)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena;
|
|
|
|
SECItem certKey;
|
|
|
|
SECStatus rv;
|
|
|
|
CERTCertificate *cert = NULL;
|
|
|
|
|
|
|
|
/* create a scratch arena */
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* extract the database key from the cert */
|
|
|
|
rv = CERT_KeyFromDERCert(arena, derCert, &certKey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find the certificate */
|
|
|
|
cert = CERT_FindCertByKey(handle, &certKey);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(cert);
|
|
|
|
}
|
|
|
|
|
2000-12-08 03:35:29 +00:00
|
|
|
/*
|
|
|
|
* look for the given DER certificate in the database
|
|
|
|
*/
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByDERCertNoLocking(CERTCertDBHandle *handle, SECItem *derCert)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena;
|
|
|
|
SECItem certKey;
|
|
|
|
SECStatus rv;
|
|
|
|
CERTCertificate *cert = NULL;
|
|
|
|
|
|
|
|
/* create a scratch arena */
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* extract the database key from the cert */
|
|
|
|
rv = CERT_KeyFromDERCert(arena, derCert, &certKey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find the certificate */
|
|
|
|
cert = CERT_FindCertByKeyNoLocking(handle, &certKey);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(cert);
|
|
|
|
}
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
/*
|
|
|
|
* The following is bunch of types and code to allow looking up a certificate
|
|
|
|
* by a hash of its subject public key. Because the words "hash" and "key"
|
|
|
|
* are overloaded and thus terribly confusing, I tried to disambiguate things.
|
|
|
|
* - Where I could, I used "digest" instead of "hash" when referring to
|
|
|
|
* hashing of the subject public key. The PLHashTable interfaces and
|
|
|
|
* our own HASH_Foo interfaces had to be left as is, obviously. The latter
|
|
|
|
* should be thought of as "digest" in this case.
|
|
|
|
* - There are three keys in use here -- the subject public key, the key
|
|
|
|
* used to do a lookup in the PLHashTable, and the key used to do a lookup
|
|
|
|
* in the cert database. As the latter is a fairly pervasive interface,
|
|
|
|
* I left it alone. The other two uses I changed to "spk" or "SPK" when
|
|
|
|
* referring to the subject public key, and "index" when referring to the
|
|
|
|
* key into the PLHashTable.
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct SPKDigestInfoStr {
|
|
|
|
PLHashTable *table;
|
|
|
|
PRBool permPopulated;
|
|
|
|
} SPKDigestInfo;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since the key hash information is "hidden" (in a void pointer in the handle)
|
|
|
|
* these macros with the appropriate casts make it easy to get at the parts.
|
|
|
|
*/
|
|
|
|
#define SPK_DIGEST_TABLE(handle) \
|
|
|
|
(((SPKDigestInfo *)(handle->spkDigestInfo))->table)
|
|
|
|
|
2000-04-06 00:24:43 +00:00
|
|
|
/*
|
|
|
|
** Hash allocator ops for the SPKDigest hash table. The rules are:
|
|
|
|
** + The index and value fields are "owned" by the hash table, and are
|
|
|
|
** freed when the table entry is deleted.
|
|
|
|
** + Replacing a value in the table is not allowed, since the caller can't
|
|
|
|
** tell whether the index field was used or not, resulting in a memory
|
|
|
|
** leak. (This is a bug in the PL_Hash routines.
|
|
|
|
*/
|
|
|
|
static void * PR_CALLBACK
|
|
|
|
spkAllocTable(void *pool, PRSize size)
|
|
|
|
{
|
|
|
|
#if defined(XP_MAC)
|
|
|
|
#pragma unused (pool)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return PR_MALLOC(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PR_CALLBACK
|
|
|
|
spkFreeTable(void *pool, void *item)
|
|
|
|
{
|
|
|
|
#if defined(XP_MAC)
|
|
|
|
#pragma unused (pool)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
PR_Free(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NOTE: the key argument here appears to be useless, since the RawAdd
|
|
|
|
* routine in PL_Hash just uses the original anyway.
|
|
|
|
*/
|
|
|
|
static PLHashEntry * PR_CALLBACK
|
|
|
|
spkAllocEntry(void *pool, const void *key)
|
|
|
|
{
|
|
|
|
#if defined(XP_MAC)
|
|
|
|
#pragma unused (pool,key)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return PR_NEW(PLHashEntry);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PR_CALLBACK
|
|
|
|
spkFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
|
|
|
|
{
|
|
|
|
#if defined(XP_MAC)
|
|
|
|
#pragma unused (pool)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SECItem *value = (SECItem *)he->value;
|
|
|
|
|
|
|
|
/* The flag should always be to free the whole entry. Otherwise the
|
|
|
|
* index field gets leaked because the caller can't tell whether
|
|
|
|
* the "new" value (which is the same as the old) was used or not.
|
|
|
|
*/
|
|
|
|
PORT_Assert(flag == HT_FREE_ENTRY);
|
|
|
|
|
|
|
|
/* We always free the value */
|
|
|
|
SECITEM_FreeItem(value, PR_TRUE);
|
|
|
|
|
|
|
|
if (flag == HT_FREE_ENTRY)
|
|
|
|
{
|
|
|
|
/* Comes from BTOA, is this the right free call? */
|
|
|
|
PORT_Free((char *)he->key);
|
|
|
|
PR_Free(he);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static PLHashAllocOps spkHashAllocOps = {
|
|
|
|
spkAllocTable, spkFreeTable,
|
|
|
|
spkAllocEntry, spkFreeEntry
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2000-03-31 20:13:40 +00:00
|
|
|
/*
|
|
|
|
* Create the key hash lookup table. Note that the table, and the
|
|
|
|
* structure which holds it and a little more information, is never freed.
|
|
|
|
* This is because the temporary database is never actually closed out,
|
2000-04-06 00:24:43 +00:00
|
|
|
* so there is no safe/obvious place to free the whole thing.
|
2000-03-31 20:13:40 +00:00
|
|
|
*
|
|
|
|
* The database must be locked already.
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
InitDBspkDigestInfo(CERTCertDBHandle *handle)
|
|
|
|
{
|
|
|
|
SPKDigestInfo *spkDigestInfo;
|
|
|
|
PLHashTable *table;
|
|
|
|
|
|
|
|
PORT_Assert(handle != NULL);
|
|
|
|
PORT_Assert(handle->spkDigestInfo == NULL);
|
|
|
|
|
|
|
|
spkDigestInfo = PORT_ZAlloc(sizeof(SPKDigestInfo));
|
|
|
|
if ( spkDigestInfo == NULL ) {
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
table = PL_NewHashTable(128, PL_HashString, PL_CompareStrings,
|
|
|
|
(PLHashComparator) SECITEM_ItemsAreEqual,
|
2000-04-06 00:24:43 +00:00
|
|
|
&spkHashAllocOps, NULL);
|
2000-03-31 20:13:40 +00:00
|
|
|
if ( table == NULL ) {
|
|
|
|
PORT_Free(spkDigestInfo);
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
spkDigestInfo->table = table;
|
|
|
|
handle->spkDigestInfo = spkDigestInfo;
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
2001-01-05 01:38:26 +00:00
|
|
|
static const SECHashObject *
|
2000-03-31 20:13:40 +00:00
|
|
|
OidTagToRawDigestObject(SECOidTag digestAlg)
|
|
|
|
{
|
2001-01-05 01:38:26 +00:00
|
|
|
const SECHashObject *rawDigestObject;
|
2000-03-31 20:13:40 +00:00
|
|
|
|
|
|
|
switch (digestAlg) {
|
|
|
|
case SEC_OID_MD2:
|
|
|
|
rawDigestObject = &SECRawHashObjects[HASH_AlgMD2];
|
|
|
|
break;
|
|
|
|
case SEC_OID_MD5:
|
|
|
|
rawDigestObject = &SECRawHashObjects[HASH_AlgMD5];
|
|
|
|
break;
|
|
|
|
case SEC_OID_SHA1:
|
|
|
|
rawDigestObject = &SECRawHashObjects[HASH_AlgSHA1];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
|
|
|
|
rawDigestObject = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return(rawDigestObject);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Digest the cert's subject public key using the specified algorithm.
|
|
|
|
* The necessary storage for the digest data is allocated. If "fill" is
|
|
|
|
* non-null, the data is put there, otherwise a SECItem is allocated.
|
|
|
|
* Allocation from "arena" if it is non-null, heap otherwise. Any problem
|
|
|
|
* results in a NULL being returned (and an appropriate error set).
|
|
|
|
*/
|
|
|
|
SECItem *
|
|
|
|
CERT_SPKDigestValueForCert(PRArenaPool *arena, CERTCertificate *cert,
|
|
|
|
SECOidTag digestAlg, SECItem *fill)
|
|
|
|
{
|
2001-01-05 01:38:26 +00:00
|
|
|
const SECHashObject *digestObject;
|
2000-03-31 20:13:40 +00:00
|
|
|
void *digestContext;
|
|
|
|
SECItem *result = NULL;
|
|
|
|
void *mark = NULL;
|
|
|
|
SECItem spk;
|
|
|
|
|
|
|
|
if ( arena != NULL ) {
|
|
|
|
mark = PORT_ArenaMark(arena);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This can end up being called before PKCS #11 is initialized,
|
|
|
|
* so we have to use the raw digest functions.
|
|
|
|
*/
|
|
|
|
digestObject = OidTagToRawDigestObject(digestAlg);
|
|
|
|
if ( digestObject == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = SECITEM_AllocItem(arena, fill, digestObject->length);
|
|
|
|
if ( result == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy just the length and data pointer (nothing needs to be freed)
|
|
|
|
* of the subject public key so we can convert the length from bits
|
|
|
|
* to bytes, which is what the digest function expects.
|
|
|
|
*/
|
|
|
|
spk = cert->subjectPublicKeyInfo.subjectPublicKey;
|
|
|
|
DER_ConvertBitString(&spk);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now digest the value, using the specified algorithm.
|
|
|
|
*/
|
|
|
|
digestContext = digestObject->create();
|
|
|
|
if ( digestContext == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
digestObject->begin(digestContext);
|
|
|
|
digestObject->update(digestContext, spk.data, spk.len);
|
|
|
|
digestObject->end(digestContext, result->data, &(result->len), result->len);
|
|
|
|
digestObject->destroy(digestContext, PR_TRUE);
|
|
|
|
|
|
|
|
if ( arena != NULL ) {
|
|
|
|
PORT_ArenaUnmark(arena, mark);
|
|
|
|
}
|
|
|
|
return(result);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena != NULL ) {
|
|
|
|
PORT_ArenaRelease(arena, mark);
|
|
|
|
} else {
|
|
|
|
if ( result != NULL ) {
|
|
|
|
SECITEM_FreeItem(result, (fill == NULL) ? PR_TRUE : PR_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the index for the spk digest lookup table for "spkDigest".
|
|
|
|
*
|
|
|
|
* Caller is responsible for freeing the returned string.
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
spkDigestIndexFromDigest(SECItem *spkDigest)
|
|
|
|
{
|
|
|
|
return BTOA_ConvertItemToAscii(spkDigest);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the index for the spk digest lookup table for this certificate,
|
|
|
|
* based on the specified digest algorithm.
|
|
|
|
*
|
|
|
|
* Caller is responsible for freeing the returned string.
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
spkDigestIndexFromCert(CERTCertificate *cert, SECOidTag digestAlg)
|
|
|
|
{
|
|
|
|
SECItem *spkDigest;
|
|
|
|
char *index;
|
|
|
|
|
|
|
|
spkDigest = CERT_SPKDigestValueForCert(NULL, cert, digestAlg, NULL);
|
|
|
|
if ( spkDigest == NULL )
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
index = spkDigestIndexFromDigest(spkDigest);
|
|
|
|
|
|
|
|
SECITEM_FreeItem(spkDigest, PR_TRUE);
|
|
|
|
|
|
|
|
return(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove the spk digest for the given cert from the spk digest table,
|
|
|
|
* based on the given digest algorithm.
|
|
|
|
*
|
|
|
|
* The database must be locked already.
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
RemoveCertFromSPKDigestTableForAlg(CERTCertDBHandle *handle,
|
|
|
|
CERTCertificate *cert, SECOidTag digestAlg)
|
|
|
|
{
|
|
|
|
SECStatus rv = SECSuccess;
|
|
|
|
char *index = NULL;
|
|
|
|
PLHashTable *table;
|
|
|
|
|
|
|
|
/* Expect to only be called if there is a table to work with. */
|
|
|
|
PORT_Assert(handle->spkDigestInfo != NULL);
|
|
|
|
|
|
|
|
table = SPK_DIGEST_TABLE(handle);
|
|
|
|
PORT_Assert(table != NULL);
|
|
|
|
|
|
|
|
index = spkDigestIndexFromCert(cert, digestAlg);
|
|
|
|
if ( index == NULL ) {
|
|
|
|
rv = SECFailure;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( PL_HashTableRemove(table, index) != PR_TRUE ) {
|
2000-04-06 00:24:43 +00:00
|
|
|
/* not found means nothing to remove, which is fine */
|
2000-03-31 20:13:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
if ( index != NULL ) {
|
|
|
|
PORT_Free(index);
|
|
|
|
}
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove the spk digests for the given cert from the spk digest table,
|
|
|
|
* for all known digest algorithms.
|
|
|
|
*
|
|
|
|
* The database must be locked already.
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
RemoveCertFromSPKDigestTable(CERTCertDBHandle *handle, CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If no certs have been added yet, then nothing to do.
|
|
|
|
*/
|
|
|
|
if ( handle->spkDigestInfo == NULL ) {
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) RemoveCertFromSPKDigestTableForAlg(handle, cert, SEC_OID_MD2);
|
|
|
|
(void) RemoveCertFromSPKDigestTableForAlg(handle, cert, SEC_OID_MD5);
|
|
|
|
return RemoveCertFromSPKDigestTableForAlg(handle, cert, SEC_OID_SHA1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the spk digest for the given cert to the spk digest table,
|
|
|
|
* based on the given digest algorithm.
|
|
|
|
*
|
|
|
|
* If a cert for the same spk digest is already in the table, choose whichever
|
|
|
|
* cert is "newer". (The other cert cannot be found via spk digest.)
|
|
|
|
*
|
|
|
|
* The database must be locked already.
|
|
|
|
*
|
|
|
|
* XXX Note that this implementation results in leaking the index value.
|
|
|
|
* Fixing that did not seem worth the trouble, given we will only leak
|
|
|
|
* once per cert. This whole thing should be done differently in the
|
|
|
|
* new rewrite (Stan), and then the problem will go away.
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
AddCertToSPKDigestTableForAlg(CERTCertDBHandle *handle, CERTCertificate *cert,
|
|
|
|
SECItem *certDBKey, SECOidTag digestAlg)
|
|
|
|
{
|
|
|
|
SECStatus rv = SECFailure;
|
2000-04-06 00:24:43 +00:00
|
|
|
SECItem *oldCertDBKey;
|
2000-03-31 20:13:40 +00:00
|
|
|
PRBool addit = PR_TRUE;
|
|
|
|
CERTCertificate *oldCert = NULL;
|
|
|
|
char *index = NULL;
|
|
|
|
PLHashTable *table;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* After running some testing doing key hash lookups (like using OCSP),
|
|
|
|
* if these are never hit, they can probably be removed.
|
|
|
|
*/
|
|
|
|
PORT_Assert(handle != NULL);
|
|
|
|
PORT_Assert(handle == cert->dbhandle);
|
|
|
|
PORT_Assert(handle->spkDigestInfo != NULL);
|
|
|
|
PORT_Assert((certDBKey == &cert->certKey)
|
|
|
|
|| (SECITEM_CompareItem(certDBKey,
|
|
|
|
&cert->certKey) == SECEqual));
|
|
|
|
|
|
|
|
table = SPK_DIGEST_TABLE(handle);
|
|
|
|
PORT_Assert(table != NULL);
|
|
|
|
|
|
|
|
index = spkDigestIndexFromCert(cert, digestAlg);
|
|
|
|
if ( index == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if this cert's spk digest is already in the table.
|
|
|
|
*/
|
|
|
|
oldCertDBKey = PL_HashTableLookup(table, index);
|
|
|
|
if ( oldCertDBKey != NULL ) {
|
|
|
|
/*
|
|
|
|
* The spk digest *is* already in the table. We need to find that
|
|
|
|
* cert and see -- if it is the same, then we can just leave as is.
|
|
|
|
* Otherwise we have to choose which cert we want represented;
|
|
|
|
* in that case the best plan I can think of is to hang onto the
|
|
|
|
* most recent one.
|
|
|
|
*/
|
|
|
|
oldCert = CERT_FindCertByKey(handle, oldCertDBKey);
|
|
|
|
if ( oldCert != NULL ) {
|
|
|
|
if ( cert == oldCert ) {
|
|
|
|
/* They are the same cert, so we are done. */
|
|
|
|
addit = PR_FALSE;
|
|
|
|
} else if ( CERT_IsNewer(cert, oldCert) ) {
|
|
|
|
if ( PL_HashTableRemove(table, index) != PR_TRUE ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* oldCert is "newer", so we are done. */
|
|
|
|
addit = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( addit ) {
|
|
|
|
certDBKey = SECITEM_DupItem(certDBKey);
|
|
|
|
if ( certDBKey == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
if ( PL_HashTableAdd(table, index, certDBKey) == NULL ) {
|
|
|
|
SECITEM_FreeItem(certDBKey, PR_TRUE);
|
|
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
index = NULL; /* don't want to free it */
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = SECSuccess;
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( index != NULL ) {
|
|
|
|
PORT_Free(index);
|
|
|
|
}
|
|
|
|
if ( oldCert != NULL ) {
|
|
|
|
CERT_DestroyCertificate(oldCert);
|
|
|
|
}
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the spk digest for the given cert to the spk digest table,
|
|
|
|
* for all known digest algorithms.
|
|
|
|
*
|
|
|
|
* The database must be locked already, and the digest table already created.
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
AddCertToSPKDigestTableForAllAlgs(CERTCertDBHandle *handle,
|
|
|
|
CERTCertificate *cert, SECItem *certDBKey)
|
|
|
|
{
|
|
|
|
(void) AddCertToSPKDigestTableForAlg(handle, cert, certDBKey, SEC_OID_MD2);
|
|
|
|
(void) AddCertToSPKDigestTableForAlg(handle, cert, certDBKey, SEC_OID_MD5);
|
|
|
|
return AddCertToSPKDigestTableForAlg(handle, cert, certDBKey, SEC_OID_SHA1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the spk digests for the given cert to the spk digest table,
|
|
|
|
* for all known digest algorithms. (This function is called when a
|
|
|
|
* new cert is added to the temporary database.)
|
|
|
|
*
|
|
|
|
* If the spk digest table does not yet exist, create it.
|
|
|
|
*
|
|
|
|
* In an ideal world, we would not hardwire the digest algorithms.
|
|
|
|
* But it is the case that we do not currently support any digest
|
|
|
|
* algorithms outside of these three. In the newer, cleaned-up world,
|
|
|
|
* this may be done differently.
|
|
|
|
*
|
|
|
|
* The database must be locked already.
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
AddCertToSPKDigestTable(CERTCertDBHandle *handle, CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
if ( handle->spkDigestInfo == NULL ) {
|
|
|
|
rv = InitDBspkDigestInfo(handle);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return AddCertToSPKDigestTableForAllAlgs(handle, cert, &cert->certKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the spk digest for the given cert to the spk digest table,
|
|
|
|
* for all known digest algorithms. This function is called while
|
|
|
|
* traversing all of the certs in the permanent database -- since
|
|
|
|
* that imposes some constraints on its arguments this routine is a
|
|
|
|
* simple cover for the "real" interface.
|
|
|
|
*
|
|
|
|
* The database must be locked already, and the digest table already created.
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
AddCertToSPKDigestTableInTraversal(CERTCertificate *cert, SECItem *certDBKey,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
CERTCertDBHandle *handle = data;
|
|
|
|
|
|
|
|
return AddCertToSPKDigestTableForAllAlgs(handle, cert, certDBKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the spk digests for the all permanent certs to the spk digest table,
|
|
|
|
* for all known digest algorithms.
|
|
|
|
*
|
|
|
|
* This locks the database, and then checks to make sure that the work
|
|
|
|
* actually needs to get done.
|
|
|
|
*
|
|
|
|
* If the spk digest table does not yet exist, it is created.
|
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
PopulateSPKDigestTable(CERTCertDBHandle *handle)
|
|
|
|
{
|
|
|
|
SPKDigestInfo *spkDigestInfo;
|
|
|
|
SECStatus rv = SECSuccess;
|
|
|
|
|
|
|
|
CERT_LockDB(handle);
|
|
|
|
|
|
|
|
spkDigestInfo = handle->spkDigestInfo;
|
|
|
|
if ( spkDigestInfo == NULL ) {
|
|
|
|
rv = InitDBspkDigestInfo(handle);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
spkDigestInfo = handle->spkDigestInfo;
|
|
|
|
PORT_Assert(spkDigestInfo != NULL);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Check to see if someone already did it; it is important to do
|
|
|
|
* this after getting the lock.
|
|
|
|
*/
|
|
|
|
if ( spkDigestInfo->permPopulated == PR_TRUE ) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = TraversePermCertsNoLocking(handle, AddCertToSPKDigestTableInTraversal,
|
|
|
|
handle);
|
|
|
|
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
spkDigestInfo->permPopulated = PR_TRUE;
|
|
|
|
|
|
|
|
done:
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup a certificate by a digest of a subject public key. If it is
|
|
|
|
* found, it is returned (and must then be destroyed by the caller).
|
|
|
|
* NULL is returned otherwise -- if there was a problem performing the
|
|
|
|
* lookup, an appropriate error is set (e.g. SEC_ERROR_NO_MEMORY);
|
|
|
|
* if the cert simply was not found, the error is SEC_ERROR_UNKNOWN_CERT.
|
|
|
|
*
|
|
|
|
* If the lookup table has not yet been created or populated, do that first.
|
|
|
|
*/
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertBySPKDigest(CERTCertDBHandle *handle, SECItem *spkDigest)
|
|
|
|
{
|
|
|
|
SPKDigestInfo *spkDigestInfo;
|
|
|
|
char *index = NULL;
|
|
|
|
SECItem *certDBKey;
|
|
|
|
CERTCertificate *cert = NULL;
|
|
|
|
|
|
|
|
PORT_Assert(handle != NULL);
|
|
|
|
spkDigestInfo = handle->spkDigestInfo;
|
|
|
|
|
|
|
|
if ( spkDigestInfo == NULL || spkDigestInfo->permPopulated != PR_TRUE ) {
|
|
|
|
if ( PopulateSPKDigestTable(handle) != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
index = spkDigestIndexFromDigest(spkDigest);
|
|
|
|
if ( index == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
certDBKey = PL_HashTableLookup(SPK_DIGEST_TABLE(handle), index);
|
|
|
|
if ( certDBKey != NULL ) {
|
|
|
|
cert = CERT_FindCertByKey(handle, certDBKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( cert == NULL ) {
|
|
|
|
PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
|
|
|
|
}
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( index != NULL ) {
|
|
|
|
PORT_Free(index);
|
|
|
|
}
|
|
|
|
return(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
DestroyCertificate(CERTCertificate *cert, PRBool lockdb)
|
|
|
|
{
|
|
|
|
int refCount;
|
|
|
|
CERTCertDBHandle *handle;
|
|
|
|
|
|
|
|
if ( cert ) {
|
|
|
|
|
|
|
|
handle = cert->dbhandle;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* handle may be NULL, for example if the cert was created with
|
|
|
|
* CERT_DecodeDERCertificate.
|
|
|
|
*/
|
|
|
|
if ( lockdb && handle ) {
|
|
|
|
CERT_LockDB(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_LockCertRefCount(cert);
|
|
|
|
PORT_Assert(cert->referenceCount > 0);
|
|
|
|
refCount = --cert->referenceCount;
|
|
|
|
CERT_UnlockCertRefCount(cert);
|
|
|
|
|
|
|
|
if ( ( refCount == 0 ) && !cert->keepSession ) {
|
|
|
|
certDBEntryCert *entry = cert->dbEntry;
|
|
|
|
PRArenaPool * arena = cert->arena;
|
|
|
|
|
|
|
|
if ( cert->istemp ) {
|
|
|
|
CERT_DeleteTempCertificate(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( entry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* zero cert before freeing. Any stale references to this cert
|
|
|
|
* after this point will probably cause an exception. */
|
|
|
|
PORT_Memset(cert, 0, sizeof *cert);
|
|
|
|
|
|
|
|
cert = NULL;
|
|
|
|
|
|
|
|
/* free the arena that contains the cert. */
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
if ( lockdb && handle ) {
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CERT_DestroyCertificate(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
DestroyCertificate(cert, PR_TRUE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CERT_DestroyCertificateNoLocking(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
DestroyCertificate(cert, PR_FALSE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* cache a CRL from the permanent database into the temporary database
|
|
|
|
*/
|
|
|
|
CERTSignedCrl *
|
|
|
|
SEC_AddPermCrlToTemp(CERTCertDBHandle *handle, certDBEntryRevocation *entry)
|
|
|
|
{
|
|
|
|
CERTSignedCrl *crl = NULL;
|
|
|
|
DBT key;
|
|
|
|
DBT data;
|
|
|
|
int status;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECItem keyitem;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
crl = CERT_DecodeDERCrl(NULL, &entry->derCrl,
|
|
|
|
entry->common.type == certDBEntryTypeRevocation ?
|
|
|
|
SEC_CRL_TYPE : SEC_KRL_TYPE);
|
|
|
|
|
|
|
|
if ( crl == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* only save pointer to cert in database */
|
|
|
|
data.data = &crl;
|
|
|
|
data.size = sizeof(crl);
|
|
|
|
|
|
|
|
|
|
|
|
rv = EncodeDBGenericKey(&(crl->crl.derName), arena,
|
|
|
|
&keyitem, entry->common.type);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry->url) {
|
|
|
|
int nnlen = PORT_Strlen(entry->url) + 1;
|
|
|
|
crl->url = (char *)PORT_ArenaAlloc(crl->arena, nnlen);
|
|
|
|
if ( !crl->url ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(crl->url, entry->url, nnlen);
|
|
|
|
} else {
|
|
|
|
crl->url = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
key.data = keyitem.data;
|
|
|
|
key.size = keyitem.len;
|
|
|
|
|
|
|
|
/* enter into main db */
|
|
|
|
status = certdb_Put(handle->tempCertDB, &key, &data, R_NOOVERWRITE);
|
|
|
|
if ( status ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
crl->istemp = PR_TRUE;
|
|
|
|
crl->isperm = PR_TRUE;
|
|
|
|
crl->dbhandle = handle;
|
|
|
|
crl->dbEntry = entry;
|
|
|
|
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
|
|
|
|
crl->keep = PR_TRUE;
|
|
|
|
return(crl);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( crl ) {
|
|
|
|
SEC_DestroyCrl(crl);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
SEC_DeleteTempCrl(CERTSignedCrl *crl)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
DBT nameKey;
|
|
|
|
CERTCertDBHandle *handle;
|
|
|
|
SECItem keyitem;
|
|
|
|
PRArenaPool *arena;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
handle = crl->dbhandle;
|
|
|
|
|
|
|
|
if ( !crl->istemp ) {
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBGenericKey
|
|
|
|
(&crl->crl.derName, arena, &keyitem, crl->dbEntry->common.type);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
nameKey.data = keyitem.data;
|
|
|
|
nameKey.size = keyitem.len;
|
|
|
|
|
|
|
|
/* delete the cert */
|
|
|
|
ret = certdb_Del(handle->tempCertDB, &nameKey, 0);
|
|
|
|
if ( ret ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
crl->istemp = PR_FALSE;
|
|
|
|
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup a CRL in the databases. We mirror the same fast caching data base
|
|
|
|
* caching stuff used by certificates....?
|
|
|
|
*/
|
|
|
|
CERTSignedCrl *
|
|
|
|
SEC_FindCrlByKey(CERTCertDBHandle *handle, SECItem *crlKey, int type)
|
|
|
|
{
|
|
|
|
DBT tmpdata;
|
|
|
|
int ret;
|
|
|
|
SECItem keyitem;
|
|
|
|
DBT key;
|
|
|
|
SECStatus rv;
|
|
|
|
CERTSignedCrl *crl = NULL;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
certDBEntryRevocation *entry;
|
|
|
|
certDBEntryType crlType = (type == SEC_CRL_TYPE) ?
|
|
|
|
certDBEntryTypeRevocation : certDBEntryTypeKeyRevocation;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = EncodeDBGenericKey(crlKey, arena, &keyitem, crlType);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
key.data = keyitem.data;
|
|
|
|
key.size = keyitem.len;
|
|
|
|
|
|
|
|
/* lookup in the temporary database */
|
|
|
|
ret = certdb_Get( handle->tempCertDB, &key, &tmpdata, 0 );
|
|
|
|
|
|
|
|
/* error accessing the database */
|
|
|
|
if ( ret < 0 ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ret == 0 ) { /* found in temp database */
|
|
|
|
if ( tmpdata.size != sizeof(CERTSignedCrl *) ) {
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Memcpy(&crl, tmpdata.data, tmpdata.size);
|
|
|
|
crl->referenceCount++;
|
|
|
|
} else { /* not found in temporary database */
|
|
|
|
|
|
|
|
/* find in perm database */
|
|
|
|
entry = ReadDBCrlEntry(handle, crlKey, crlType);
|
|
|
|
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
crl = SEC_AddPermCrlToTemp(handle, entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(crl);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CERTSignedCrl *
|
|
|
|
SEC_FindCrlByName(CERTCertDBHandle *handle, SECItem *crlKey, int type)
|
|
|
|
{
|
|
|
|
return SEC_FindCrlByKey(handle,crlKey,type);
|
|
|
|
}
|
|
|
|
|
|
|
|
CERTSignedCrl *
|
|
|
|
SEC_FindCrlByDERCert(CERTCertDBHandle *handle, SECItem *derCrl, int type)
|
|
|
|
{
|
|
|
|
PRArenaPool *arena;
|
|
|
|
SECItem crlKey;
|
|
|
|
SECStatus rv;
|
|
|
|
CERTSignedCrl *crl = NULL;
|
|
|
|
|
|
|
|
/* create a scratch arena */
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* extract the database key from the cert */
|
|
|
|
rv = CERT_KeyFromDERCrl(arena, derCrl, &crlKey);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find the crl */
|
|
|
|
crl = SEC_FindCrlByKey(handle, &crlKey, type);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
return(crl);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
SEC_DestroyCrl(CERTSignedCrl *crl)
|
|
|
|
{
|
|
|
|
if (crl) {
|
|
|
|
if (crl->referenceCount-- <= 1) {
|
|
|
|
if (!crl->keep) {
|
|
|
|
SEC_DeleteTempCrl(crl);
|
|
|
|
if (crl->dbEntry) {
|
|
|
|
DestroyDBEntry((certDBEntry *)crl->dbEntry);
|
|
|
|
}
|
|
|
|
PORT_FreeArena(crl->arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
CERTSignedCrl *
|
|
|
|
cert_DBInsertCRL (CERTCertDBHandle *handle, char *url,
|
|
|
|
CERTSignedCrl *newCrl, SECItem *derCrl, int type)
|
|
|
|
{
|
|
|
|
CERTSignedCrl *oldCrl = NULL, *crl = NULL;
|
|
|
|
certDBEntryRevocation *entry = NULL;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECCertTimeValidity validity;
|
|
|
|
certDBEntryType crlType = (type == SEC_CRL_TYPE) ?
|
|
|
|
certDBEntryTypeRevocation : certDBEntryTypeKeyRevocation;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if (arena == NULL) goto done;
|
|
|
|
|
|
|
|
validity = SEC_CheckCrlTimes(&newCrl->crl,PR_Now());
|
|
|
|
if ( validity == secCertTimeExpired) {
|
|
|
|
if (type == SEC_CRL_TYPE) {
|
|
|
|
PORT_SetError(SEC_ERROR_CRL_EXPIRED);
|
|
|
|
} else {
|
|
|
|
PORT_SetError(SEC_ERROR_KRL_EXPIRED);
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
} else if (validity == secCertTimeNotValidYet) {
|
|
|
|
if (type == SEC_CRL_TYPE) {
|
|
|
|
PORT_SetError(SEC_ERROR_CRL_NOT_YET_VALID);
|
|
|
|
} else {
|
|
|
|
PORT_SetError(SEC_ERROR_KRL_NOT_YET_VALID);
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
oldCrl = SEC_FindCrlByKey(handle, &newCrl->crl.derName, type);
|
|
|
|
|
|
|
|
/* if there is an old crl, make sure the one we are installing
|
|
|
|
* is newer. If not, exit out, otherwise delete the old crl.
|
|
|
|
*/
|
|
|
|
if (oldCrl != NULL) {
|
|
|
|
if (!SEC_CrlIsNewer(&newCrl->crl,&oldCrl->crl)) {
|
|
|
|
|
|
|
|
if (type == SEC_CRL_TYPE) {
|
|
|
|
PORT_SetError(SEC_ERROR_OLD_CRL);
|
|
|
|
} else {
|
|
|
|
PORT_SetError(SEC_ERROR_OLD_KRL);
|
|
|
|
}
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((SECITEM_CompareItem(&newCrl->crl.derName,
|
|
|
|
&oldCrl->crl.derName) != SECEqual) &&
|
|
|
|
(type == SEC_KRL_TYPE) ) {
|
|
|
|
|
|
|
|
PORT_SetError(SEC_ERROR_CKL_CONFLICT);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we have a url in the database, use that one */
|
|
|
|
if (oldCrl->url) {
|
|
|
|
int nnlen = PORT_Strlen(oldCrl->url) + 1;
|
|
|
|
url = (char *)PORT_ArenaAlloc(arena, nnlen);
|
|
|
|
if ( url != NULL ) {
|
|
|
|
PORT_Memcpy(url, oldCrl->url, nnlen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* really destroy this crl */
|
|
|
|
/* first drum it out of the permanment Data base */
|
|
|
|
SEC_DeletePermCRL(oldCrl);
|
|
|
|
/* then get rid of our reference to it... */
|
|
|
|
SEC_DestroyCrl(oldCrl);
|
|
|
|
oldCrl = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write the new entry into the data base */
|
|
|
|
entry = NewDBCrlEntry(derCrl, url, crlType, 0);
|
|
|
|
if (entry == NULL) goto done;
|
|
|
|
|
|
|
|
rv = WriteDBCrlEntry(handle, entry);
|
|
|
|
if (rv != SECSuccess) goto done;
|
|
|
|
|
|
|
|
crl = SEC_AddPermCrlToTemp(handle, entry);
|
|
|
|
if (crl) entry = NULL; /*crl->dbEntry now points to entry data */
|
|
|
|
crl->isperm = PR_TRUE;
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (entry) DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
if (arena) PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
if (oldCrl) SEC_DestroyCrl(oldCrl);
|
|
|
|
|
|
|
|
return crl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create a new CRL from DER material.
|
|
|
|
*
|
|
|
|
* The signature on this CRL must be checked before you
|
|
|
|
* load it. ???
|
|
|
|
*/
|
|
|
|
CERTSignedCrl *
|
|
|
|
SEC_NewCrl(CERTCertDBHandle *handle, char *url, SECItem *derCrl, int type)
|
|
|
|
{
|
|
|
|
CERTSignedCrl *newCrl = NULL, *crl = NULL;
|
|
|
|
|
|
|
|
/* make this decode dates! */
|
|
|
|
newCrl = CERT_DecodeDERCrl(NULL, derCrl, type);
|
|
|
|
if (newCrl == NULL) {
|
|
|
|
if (type == SEC_CRL_TYPE) {
|
|
|
|
PORT_SetError(SEC_ERROR_CRL_INVALID);
|
|
|
|
} else {
|
|
|
|
PORT_SetError(SEC_ERROR_KRL_INVALID);
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
crl = cert_DBInsertCRL (handle, url, newCrl, derCrl, type);
|
|
|
|
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (newCrl) PORT_FreeArena(newCrl->arena, PR_FALSE);
|
|
|
|
|
|
|
|
return crl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* replace the existing URL in the data base with a new one
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
SEC_CrlReplaceUrl(CERTSignedCrl *crl,char *url) {
|
|
|
|
SECStatus rv = SECFailure;
|
|
|
|
certDBEntryRevocation *entry = NULL;
|
|
|
|
int nnlen=0;
|
|
|
|
|
|
|
|
SEC_DeletePermCRL(crl);
|
|
|
|
|
|
|
|
/* Write the new entry into the data base */
|
|
|
|
entry = NewDBCrlEntry(&crl->dbEntry->derCrl, url, crl->dbEntry->common.type, 0);
|
|
|
|
if (entry == NULL) goto done;
|
|
|
|
|
|
|
|
rv = WriteDBCrlEntry(crl->dbhandle, entry);
|
|
|
|
if (rv != SECSuccess) goto done;
|
|
|
|
|
|
|
|
if (url) {
|
|
|
|
nnlen = PORT_Strlen(url) + 1;
|
|
|
|
crl->url = (char *)PORT_ArenaAlloc(crl->arena, nnlen);
|
|
|
|
if ( !crl->url ) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(crl->url, url, nnlen);
|
|
|
|
} else {
|
|
|
|
crl->url = NULL;
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* collect a linked list of CRL's
|
|
|
|
*/
|
|
|
|
static SECStatus CollectCrls(SECItem *dbdata, SECItem *dbkey,
|
|
|
|
certDBEntryType type, void *data) {
|
|
|
|
SECStatus rv;
|
|
|
|
certDBEntryRevocation entry;
|
|
|
|
SECItem entryitem;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
CERTCrlHeadNode *head;
|
|
|
|
CERTCrlNode *new_node;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
head = (CERTCrlHeadNode *)data;
|
|
|
|
entry.common.version = (unsigned int)dbdata->data[0];
|
|
|
|
entry.common.type = (certDBEntryType)dbdata->data[1];
|
|
|
|
entry.common.flags = (unsigned int)dbdata->data[2];
|
|
|
|
entry.common.arena = arena;
|
|
|
|
|
|
|
|
entryitem.len = dbdata->len - SEC_DB_ENTRY_HEADER_LEN;
|
|
|
|
entryitem.data = &dbdata->data[SEC_DB_ENTRY_HEADER_LEN];
|
|
|
|
|
|
|
|
rv = DecodeDBCrlEntry(&entry, &entryitem);
|
|
|
|
if (rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_node = (CERTCrlNode *)PORT_ArenaAlloc(head->arena, sizeof(CERTCrlNode));
|
|
|
|
if (new_node == NULL) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_node->type = (entry.common.type == certDBEntryTypeRevocation) ?
|
|
|
|
SEC_CRL_TYPE : SEC_KRL_TYPE;
|
|
|
|
new_node->crl=CERT_DecodeDERCrl(head->arena,&entry.derCrl,new_node->type);
|
|
|
|
|
|
|
|
if (entry.url) {
|
|
|
|
int nnlen = PORT_Strlen(entry.url) + 1;
|
|
|
|
new_node->crl->url = (char *)PORT_ArenaAlloc(head->arena, nnlen);
|
|
|
|
if ( !new_node->crl->url ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
PORT_Memcpy(new_node->crl->url, entry.url, nnlen);
|
|
|
|
} else {
|
|
|
|
new_node->crl->url = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
new_node->next = NULL;
|
|
|
|
if (head->last) {
|
|
|
|
head->last->next = new_node;
|
|
|
|
head->last = new_node;
|
|
|
|
} else {
|
|
|
|
head->first = head->last = new_node;
|
|
|
|
}
|
|
|
|
return (SECSuccess);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
}
|
|
|
|
return(SECFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
SEC_LookupCrls(CERTCertDBHandle *handle, CERTCrlHeadNode **nodes, int type)
|
|
|
|
{
|
|
|
|
CERTCrlHeadNode *head;
|
|
|
|
PRArenaPool *arena = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
*nodes = NULL;
|
|
|
|
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if ( arena == NULL ) {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* build a head structure */
|
|
|
|
head = (CERTCrlHeadNode *)PORT_ArenaAlloc(arena, sizeof(CERTCrlHeadNode));
|
|
|
|
head->arena = arena;
|
|
|
|
head->first = NULL;
|
|
|
|
head->last = NULL;
|
|
|
|
head->dbhandle = handle;
|
|
|
|
|
|
|
|
/* Look up the proper crl types */
|
|
|
|
*nodes = head;
|
|
|
|
|
|
|
|
CERT_LockDB(handle);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case SEC_CRL_TYPE:
|
|
|
|
rv = SEC_TraverseDBEntries(handle, certDBEntryTypeRevocation,
|
|
|
|
CollectCrls, (void *)head);
|
|
|
|
break;
|
|
|
|
case SEC_KRL_TYPE:
|
|
|
|
rv = SEC_TraverseDBEntries(handle, certDBEntryTypeKeyRevocation,
|
|
|
|
CollectCrls, (void *)head);
|
|
|
|
break;
|
|
|
|
case -1:
|
|
|
|
rv = SEC_TraverseDBEntries(handle, certDBEntryTypeKeyRevocation,
|
|
|
|
CollectCrls, (void *)head);
|
|
|
|
if (rv != SECSuccess) break;
|
|
|
|
rv = SEC_TraverseDBEntries(handle, certDBEntryTypeRevocation,
|
|
|
|
CollectCrls, (void *)head);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rv = SECFailure;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_UnlockDB(handle);
|
|
|
|
|
|
|
|
if (rv != SECSuccess) {
|
|
|
|
if ( arena ) {
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
|
|
*nodes = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
SEC_DeletePermCRL(CERTSignedCrl *crl) {
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
if (crl == NULL) {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DeleteDBCrlEntry(crl->dbhandle, &crl->crl.derName,
|
|
|
|
crl->dbEntry->common.type);
|
|
|
|
if (rv != SECSuccess) goto done;
|
|
|
|
|
|
|
|
/* now force it to be freed when all the reference counts go */
|
|
|
|
crl->keep = PR_FALSE;
|
|
|
|
/* force it out of the temporary data base */
|
|
|
|
SEC_DeleteTempCrl(crl);
|
|
|
|
|
|
|
|
done:
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* find a cert by email address
|
|
|
|
*
|
|
|
|
* pick one that is a valid recipient, meaning that it is an encryption
|
|
|
|
* cert.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static CERTCertificate*
|
|
|
|
find_smime_recipient_cert(CERTCertDBHandle* handle, const char* email_addr)
|
|
|
|
{
|
|
|
|
CERTCertificate* cert = NULL;
|
|
|
|
CERTCertList* certList = NULL;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
certList = CERT_CreateEmailAddrCertList(NULL, handle, (char*)email_addr,
|
|
|
|
PR_Now(), PR_TRUE);
|
|
|
|
if (certList == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = CERT_FilterCertListByUsage(certList, certUsageEmailRecipient,
|
|
|
|
PR_FALSE);
|
|
|
|
|
|
|
|
if (!CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) {
|
|
|
|
cert = CERT_DupCertificate(CERT_LIST_HEAD(certList)->cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_DestroyCertList(certList);
|
|
|
|
|
|
|
|
return cert; /* cert may point to a cert or may be NULL */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function has the logic that decides if another person's cert and
|
|
|
|
* email profile from an S/MIME message should be saved. It can deal with
|
|
|
|
* the case when there is no profile.
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
CERT_SaveSMimeProfile(CERTCertificate *cert, SECItem *emailProfile,
|
|
|
|
SECItem *profileTime)
|
|
|
|
{
|
|
|
|
certDBEntrySMime *entry = NULL, *oldentry = NULL;
|
|
|
|
int64 oldtime;
|
|
|
|
int64 newtime;
|
|
|
|
SECStatus rv;
|
|
|
|
CERTCertificate *oldcert = NULL;
|
|
|
|
PRBool saveit;
|
|
|
|
CERTCertTrust trust;
|
|
|
|
CERTCertTrust tmptrust;
|
|
|
|
char *emailAddr;
|
|
|
|
|
|
|
|
emailAddr = cert->emailAddr;
|
|
|
|
|
|
|
|
PORT_Assert(emailAddr);
|
|
|
|
if ( emailAddr == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
saveit = PR_FALSE;
|
|
|
|
|
|
|
|
oldcert = find_smime_recipient_cert(cert->dbhandle, emailAddr);
|
|
|
|
if (oldcert) {
|
|
|
|
/* see if there is an entry already */
|
|
|
|
oldentry = ReadDBSMimeEntry(cert->dbhandle, emailAddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* both profileTime and emailProfile have to exist or not exist */
|
|
|
|
if ( emailProfile == NULL ) {
|
|
|
|
profileTime = NULL;
|
|
|
|
} else if ( profileTime == NULL ) {
|
|
|
|
emailProfile = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( oldentry == NULL ) {
|
|
|
|
/* no old entry for this address */
|
|
|
|
PORT_Assert(oldcert == NULL);
|
|
|
|
saveit = PR_TRUE;
|
|
|
|
} else {
|
|
|
|
/* there was already a profile for this email addr */
|
|
|
|
if ( profileTime ) {
|
|
|
|
/* we have an old and new profile - save whichever is more recent*/
|
|
|
|
if ( oldentry->optionsDate.len == 0 ) {
|
|
|
|
/* always replace if old entry doesn't have a time */
|
|
|
|
oldtime = LL_MININT;
|
|
|
|
} else {
|
|
|
|
rv = DER_UTCTimeToTime(&oldtime, &oldentry->optionsDate);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DER_UTCTimeToTime(&newtime, profileTime);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( LL_CMP(newtime, >, oldtime ) ) {
|
|
|
|
/* this is a newer profile, save it and cert */
|
|
|
|
saveit = PR_TRUE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* we don't have a new profile or time */
|
|
|
|
if ( oldentry->optionsDate.len == 0 ) {
|
|
|
|
/* the old entry doesn't have a time either, so compare certs*/
|
|
|
|
if ( CERT_IsNewer(cert, oldcert) ) {
|
|
|
|
/* new cert is newer, use it instead */
|
|
|
|
saveit = PR_TRUE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (oldcert) {
|
|
|
|
if (CERT_IsNewer(cert, oldcert)) {
|
|
|
|
saveit = PR_TRUE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
saveit = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( saveit ) {
|
|
|
|
if ( oldcert && ( oldcert != cert ) ) {
|
|
|
|
/* old cert is different from new cert */
|
|
|
|
if ( PORT_Memcmp(oldcert->trust, &trust, sizeof(trust)) == 0 ) {
|
|
|
|
/* old cert is only for e-mail, so delete it */
|
|
|
|
SEC_DeletePermCertificate(oldcert);
|
|
|
|
} else {
|
|
|
|
/* old cert is for other things too, so just change trust */
|
|
|
|
tmptrust = *oldcert->trust;
|
|
|
|
tmptrust.emailFlags &= ( ~CERTDB_VALID_PEER );
|
|
|
|
rv = CERT_ChangeCertTrust(oldcert->dbhandle, oldcert,
|
|
|
|
&tmptrust);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Subroutine */
|
|
|
|
/* now save the entry */
|
|
|
|
entry = NewDBSMimeEntry(emailAddr, &cert->derSubject, emailProfile,
|
|
|
|
profileTime, 0);
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_LockDB(cert->dbhandle);
|
|
|
|
|
|
|
|
rv = DeleteDBSMimeEntry(cert->dbhandle, emailAddr);
|
|
|
|
/* if delete fails, try to write new entry anyway... */
|
|
|
|
|
|
|
|
rv = WriteDBSMimeEntry(cert->dbhandle, entry);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
CERT_UnlockDB(cert->dbhandle);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* link subject entry back here */
|
|
|
|
rv = UpdateSubjectWithEmailAddr(cert, emailAddr);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
CERT_UnlockDB(cert->dbhandle);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_UnlockDB(cert->dbhandle);
|
|
|
|
/* End Subroutine */
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = SECSuccess;
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
loser:
|
|
|
|
rv = SECFailure;
|
|
|
|
|
|
|
|
done:
|
|
|
|
if ( oldcert ) {
|
|
|
|
CERT_DestroyCertificate(oldcert);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( entry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
}
|
|
|
|
if ( oldentry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)oldentry);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByEmailAddr(CERTCertDBHandle *handle, char *emailAddr)
|
|
|
|
{
|
|
|
|
certDBEntrySMime *entry;
|
|
|
|
CERTCertificate *cert = NULL;
|
|
|
|
|
|
|
|
emailAddr = CERT_FixupEmailAddr(emailAddr);
|
|
|
|
if ( emailAddr == NULL ) {
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = ReadDBSMimeEntry(handle, emailAddr);
|
|
|
|
|
|
|
|
/* XXX - this will have to change when multiple certs per subject
|
|
|
|
* are allowed
|
|
|
|
*/
|
|
|
|
if ( entry != NULL ) {
|
|
|
|
cert = CERT_FindCertByName(handle, &entry->subjectName);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( entry ) {
|
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Free(emailAddr);
|
|
|
|
|
|
|
|
return(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* find the smime symmetric capabilities profile for a given cert
|
|
|
|
*/
|
|
|
|
SECItem *
|
|
|
|
CERT_FindSMimeProfile(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
certDBEntrySMime *entry;
|
|
|
|
SECItem *retitem = NULL;
|
|
|
|
|
|
|
|
PORT_Assert(cert->emailAddr != NULL);
|
|
|
|
|
|
|
|
if ( cert->emailAddr == NULL ) {
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = ReadDBSMimeEntry(cert->dbhandle, cert->emailAddr);
|
|
|
|
|
|
|
|
if ( entry ) {
|
2001-01-08 19:24:23 +00:00
|
|
|
/* May not be for this cert... */
|
|
|
|
if (SECITEM_ItemsAreEqual(&cert->derSubject, &entry->subjectName))
|
|
|
|
retitem = SECITEM_DupItem(&entry->smimeOptions);
|
2000-03-31 20:13:40 +00:00
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(retitem);
|
|
|
|
}
|
|
|
|
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_FindCertByNicknameOrEmailAddr(CERTCertDBHandle *handle, char *name)
|
|
|
|
{
|
|
|
|
CERTCertificate *cert;
|
|
|
|
cert = CERT_FindCertByNickname(handle, name);
|
|
|
|
if ( cert == NULL ) {
|
|
|
|
cert = CERT_FindCertByEmailAddr(handle, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
CERT_IsCertRevoked(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
return(PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_NextSubjectCert(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
CERTSubjectNode *node;
|
|
|
|
CERTCertificate *retcert = NULL;
|
|
|
|
|
|
|
|
CERT_LockDB(cert->dbhandle);
|
|
|
|
|
|
|
|
node = FindCertSubjectNode(cert);
|
|
|
|
PORT_Assert(node != NULL);
|
|
|
|
|
|
|
|
if ( node->next != NULL ) {
|
|
|
|
retcert = CERT_FindCertByKeyNoLocking(cert->dbhandle,
|
|
|
|
&node->next->certKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
CERT_UnlockDB(cert->dbhandle);
|
|
|
|
|
|
|
|
return(retcert);
|
|
|
|
}
|
|
|
|
|
|
|
|
CERTCertificate *
|
|
|
|
CERT_PrevSubjectCert(CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
CERTSubjectNode *node;
|
|
|
|
CERTCertificate *retcert = NULL;
|
|
|
|
|
|
|
|
CERT_LockDB(cert->dbhandle);
|
|
|
|
node = FindCertSubjectNode(cert);
|
|
|
|
PORT_Assert(node != NULL);
|
|
|
|
|
|
|
|
if ( node->prev != NULL ) {
|
|
|
|
retcert = CERT_FindCertByKeyNoLocking(cert->dbhandle,
|
|
|
|
&node->prev->certKey);
|
|
|
|
}
|
|
|
|
CERT_UnlockDB(cert->dbhandle);
|
|
|
|
|
|
|
|
return(retcert);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
CERT_SaveImportedCert(CERTCertificate *cert, SECCertUsage usage,
|
|
|
|
PRBool caOnly, char *nickname)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
PRBool saveit;
|
|
|
|
CERTCertTrust trust;
|
|
|
|
CERTCertTrust tmptrust;
|
|
|
|
PRBool isCA;
|
|
|
|
unsigned int certtype;
|
|
|
|
PRBool freeNickname = PR_FALSE;
|
|
|
|
|
|
|
|
isCA = CERT_IsCACert(cert, NULL);
|
|
|
|
if ( caOnly && ( !isCA ) ) {
|
|
|
|
return(SECSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
saveit = PR_TRUE;
|
|
|
|
|
|
|
|
PORT_Memset((void *)&trust, 0, sizeof(trust));
|
|
|
|
|
|
|
|
certtype = cert->nsCertType;
|
|
|
|
|
|
|
|
/* if no CA bits in cert type, then set all CA bits */
|
|
|
|
if ( isCA && ( ! ( certtype & NS_CERT_TYPE_CA ) ) ) {
|
|
|
|
certtype |= NS_CERT_TYPE_CA;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if no app bits in cert type, then set all app bits */
|
|
|
|
if ( ( !isCA ) && ( ! ( certtype & NS_CERT_TYPE_APP ) ) ) {
|
|
|
|
certtype |= NS_CERT_TYPE_APP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( isCA && !nickname ) {
|
|
|
|
nickname = CERT_MakeCANickname(cert);
|
|
|
|
if ( nickname != NULL ) {
|
|
|
|
freeNickname = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( usage ) {
|
|
|
|
case certUsageEmailSigner:
|
|
|
|
case certUsageEmailRecipient:
|
|
|
|
if ( isCA ) {
|
|
|
|
if ( certtype & NS_CERT_TYPE_EMAIL_CA ) {
|
|
|
|
trust.emailFlags = CERTDB_VALID_CA;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PORT_Assert(nickname == NULL);
|
|
|
|
|
|
|
|
if ( cert->emailAddr == NULL ) {
|
|
|
|
saveit = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( certtype & NS_CERT_TYPE_EMAIL ) {
|
|
|
|
trust.emailFlags = CERTDB_VALID_PEER;
|
|
|
|
if ( ! ( cert->rawKeyUsage & KU_KEY_ENCIPHERMENT ) ) {
|
|
|
|
/* don't save it if KeyEncipherment is not allowed */
|
|
|
|
saveit = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case certUsageUserCertImport:
|
|
|
|
if ( isCA ) {
|
|
|
|
if ( certtype & NS_CERT_TYPE_SSL_CA ) {
|
|
|
|
trust.sslFlags = CERTDB_VALID_CA;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( certtype & NS_CERT_TYPE_EMAIL_CA ) {
|
|
|
|
trust.emailFlags = CERTDB_VALID_CA;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( certtype & NS_CERT_TYPE_OBJECT_SIGNING_CA ) {
|
|
|
|
trust.objectSigningFlags = CERTDB_VALID_CA;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if ( certtype & NS_CERT_TYPE_SSL_CLIENT ) {
|
|
|
|
trust.sslFlags = CERTDB_VALID_PEER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( certtype & NS_CERT_TYPE_EMAIL ) {
|
|
|
|
trust.emailFlags = CERTDB_VALID_PEER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( certtype & NS_CERT_TYPE_OBJECT_SIGNING ) {
|
|
|
|
trust.objectSigningFlags = CERTDB_VALID_PEER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2001-07-11 23:57:01 +00:00
|
|
|
case certUsageAnyCA:
|
|
|
|
trust.sslFlags = CERTDB_VALID_CA;
|
|
|
|
break;
|
|
|
|
case certUsageSSLCA:
|
|
|
|
trust.sslFlags = CERTDB_VALID_CA |
|
|
|
|
CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
|
|
|
|
break;
|
2000-03-31 20:13:40 +00:00
|
|
|
default: /* XXX added to quiet warnings; no other cases needed? */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( saveit ) {
|
|
|
|
if ( cert->isperm ) {
|
|
|
|
/* Cert already in the DB. Just adjust flags */
|
|
|
|
tmptrust = *cert->trust;
|
|
|
|
tmptrust.sslFlags |= trust.sslFlags;
|
|
|
|
tmptrust.emailFlags |= trust.emailFlags;
|
|
|
|
tmptrust.objectSigningFlags |= trust.objectSigningFlags;
|
|
|
|
|
|
|
|
rv = CERT_ChangeCertTrust(cert->dbhandle, cert,
|
|
|
|
&tmptrust);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Cert not already in the DB. Add it */
|
|
|
|
rv = CERT_AddTempCertToPerm(cert, nickname, &trust);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = SECSuccess;
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
loser:
|
|
|
|
rv = SECFailure;
|
|
|
|
done:
|
|
|
|
|
|
|
|
if ( freeNickname ) {
|
|
|
|
PORT_Free(nickname);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
CERT_ChangeCertTrustByUsage(CERTCertDBHandle *certdb,
|
|
|
|
CERTCertificate *cert, SECCertUsage usage)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
CERTCertTrust trust;
|
|
|
|
CERTCertTrust tmptrust;
|
|
|
|
unsigned int certtype;
|
|
|
|
PRBool saveit;
|
|
|
|
|
|
|
|
saveit = PR_TRUE;
|
|
|
|
|
|
|
|
PORT_Memset((void *)&trust, 0, sizeof(trust));
|
|
|
|
|
|
|
|
certtype = cert->nsCertType;
|
|
|
|
|
|
|
|
/* if no app bits in cert type, then set all app bits */
|
|
|
|
if ( ! ( certtype & NS_CERT_TYPE_APP ) ) {
|
|
|
|
certtype |= NS_CERT_TYPE_APP;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( usage ) {
|
|
|
|
case certUsageEmailSigner:
|
|
|
|
case certUsageEmailRecipient:
|
|
|
|
if ( certtype & NS_CERT_TYPE_EMAIL ) {
|
|
|
|
trust.emailFlags = CERTDB_VALID_PEER;
|
|
|
|
if ( ! ( cert->rawKeyUsage & KU_KEY_ENCIPHERMENT ) ) {
|
|
|
|
/* don't save it if KeyEncipherment is not allowed */
|
|
|
|
saveit = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case certUsageUserCertImport:
|
|
|
|
if ( certtype & NS_CERT_TYPE_EMAIL ) {
|
|
|
|
trust.emailFlags = CERTDB_VALID_PEER;
|
|
|
|
}
|
|
|
|
/* VALID_USER is already set if the cert was imported,
|
|
|
|
* in the case that the cert was already in the database
|
|
|
|
* through SMIME or other means, we should set the USER
|
|
|
|
* flags, if they are not already set.
|
|
|
|
*/
|
|
|
|
if( cert->isperm ) {
|
|
|
|
if ( certtype & NS_CERT_TYPE_SSL_CLIENT ) {
|
|
|
|
if( !(cert->trust->sslFlags & CERTDB_USER) ) {
|
|
|
|
trust.sslFlags |= CERTDB_USER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( certtype & NS_CERT_TYPE_EMAIL ) {
|
|
|
|
if( !(cert->trust->emailFlags & CERTDB_USER) ) {
|
|
|
|
trust.emailFlags |= CERTDB_USER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( certtype & NS_CERT_TYPE_OBJECT_SIGNING ) {
|
|
|
|
if( !(cert->trust->objectSigningFlags & CERTDB_USER) ) {
|
|
|
|
trust.objectSigningFlags |= CERTDB_USER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: /* XXX added to quiet warnings; no other cases needed? */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (trust.sslFlags | trust.emailFlags | trust.objectSigningFlags) == 0 ){
|
|
|
|
saveit = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( saveit && cert->isperm ) {
|
|
|
|
/* Cert already in the DB. Just adjust flags */
|
|
|
|
tmptrust = *cert->trust;
|
|
|
|
tmptrust.sslFlags |= trust.sslFlags;
|
|
|
|
tmptrust.emailFlags |= trust.emailFlags;
|
|
|
|
tmptrust.objectSigningFlags |= trust.objectSigningFlags;
|
|
|
|
|
|
|
|
rv = CERT_ChangeCertTrust(cert->dbhandle, cert,
|
|
|
|
&tmptrust);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = SECSuccess;
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
loser:
|
|
|
|
rv = SECFailure;
|
|
|
|
done:
|
|
|
|
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
CERT_GetDBContentVersion(CERTCertDBHandle *handle)
|
|
|
|
{
|
|
|
|
certDBEntryContentVersion *entry;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
entry = ReadDBContentVersionEntry(handle);
|
|
|
|
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = entry->contentVersion;
|
|
|
|
|
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CERT_SetDBContentVersion(int version, CERTCertDBHandle *handle)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
certDBEntryContentVersion *entry;
|
|
|
|
|
|
|
|
entry = NewDBContentVersionEntry(0);
|
|
|
|
|
|
|
|
if ( entry == NULL ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = DeleteDBContentVersionEntry(handle);
|
|
|
|
rv = WriteDBContentVersionEntry(handle, entry);
|
|
|
|
|
|
|
|
DestroyDBEntry((certDBEntry *)entry);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Creates or adds to a list of all certs with a give subject name, sorted by
|
|
|
|
* validity time, newest first. Invalid certs are considered older than
|
|
|
|
* valid certs. If validOnly is set, do not include invalid certs on list.
|
|
|
|
*/
|
|
|
|
CERTCertList *
|
|
|
|
CERT_CreateSubjectCertList(CERTCertList *certList, CERTCertDBHandle *handle,
|
|
|
|
SECItem *name, int64 sorttime, PRBool validOnly)
|
|
|
|
{
|
|
|
|
CERTCertificate *cert = NULL;
|
|
|
|
CERTSubjectList *subjectList = NULL;
|
|
|
|
CERTSubjectNode *node;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
if ( certList == NULL ) {
|
|
|
|
certList = CERT_NewCertList();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( certList == NULL ) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
|
|
|
|
subjectList = FindSubjectList(handle, name, PR_FALSE);
|
|
|
|
|
|
|
|
if ( subjectList != NULL ) {
|
|
|
|
node = subjectList->head;
|
|
|
|
PORT_Assert(node);
|
|
|
|
while (node) {
|
|
|
|
cert = CERT_FindCertByKey(handle, &node->certKey);
|
|
|
|
|
|
|
|
/* if validOnly, then check validity period before adding to list*/
|
|
|
|
if ( ( !validOnly ) ||
|
|
|
|
( CERT_CheckCertValidTimes(cert, sorttime, PR_FALSE)
|
|
|
|
== secCertTimeValid ) ) {
|
|
|
|
rv = CERT_AddCertToListSorted(certList, cert,
|
|
|
|
CERT_SortCBValidity,
|
|
|
|
(void *)&sorttime);
|
|
|
|
if ( rv != SECSuccess ) {
|
|
|
|
CERT_DestroyCertificate(cert);
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CERT_DestroyCertificate(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(certList);
|
|
|
|
|
|
|
|
loser:
|
|
|
|
if ( certList != NULL ) {
|
|
|
|
CERT_DestroyCertList(certList);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Creates or adds to a list of all certs with a give nickname, sorted by
|
|
|
|
* validity time, newest first. Invalid certs are considered older than valid
|
|
|
|
* certs. If validOnly is set, do not include invalid certs on list.
|
|
|
|
*/
|
|
|
|
CERTCertList *
|
|
|
|
CERT_CreateNicknameCertList(CERTCertList *certList, CERTCertDBHandle *handle,
|
|
|
|
char *nickname, int64 sorttime, PRBool validOnly)
|
|
|
|
{
|
|
|
|
CERTCertificate *cert;
|
|
|
|
CERTCertList *ret;
|
|
|
|
|
|
|
|
cert = CERT_FindCertByNickname(handle, nickname);
|
|
|
|
if ( cert == NULL ) {
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = CERT_CreateSubjectCertList(certList, handle, &cert->derSubject,
|
|
|
|
sorttime, validOnly);
|
|
|
|
|
|
|
|
CERT_DestroyCertificate(cert);
|
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Creates or adds to a list of all certs with a give email addr, sorted by
|
|
|
|
* validity time, newest first. Invalid certs are considered older than valid
|
|
|
|
* certs. If validOnly is set, do not include invalid certs on list.
|
|
|
|
*/
|
|
|
|
CERTCertList *
|
|
|
|
CERT_CreateEmailAddrCertList(CERTCertList *certList, CERTCertDBHandle *handle,
|
|
|
|
char *emailAddr, int64 sorttime, PRBool validOnly)
|
|
|
|
{
|
|
|
|
CERTCertificate *cert;
|
|
|
|
CERTCertList *ret;
|
|
|
|
|
|
|
|
cert = CERT_FindCertByEmailAddr(handle, emailAddr);
|
|
|
|
if ( cert == NULL ) {
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = CERT_CreateSubjectCertList(certList, handle, &cert->derSubject,
|
|
|
|
sorttime, validOnly);
|
|
|
|
|
|
|
|
CERT_DestroyCertificate(cert);
|
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|