diff options
author | cvs2hg <devnull@localhost> | 2001-07-11 23:58:03 +0000 |
---|---|---|
committer | cvs2hg <devnull@localhost> | 2001-07-11 23:58:03 +0000 |
commit | 9b09365045637779e78b6bc89af721928f6e768e (patch) | |
tree | 16a8c88bd91c4f759abdec26dd13fdfd281ba785 | |
parent | 1d2fc658c9eeed67ed768fdb3924b176b9d05b22 (diff) | |
download | nss-hg-9b09365045637779e78b6bc89af721928f6e768e.tar.gz |
fixup commit for tag 'NSS_3_3_BETA3'NSS_3_3_BETA3
-rw-r--r-- | security/nss/lib/ckfw/object.c | 1044 | ||||
-rw-r--r-- | security/nss/lib/pk11wrap/pk11kea.c | 224 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12d.c | 3230 | ||||
-rw-r--r-- | security/nss/lib/softoken/keydb.c | 2482 | ||||
-rw-r--r-- | security/nss/lib/softoken/private.h | 78 | ||||
-rw-r--r-- | security/nss/lib/util/secerr.h | 187 |
6 files changed, 7245 insertions, 0 deletions
diff --git a/security/nss/lib/ckfw/object.c b/security/nss/lib/ckfw/object.c new file mode 100644 index 000000000..ec8dfd6d5 --- /dev/null +++ b/security/nss/lib/ckfw/object.c @@ -0,0 +1,1044 @@ +/* + * 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. + */ + +#ifdef DEBUG +static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; +#endif /* DEBUG */ + +/* + * object.c + * + * This file implements the NSSCKFWObject type and methods. + */ + +#ifndef CK_T +#include "ck.h" +#endif /* CK_T */ + +/* + * NSSCKFWObject + * + * -- create/destroy -- + * nssCKFWObject_Create + * nssCKFWObject_Finalize + * nssCKFWObject_Destroy + * + * -- public accessors -- + * NSSCKFWObject_GetMDObject + * NSSCKFWObject_GetArena + * NSSCKFWObject_IsTokenObject + * NSSCKFWObject_GetAttributeCount + * NSSCKFWObject_GetAttributeTypes + * NSSCKFWObject_GetAttributeSize + * NSSCKFWObject_GetAttribute + * NSSCKFWObject_SetAttribute + * NSSCKFWObject_GetObjectSize + * + * -- implement public accessors -- + * nssCKFWObject_GetMDObject + * nssCKFWObject_GetArena + * + * -- private accessors -- + * nssCKFWObject_SetHandle + * nssCKFWObject_GetHandle + * + * -- module fronts -- + * nssCKFWObject_IsTokenObject + * nssCKFWObject_GetAttributeCount + * nssCKFWObject_GetAttributeTypes + * nssCKFWObject_GetAttributeSize + * nssCKFWObject_GetAttribute + * nssCKFWObject_SetAttribute + * nssCKFWObject_GetObjectSize + */ + +struct NSSCKFWObjectStr { + NSSCKFWMutex *mutex; /* merely to serialise the MDObject calls */ + NSSArena *arena; + NSSCKMDObject *mdObject; + NSSCKMDSession *mdSession; + NSSCKFWSession *fwSession; + NSSCKMDToken *mdToken; + NSSCKFWToken *fwToken; + NSSCKMDInstance *mdInstance; + NSSCKFWInstance *fwInstance; + CK_OBJECT_HANDLE hObject; +}; + +#ifdef DEBUG +/* + * But first, the pointer-tracking stuff. + * + * NOTE: the pointer-tracking support in NSS/base currently relies + * upon NSPR's CallOnce support. That, however, relies upon NSPR's + * locking, which is tied into the runtime. We need a pointer-tracker + * implementation that uses the locks supplied through C_Initialize. + * That support, however, can be filled in later. So for now, I'll + * just do this routines as no-ops. + */ + +static CK_RV +object_add_pointer +( + const NSSCKFWObject *fwObject +) +{ + return CKR_OK; +} + +static CK_RV +object_remove_pointer +( + const NSSCKFWObject *fwObject +) +{ + return CKR_OK; +} + +NSS_IMPLEMENT CK_RV +nssCKFWObject_verifyPointer +( + const NSSCKFWObject *fwObject +) +{ + return CKR_OK; +} + +#endif /* DEBUG */ + + +/* + * nssCKFWObject_Create + * + */ +NSS_IMPLEMENT NSSCKFWObject * +nssCKFWObject_Create +( + NSSArena *arena, + NSSCKMDObject *mdObject, + NSSCKFWSession *fwSession, + NSSCKFWToken *fwToken, + NSSCKFWInstance *fwInstance, + CK_RV *pError +) +{ + NSSCKFWObject *fwObject; + nssCKFWHash *mdObjectHash; + +#ifdef NSSDEBUG + if( (CK_RV *)NULL == pError ) { + return (NSSCKFWObject *)NULL; + } + + if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { + *pError = CKR_ARGUMENTS_BAD; + return (NSSCKFWObject *)NULL; + } +#endif /* NSSDEBUG */ + + mdObjectHash = nssCKFWToken_GetMDObjectHash(fwToken); + if( (nssCKFWHash *)NULL == mdObjectHash ) { + *pError = CKR_GENERAL_ERROR; + return (NSSCKFWObject *)NULL; + } + + if( nssCKFWHash_Exists(mdObjectHash, mdObject) ) { + fwObject = nssCKFWHash_Lookup(mdObjectHash, mdObject); + return fwObject; + } + + fwObject = nss_ZNEW(arena, NSSCKFWObject); + if( (NSSCKFWObject *)NULL == fwObject ) { + *pError = CKR_HOST_MEMORY; + return (NSSCKFWObject *)NULL; + } + + fwObject->arena = arena; + fwObject->mdObject = mdObject; + fwObject->fwSession = fwSession; + + if( (NSSCKFWSession *)NULL != fwSession ) { + fwObject->mdSession = nssCKFWSession_GetMDSession(fwSession); + } + + fwObject->fwToken = fwToken; + + if( (NSSCKFWToken *)NULL != fwToken ) { + fwObject->mdToken = nssCKFWToken_GetMDToken(fwToken); + } + + fwObject->fwInstance = fwInstance; + fwObject->mdInstance = nssCKFWInstance_GetMDInstance(fwInstance); + fwObject->mutex = nssCKFWInstance_CreateMutex(fwInstance, arena, pError); + if( (NSSCKFWMutex *)NULL == fwObject->mutex ) { + if( CKR_OK == *pError ) { + *pError = CKR_GENERAL_ERROR; + } + return (NSSCKFWObject *)NULL; + } + + *pError = nssCKFWHash_Add(mdObjectHash, mdObject, fwObject); + if( CKR_OK != *pError ) { + nss_ZFreeIf(fwObject); + return (NSSCKFWObject *)NULL; + } + +#ifdef DEBUG + *pError = object_add_pointer(fwObject); + if( CKR_OK != *pError ) { + nssCKFWHash_Remove(mdObjectHash, mdObject); + nss_ZFreeIf(fwObject); + return (NSSCKFWObject *)NULL; + } +#endif /* DEBUG */ + + *pError = CKR_OK; + return fwObject; +} + +/* + * nssCKFWObject_Finalize + * + */ +NSS_IMPLEMENT void +nssCKFWObject_Finalize +( + NSSCKFWObject *fwObject +) +{ + nssCKFWHash *mdObjectHash; + +#ifdef NSSDEBUG + if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) { + return; + } +#endif /* NSSDEBUG */ + + (void)nssCKFWMutex_Destroy(fwObject->mutex); + + if( (void *)NULL != (void *)fwObject->mdObject->Finalize ) { + fwObject->mdObject->Finalize(fwObject->mdObject, fwObject, + fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, + fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance); + } + + mdObjectHash = nssCKFWToken_GetMDObjectHash(fwObject->fwToken); + if( (nssCKFWHash *)NULL != mdObjectHash ) { + nssCKFWHash_Remove(mdObjectHash, fwObject->mdObject); + } + + nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject); + nss_ZFreeIf(fwObject); + +#ifdef DEBUG + (void)object_remove_pointer(fwObject); +#endif /* DEBUG */ + + return; +} + +/* + * nssCKFWObject_Destroy + * + */ +NSS_IMPLEMENT void +nssCKFWObject_Destroy +( + NSSCKFWObject *fwObject +) +{ + nssCKFWHash *mdObjectHash; + +#ifdef NSSDEBUG + if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) { + return; + } +#endif /* NSSDEBUG */ + + (void)nssCKFWMutex_Destroy(fwObject->mutex); + + if( (void *)NULL != (void *)fwObject->mdObject->Destroy ) { + fwObject->mdObject->Destroy(fwObject->mdObject, fwObject, + fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, + fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance); + } + + mdObjectHash = nssCKFWToken_GetMDObjectHash(fwObject->fwToken); + if( (nssCKFWHash *)NULL != mdObjectHash ) { + nssCKFWHash_Remove(mdObjectHash, fwObject->mdObject); + } + + nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject); + nss_ZFreeIf(fwObject); + +#ifdef DEBUG + (void)object_remove_pointer(fwObject); +#endif /* DEBUG */ + + return; +} + +/* + * nssCKFWObject_GetMDObject + * + */ +NSS_IMPLEMENT NSSCKMDObject * +nssCKFWObject_GetMDObject +( + NSSCKFWObject *fwObject +) +{ +#ifdef NSSDEBUG + if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) { + return (NSSCKMDObject *)NULL; + } +#endif /* NSSDEBUG */ + + return fwObject->mdObject; +} + +/* + * nssCKFWObject_GetArena + * + */ +NSS_IMPLEMENT NSSArena * +nssCKFWObject_GetArena +( + NSSCKFWObject *fwObject, + CK_RV *pError +) +{ +#ifdef NSSDEBUG + if( (CK_RV *)NULL == pError ) { + return (NSSArena *)NULL; + } + + *pError = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != *pError ) { + return (NSSArena *)NULL; + } +#endif /* NSSDEBUG */ + + return fwObject->arena; +} + +/* + * nssCKFWObject_SetHandle + * + */ +NSS_IMPLEMENT CK_RV +nssCKFWObject_SetHandle +( + NSSCKFWObject *fwObject, + CK_OBJECT_HANDLE hObject +) +{ +#ifdef NSSDEBUG + CK_RV error = CKR_OK; +#endif /* NSSDEBUG */ + +#ifdef NSSDEBUG + error = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != error ) { + return error; + } +#endif /* NSSDEBUG */ + + if( (CK_OBJECT_HANDLE)0 != fwObject->hObject ) { + return CKR_GENERAL_ERROR; + } + + fwObject->hObject = hObject; + + return CKR_OK; +} + +/* + * nssCKFWObject_GetHandle + * + */ +NSS_IMPLEMENT CK_OBJECT_HANDLE +nssCKFWObject_GetHandle +( + NSSCKFWObject *fwObject +) +{ +#ifdef NSSDEBUG + if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) { + return (CK_OBJECT_HANDLE)0; + } +#endif /* NSSDEBUG */ + + return fwObject->hObject; +} + +/* + * nssCKFWObject_IsTokenObject + * + */ +NSS_IMPLEMENT CK_BBOOL +nssCKFWObject_IsTokenObject +( + NSSCKFWObject *fwObject +) +{ + CK_BBOOL b = CK_FALSE; + +#ifdef NSSDEBUG + if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) { + return CK_FALSE; + } +#endif /* NSSDEBUG */ + + if( CKR_OK != nssCKFWMutex_Lock(fwObject->mutex) ) { + return CK_FALSE; + } + + if( (void *)NULL == (void *)fwObject->mdObject->IsTokenObject ) { + NSSItem item; + NSSItem *pItem; + CK_RV rv = CKR_OK; + + item.data = (void *)&b; + item.size = sizeof(b); + + pItem = nssCKFWObject_GetAttribute(fwObject, CKA_TOKEN, &item, + (NSSArena *)NULL, &rv); + if( (NSSItem *)NULL == pItem ) { + /* Error of some type */ + b = CK_FALSE; + goto done; + } + + goto done; + } + + b = fwObject->mdObject->IsTokenObject(fwObject->mdObject, fwObject, + fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, + fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance); + + done: + (void)nssCKFWMutex_Unlock(fwObject->mutex); + return b; +} + +/* + * nssCKFWObject_GetAttributeCount + * + */ +NSS_IMPLEMENT CK_ULONG +nssCKFWObject_GetAttributeCount +( + NSSCKFWObject *fwObject, + CK_RV *pError +) +{ + CK_ULONG rv; + +#ifdef NSSDEBUG + if( (CK_RV *)NULL == pError ) { + return (CK_ULONG)0; + } + + *pError = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != *pError ) { + return (CK_ULONG)0; + } +#endif /* NSSDEBUG */ + + if( (void *)NULL == (void *)fwObject->mdObject->GetAttributeCount ) { + *pError = CKR_GENERAL_ERROR; + return (CK_ULONG)0; + } + + *pError = nssCKFWMutex_Lock(fwObject->mutex); + if( CKR_OK != *pError ) { + return (CK_ULONG)0; + } + + rv = fwObject->mdObject->GetAttributeCount(fwObject->mdObject, fwObject, + fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, + fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance, + pError); + + (void)nssCKFWMutex_Unlock(fwObject->mutex); + return rv; +} + +/* + * nssCKFWObject_GetAttributeTypes + * + */ +NSS_IMPLEMENT CK_RV +nssCKFWObject_GetAttributeTypes +( + NSSCKFWObject *fwObject, + CK_ATTRIBUTE_TYPE_PTR typeArray, + CK_ULONG ulCount +) +{ + CK_RV error = CKR_OK; + +#ifdef NSSDEBUG + error = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != error ) { + return error; + } + + if( (CK_ATTRIBUTE_TYPE_PTR)NULL == typeArray ) { + return CKR_ARGUMENTS_BAD; + } +#endif /* NSSDEBUG */ + + if( (void *)NULL == (void *)fwObject->mdObject->GetAttributeTypes ) { + return CKR_GENERAL_ERROR; + } + + error = nssCKFWMutex_Lock(fwObject->mutex); + if( CKR_OK != error ) { + return error; + } + + error = fwObject->mdObject->GetAttributeTypes(fwObject->mdObject, fwObject, + fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, + fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance, + typeArray, ulCount); + + (void)nssCKFWMutex_Unlock(fwObject->mutex); + return error; +} + +/* + * nssCKFWObject_GetAttributeSize + * + */ +NSS_IMPLEMENT CK_ULONG +nssCKFWObject_GetAttributeSize +( + NSSCKFWObject *fwObject, + CK_ATTRIBUTE_TYPE attribute, + CK_RV *pError +) +{ + CK_ULONG rv; + +#ifdef NSSDEBUG + if( (CK_RV *)NULL == pError ) { + return (CK_ULONG)0; + } + + *pError = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != *pError ) { + return (CK_ULONG)0; + } +#endif /* NSSDEBUG */ + + if( (void *)NULL == (void *)fwObject->mdObject->GetAttributeSize ) { + *pError = CKR_GENERAL_ERROR; + return (CK_ULONG )0; + } + + *pError = nssCKFWMutex_Lock(fwObject->mutex); + if( CKR_OK != *pError ) { + return (CK_ULONG)0; + } + + rv = fwObject->mdObject->GetAttributeSize(fwObject->mdObject, fwObject, + fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, + fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance, + attribute, pError); + + (void)nssCKFWMutex_Unlock(fwObject->mutex); + return rv; +} + +/* + * nssCKFWObject_GetAttribute + * + * Usual NSS allocation rules: + * If itemOpt is not NULL, it will be returned; otherwise an NSSItem + * will be allocated. If itemOpt is not NULL but itemOpt->data is, + * the buffer will be allocated; otherwise, the buffer will be used. + * Any allocations will come from the optional arena, if one is + * specified. + */ +NSS_IMPLEMENT NSSItem * +nssCKFWObject_GetAttribute +( + NSSCKFWObject *fwObject, + CK_ATTRIBUTE_TYPE attribute, + NSSItem *itemOpt, + NSSArena *arenaOpt, + CK_RV *pError +) +{ + NSSItem *rv = (NSSItem *)NULL; + NSSItem *mdItem; + +#ifdef NSSDEBUG + if( (CK_RV *)NULL == pError ) { + return (NSSItem *)NULL; + } + + *pError = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != *pError ) { + return (NSSItem *)NULL; + } +#endif /* NSSDEBUG */ + + if( (void *)NULL == (void *)fwObject->mdObject->GetAttributeSize ) { + *pError = CKR_GENERAL_ERROR; + return (NSSItem *)NULL; + } + + *pError = nssCKFWMutex_Lock(fwObject->mutex); + if( CKR_OK != *pError ) { + return (NSSItem *)NULL; + } + + mdItem = fwObject->mdObject->GetAttribute(fwObject->mdObject, fwObject, + fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, + fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance, + attribute, pError); + + if( (NSSItem *)NULL == mdItem ) { + if( CKR_OK == *pError ) { + *pError = CKR_GENERAL_ERROR; + } + + goto done; + } + + if( (NSSItem *)NULL == itemOpt ) { + rv = nss_ZNEW(arenaOpt, NSSItem); + if( (NSSItem *)NULL == rv ) { + *pError = CKR_HOST_MEMORY; + goto done; + } + } else { + rv = itemOpt; + } + + if( (void *)NULL == rv->data ) { + rv->size = mdItem->size; + rv->data = nss_ZAlloc(arenaOpt, rv->size); + if( (void *)NULL == rv->data ) { + *pError = CKR_HOST_MEMORY; + if( (NSSItem *)NULL == itemOpt ) { + nss_ZFreeIf(rv); + } + rv = (NSSItem *)NULL; + goto done; + } + } else { + if( rv->size >= mdItem->size ) { + rv->size = mdItem->size; + } else { + *pError = CKR_BUFFER_TOO_SMALL; + /* Should we set rv->size to mdItem->size? */ + /* rv can't have been allocated */ + rv = (NSSItem *)NULL; + goto done; + } + } + + (void)nsslibc_memcpy(rv->data, mdItem->data, rv->size); + + done: + (void)nssCKFWMutex_Unlock(fwObject->mutex); + return rv; +} + +/* + * nssCKFWObject_SetAttribute + * + */ +NSS_IMPLEMENT CK_RV +nssCKFWObject_SetAttribute +( + NSSCKFWObject *fwObject, + CK_ATTRIBUTE_TYPE attribute, + NSSItem *value +) +{ + CK_RV error = CKR_OK; + +#ifdef NSSDEBUG + error = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != error ) { + return error; + } +#endif /* NSSDEBUG */ + + if( CKA_TOKEN == attribute ) { + /* + * We're changing from a session object to a token object or + * vice-versa. + */ + + CK_ATTRIBUTE a; + NSSCKFWObject *newFwObject; + NSSCKFWObject swab; + + a.type = CKA_TOKEN; + a.pValue = value->data; + a.ulValueLen = value->size; + + newFwObject = nssCKFWSession_CopyObject(fwObject->fwSession, fwObject, + &a, 1, &error); + if( (NSSCKFWObject *)NULL == newFwObject ) { + if( CKR_OK == error ) { + error = CKR_GENERAL_ERROR; + } + return error; + } + + /* + * Actually, I bet the locking is worse than this.. this part of + * the code could probably use some scrutiny and reworking. + */ + error = nssCKFWMutex_Lock(fwObject->mutex); + if( CKR_OK != error ) { + nssCKFWObject_Destroy(newFwObject); + return error; + } + + error = nssCKFWMutex_Lock(newFwObject->mutex); + if( CKR_OK != error ) { + nssCKFWMutex_Unlock(fwObject->mutex); + nssCKFWObject_Destroy(newFwObject); + return error; + } + + /* + * Now, we have our new object, but it has a new fwObject pointer, + * while we have to keep the existing one. So quick swap the contents. + */ + swab = *fwObject; + *fwObject = *newFwObject; + *newFwObject = swab; + + /* But keep the mutexes the same */ + swab.mutex = fwObject->mutex; + fwObject->mutex = newFwObject->mutex; + newFwObject->mutex = swab.mutex; + + (void)nssCKFWMutex_Unlock(newFwObject->mutex); + (void)nssCKFWMutex_Unlock(fwObject->mutex); + + /* + * Either remove or add this to the list of session objects + */ + + if( CK_FALSE == *(CK_BBOOL *)value->data ) { + /* + * New one is a session object, except since we "stole" the fwObject, it's + * not in the list. Add it. + */ + nssCKFWSession_RegisterSessionObject(fwObject->fwSession, fwObject); + } else { + /* + * New one is a token object, except since we "stole" the fwObject, it's + * in the list. Remove it. + */ + nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject); + } + + /* + * Now delete the old object. Remember the names have changed. + */ + nssCKFWObject_Destroy(newFwObject); + + return CKR_OK; + } else { + /* + * An "ordinary" change. + */ + if( (void *)NULL == (void *)fwObject->mdObject->SetAttribute ) { + /* We could fake it with copying, like above.. later */ + return CKR_ATTRIBUTE_READ_ONLY; + } + + error = nssCKFWMutex_Lock(fwObject->mutex); + if( CKR_OK != error ) { + return error; + } + + error = fwObject->mdObject->SetAttribute(fwObject->mdObject, fwObject, + fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, + fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance, + attribute, value); + + (void)nssCKFWMutex_Unlock(fwObject->mutex); + + return error; + } +} + +/* + * nssCKFWObject_GetObjectSize + * + */ +NSS_IMPLEMENT CK_ULONG +nssCKFWObject_GetObjectSize +( + NSSCKFWObject *fwObject, + CK_RV *pError +) +{ + CK_ULONG rv; + +#ifdef NSSDEBUG + if( (CK_RV *)NULL == pError ) { + return (CK_ULONG)0; + } + + *pError = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != *pError ) { + return (CK_ULONG)0; + } +#endif /* NSSDEBUG */ + + if( (void *)NULL == (void *)fwObject->mdObject->GetObjectSize ) { + *pError = CKR_INFORMATION_SENSITIVE; + return (CK_ULONG)0; + } + + *pError = nssCKFWMutex_Lock(fwObject->mutex); + if( CKR_OK != *pError ) { + return (CK_ULONG)0; + } + + rv = fwObject->mdObject->GetObjectSize(fwObject->mdObject, fwObject, + fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, + fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance, + pError); + + (void)nssCKFWMutex_Unlock(fwObject->mutex); + return rv; +} + +/* + * NSSCKFWObject_GetMDObject + * + */ +NSS_IMPLEMENT NSSCKMDObject * +NSSCKFWObject_GetMDObject +( + NSSCKFWObject *fwObject +) +{ +#ifdef DEBUG + if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) { + return (NSSCKMDObject *)NULL; + } +#endif /* DEBUG */ + + return nssCKFWObject_GetMDObject(fwObject); +} + +/* + * NSSCKFWObject_GetArena + * + */ +NSS_IMPLEMENT NSSArena * +NSSCKFWObject_GetArena +( + NSSCKFWObject *fwObject, + CK_RV *pError +) +{ +#ifdef DEBUG + if( (CK_RV *)NULL == pError ) { + return (NSSArena *)NULL; + } + + *pError = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != *pError ) { + return (NSSArena *)NULL; + } +#endif /* DEBUG */ + + return nssCKFWObject_GetArena(fwObject, pError); +} + +/* + * NSSCKFWObject_IsTokenObject + * + */ +NSS_IMPLEMENT CK_BBOOL +NSSCKFWObject_IsTokenObject +( + NSSCKFWObject *fwObject +) +{ +#ifdef DEBUG + if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) { + return CK_FALSE; + } +#endif /* DEBUG */ + + return nssCKFWObject_IsTokenObject(fwObject); +} + +/* + * NSSCKFWObject_GetAttributeCount + * + */ +NSS_IMPLEMENT CK_ULONG +NSSCKFWObject_GetAttributeCount +( + NSSCKFWObject *fwObject, + CK_RV *pError +) +{ +#ifdef DEBUG + if( (CK_RV *)NULL == pError ) { + return (CK_ULONG)0; + } + + *pError = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != *pError ) { + return (CK_ULONG)0; + } +#endif /* DEBUG */ + + return nssCKFWObject_GetAttributeCount(fwObject, pError); +} + +/* + * NSSCKFWObject_GetAttributeTypes + * + */ +NSS_IMPLEMENT CK_RV +NSSCKFWObject_GetAttributeTypes +( + NSSCKFWObject *fwObject, + CK_ATTRIBUTE_TYPE_PTR typeArray, + CK_ULONG ulCount +) +{ + CK_RV error = CKR_OK; + +#ifdef DEBUG + error = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != error ) { + return error; + } + + if( (CK_ATTRIBUTE_TYPE_PTR)NULL == typeArray ) { + return CKR_ARGUMENTS_BAD; + } +#endif /* DEBUG */ + + return nssCKFWObject_GetAttributeTypes(fwObject, typeArray, ulCount); +} + +/* + * NSSCKFWObject_GetAttributeSize + * + */ +NSS_IMPLEMENT CK_ULONG +NSSCKFWObject_GetAttributeSize +( + NSSCKFWObject *fwObject, + CK_ATTRIBUTE_TYPE attribute, + CK_RV *pError +) +{ +#ifdef DEBUG + if( (CK_RV *)NULL == pError ) { + return (CK_ULONG)0; + } + + *pError = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != *pError ) { + return (CK_ULONG)0; + } +#endif /* DEBUG */ + + return nssCKFWObject_GetAttributeSize(fwObject, attribute, pError); +} + +/* + * NSSCKFWObject_GetAttribute + * + */ +NSS_IMPLEMENT NSSItem * +NSSCKFWObject_GetAttribute +( + NSSCKFWObject *fwObject, + CK_ATTRIBUTE_TYPE attribute, + NSSItem *itemOpt, + NSSArena *arenaOpt, + CK_RV *pError +) +{ +#ifdef DEBUG + if( (CK_RV *)NULL == pError ) { + return (NSSItem *)NULL; + } + + *pError = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != *pError ) { + return (NSSItem *)NULL; + } +#endif /* DEBUG */ + + return nssCKFWObject_GetAttribute(fwObject, attribute, itemOpt, arenaOpt, pError); +} + +/* + * NSSCKFWObject_GetObjectSize + * + */ +NSS_IMPLEMENT CK_ULONG +NSSCKFWObject_GetObjectSize +( + NSSCKFWObject *fwObject, + CK_RV *pError +) +{ +#ifdef DEBUG + if( (CK_RV *)NULL == pError ) { + return (CK_ULONG)0; + } + + *pError = nssCKFWObject_verifyPointer(fwObject); + if( CKR_OK != *pError ) { + return (CK_ULONG)0; + } +#endif /* DEBUG */ + + return nssCKFWObject_GetObjectSize(fwObject, pError); +} diff --git a/security/nss/lib/pk11wrap/pk11kea.c b/security/nss/lib/pk11wrap/pk11kea.c new file mode 100644 index 000000000..6006f6032 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11kea.c @@ -0,0 +1,224 @@ +/* + * 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. + */ +/* + * This file implements the Symkey wrapper and the PKCS context + * Interfaces. + */ + +#include "seccomon.h" +#include "secmod.h" +#include "nssilock.h" +#include "secmodi.h" +#include "pkcs11.h" +#include "pk11func.h" +#include "secitem.h" +#include "key.h" +#include "secasn1.h" +#include "sechash.h" +#include "cert.h" +#include "secerr.h" + +/* + * find an RSA public key on a card + */ +static CK_OBJECT_HANDLE +pk11_FindRSAPubKey(PK11SlotInfo *slot) +{ + CK_KEY_TYPE key_type = CKK_RSA; + CK_OBJECT_CLASS class_type = CKO_PUBLIC_KEY; + CK_ATTRIBUTE theTemplate[2]; + int template_count = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_ATTRIBUTE *attrs = theTemplate; + + PK11_SETATTRS(attrs,CKA_CLASS,&class_type,sizeof(class_type)); attrs++; + PK11_SETATTRS(attrs,CKA_KEY_TYPE,&key_type,sizeof(key_type)); attrs++; + template_count = attrs - theTemplate; + PR_ASSERT(template_count <= sizeof(theTemplate)/sizeof(CK_ATTRIBUTE)); + + return pk11_FindObjectByTemplate(slot,theTemplate,template_count); +} + +SECKEYPublicKey *PK11_ExtractPublicKey(PK11SlotInfo *slot, KeyType keyType, + CK_OBJECT_HANDLE id); + +PK11SymKey * +pk11_KeyExchange(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, + CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey) +{ + PK11SymKey *newSymKey = NULL; + SECStatus rv; + /* performance improvement can go here --- use a generated key to as a + * per startup wrapping key. If it exists, use it, otherwise do a full + * key exchange. */ + + /* find a common Key Exchange algorithm */ + /* RSA */ + if (PK11_DoesMechanism(symKey->slot, CKM_RSA_PKCS) && + PK11_DoesMechanism(slot,CKM_RSA_PKCS)) { + CK_OBJECT_HANDLE pubKeyHandle = CK_INVALID_KEY; + CK_OBJECT_HANDLE privKeyHandle = CK_INVALID_KEY; + SECKEYPublicKey *pubKey = NULL; + SECKEYPrivateKey *privKey = NULL; + SECItem wrapData; + + wrapData.data = NULL; + + /* find RSA Public Key on target */ + pubKeyHandle = pk11_FindRSAPubKey(slot); + if (pubKeyHandle != CK_INVALID_KEY) { + privKeyHandle = PK11_MatchItem(slot,pubKeyHandle,CKO_PRIVATE_KEY); + } + + /* if no key exits, generate a key pair */ + if (privKeyHandle == CK_INVALID_KEY) { + unsigned int keyLength = PK11_GetKeyLength(symKey); + PK11RSAGenParams rsaParams; + + rsaParams.keySizeInBits = + ((keyLength == 0) || (keyLength > 16)) ? 512 : 256; + rsaParams.pe = 0x10001; + privKey = PK11_GenerateKeyPair(slot,CKM_RSA_PKCS_KEY_PAIR_GEN, + &rsaParams, &pubKey,PR_FALSE,PR_TRUE,symKey->cx); + } else { + /* if key's exist, build SECKEY data structures for them */ + privKey = PK11_MakePrivKey(slot,nullKey, PR_TRUE, privKeyHandle, + symKey->cx); + if (privKey != NULL) { + pubKey = PK11_ExtractPublicKey(slot, rsaKey, pubKeyHandle); + if (pubKey && pubKey->pkcs11Slot) { + PK11_FreeSlot(pubKey->pkcs11Slot); + pubKey->pkcs11Slot = NULL; + pubKey->pkcs11ID = CK_INVALID_KEY; + } + } + } + if (privKey == NULL) goto rsa_failed; + if (pubKey == NULL) goto rsa_failed; + + wrapData.len = SECKEY_PublicKeyStrength(pubKey); + if (!wrapData.len) goto rsa_failed; + wrapData.data = PORT_Alloc(wrapData.len); + if (wrapData.data == NULL) goto rsa_failed; + + /* now wrap the keys in and out */ + rv = PK11_PubWrapSymKey(CKM_RSA_PKCS, pubKey, symKey, &wrapData); + if (rv == SECSuccess) { + newSymKey = PK11_PubUnwrapSymKey(privKey,&wrapData,type,operation, + symKey->size); + } +rsa_failed: + if (wrapData.data != NULL) PORT_Free(wrapData.data); + if (privKey != NULL) SECKEY_DestroyPrivateKey(privKey); + if (pubKey != NULL) SECKEY_DestroyPublicKey(pubKey); + + return newSymKey; + } + /* KEA */ + if (PK11_DoesMechanism(symKey->slot, CKM_KEA_KEY_DERIVE) && + PK11_DoesMechanism(slot,CKM_KEA_KEY_DERIVE)) { + CERTCertificate *certSource = NULL; + CERTCertificate *certTarget = NULL; + SECKEYPublicKey *pubKeySource = NULL; + SECKEYPublicKey *pubKeyTarget = NULL; + SECKEYPrivateKey *privKeySource = NULL; + SECKEYPrivateKey *privKeyTarget = NULL; + PK11SymKey *tekSource = NULL; + PK11SymKey *tekTarget = NULL; + SECItem Ra,wrap; + + /* can only exchange skipjack keys */ + if (type != CKM_SKIPJACK_CBC64) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + goto kea_failed; + } + + /* find a pair of certs we can use */ + rv = PK11_GetKEAMatchedCerts(symKey->slot,slot,&certSource,&certTarget); + if (rv != SECSuccess) goto kea_failed; + + /* get all the key pairs */ + pubKeyTarget = CERT_ExtractPublicKey(certSource); + pubKeySource = CERT_ExtractPublicKey(certTarget); + privKeySource = + PK11_FindKeyByDERCert(symKey->slot,certSource,symKey->cx); + privKeyTarget = + PK11_FindKeyByDERCert(slot,certTarget,symKey->cx); + + if ((pubKeySource == NULL) || (pubKeyTarget == NULL) || + (privKeySource == NULL) || (privKeyTarget == NULL)) goto kea_failed; + + /* generate the wrapping TEK's */ + Ra.data = (unsigned char*)PORT_Alloc(128 /* FORTEZZA RA MAGIC */); + Ra.len = 128; + if (Ra.data == NULL) goto kea_failed; + + tekSource = PK11_PubDerive(privKeySource,pubKeyTarget,PR_TRUE,&Ra,NULL, + CKM_SKIPJACK_WRAP, CKM_KEA_KEY_DERIVE,CKA_WRAP,0,symKey->cx); + tekTarget = PK11_PubDerive(privKeyTarget,pubKeySource,PR_FALSE,&Ra,NULL, + CKM_SKIPJACK_WRAP, CKM_KEA_KEY_DERIVE,CKA_WRAP,0,symKey->cx); + PORT_Free(Ra.data); + + if ((tekSource == NULL) || (tekTarget == NULL)) { goto kea_failed; } + + /* wrap the key out of Source into target */ + wrap.data = (unsigned char*)PORT_Alloc(12); /* MAGIC SKIPJACK LEN */ + wrap.len = 12; + + /* paranoia to prevent infinite recursion on bugs */ + PORT_Assert(tekSource->slot == symKey->slot); + if (tekSource->slot != symKey->slot) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + goto kea_failed; + } + + rv = PK11_WrapSymKey(CKM_SKIPJACK_WRAP,NULL,tekSource,symKey,&wrap); + if (rv == SECSuccess) { + newSymKey = PK11_UnwrapSymKey(tekTarget, CKM_SKIPJACK_WRAP, NULL, + &wrap, type, operation, symKey->size); + } + PORT_Free(wrap.data); +kea_failed: + if (certSource == NULL) CERT_DestroyCertificate(certSource); + if (certTarget == NULL) CERT_DestroyCertificate(certTarget); + if (pubKeySource == NULL) SECKEY_DestroyPublicKey(pubKeySource); + if (pubKeyTarget == NULL) SECKEY_DestroyPublicKey(pubKeyTarget); + if (privKeySource == NULL) SECKEY_DestroyPrivateKey(privKeySource); + if (privKeyTarget == NULL) SECKEY_DestroyPrivateKey(privKeyTarget); + if (tekSource == NULL) PK11_FreeSymKey(tekSource); + if (tekTarget == NULL) PK11_FreeSymKey(tekTarget); + return newSymKey; + } + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; +} + diff --git a/security/nss/lib/pkcs12/p12d.c b/security/nss/lib/pkcs12/p12d.c new file mode 100644 index 000000000..9abae13c8 --- /dev/null +++ b/security/nss/lib/pkcs12/p12d.c @@ -0,0 +1,3230 @@ +/* + * 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. + */ + + +#include "nssrenam.h" +#include "p12t.h" +#include "p12.h" +#include "plarena.h" +#include "secitem.h" +#include "secoid.h" +#include "seccomon.h" +#include "secport.h" +#include "cert.h" +#include "secpkcs7.h" +#include "secasn1.h" +#include "secerr.h" +#include "pk11func.h" +#include "p12plcy.h" +#include "p12local.h" +#include "alghmac.h" +#include "secder.h" +#include "secport.h" + +#include "certdb.h" + +#include "prcpucfg.h" + +typedef struct sec_PKCS12SafeContentsContextStr sec_PKCS12SafeContentsContext; + +/* Opaque structure for decoding SafeContents. These are used + * for each authenticated safe as well as any nested safe contents. + */ +struct sec_PKCS12SafeContentsContextStr { + /* the parent decoder context */ + SEC_PKCS12DecoderContext *p12dcx; + + /* memory arena to allocate space from */ + PRArenaPool *arena; + + /* decoder context and destination for decoding safe contents */ + SEC_ASN1DecoderContext *safeContentsDcx; + sec_PKCS12SafeContents safeContents; + + /* information for decoding safe bags within the safe contents. + * these variables are updated for each safe bag decoded. + */ + SEC_ASN1DecoderContext *currentSafeBagDcx; + sec_PKCS12SafeBag *currentSafeBag; + PRBool skipCurrentSafeBag; + + /* if the safe contents is nested, the parent is pointed to here. */ + sec_PKCS12SafeContentsContext *nestedCtx; +}; + +/* opaque decoder context structure. information for decoding a pkcs 12 + * PDU are stored here as well as decoding pointers for intermediary + * structures which are part of the PKCS 12 PDU. Upon a successful + * decode, the safe bags containing certificates and keys encountered. + */ +struct SEC_PKCS12DecoderContextStr { + PRArenaPool *arena; + PK11SlotInfo *slot; + void *wincx; + PRBool error; + int errorValue; + + /* password */ + SECItem *pwitem; + + /* used for decoding the PFX structure */ + SEC_ASN1DecoderContext *pfxDcx; + sec_PKCS12PFXItem pfx; + + /* safe bags found during decoding */ + sec_PKCS12SafeBag **safeBags; + unsigned int safeBagCount; + + /* state variables for decoding authenticated safes. */ + SEC_PKCS7DecoderContext *currentASafeP7Dcx; + SEC_PKCS5KeyAndPassword *currentASafeKeyPwd; + SEC_ASN1DecoderContext *aSafeDcx; + SEC_PKCS7DecoderContext *aSafeP7Dcx; + sec_PKCS12AuthenticatedSafe authSafe; + SEC_PKCS7ContentInfo *aSafeCinfo; + sec_PKCS12SafeContents safeContents; + + /* safe contents info */ + unsigned int safeContentsCnt; + sec_PKCS12SafeContentsContext **safeContentsList; + + /* HMAC info */ + sec_PKCS12MacData macData; + SEC_ASN1DecoderContext *hmacDcx; + + /* routines for reading back the data to be hmac'd */ + digestOpenFn dOpen; + digestCloseFn dClose; + digestIOFn dRead, dWrite; + void *dArg; + + /* helper functions */ + SECKEYGetPasswordKey pwfn; + void *pwfnarg; + PRBool swapUnicodeBytes; + + /* import information */ + PRBool bagsVerified; +}; + + +/* make sure that the PFX version being decoded is a version + * which we support. + */ +static PRBool +sec_pkcs12_proper_version(sec_PKCS12PFXItem *pfx) +{ + /* if no version, assume it is not supported */ + if(pfx->version.len == 0) { + return PR_FALSE; + } + + if(DER_GetInteger(&pfx->version) > SEC_PKCS12_VERSION) { + return PR_FALSE; + } + + return PR_TRUE; +} + +/* retrieve the key for decrypting the safe contents */ +static PK11SymKey * +sec_pkcs12_decoder_get_decrypt_key(void *arg, SECAlgorithmID *algid) +{ + SEC_PKCS5KeyAndPassword *keyPwd = + (SEC_PKCS5KeyAndPassword *)arg; + + if(!keyPwd) { + return NULL; + } + + /* if no slot specified, use the internal key slot */ + if(!keyPwd->slot) { + keyPwd->slot = PK11_GetInternalKeySlot(); + } + + /* retrieve the key */ + if(!keyPwd->key) { + keyPwd->key = PK11_PBEKeyGen(keyPwd->slot, algid, + keyPwd->pwitem, PR_FALSE, keyPwd->wincx); + } + + return (PK11SymKey *)keyPwd; +} + +/* XXX this needs to be modified to handle enveloped data. most + * likely, it should mirror the routines for SMIME in that regard. + */ +static PRBool +sec_pkcs12_decoder_decryption_allowed(SECAlgorithmID *algid, + PK11SymKey *bulkkey) +{ + PRBool decryptionAllowed = SEC_PKCS12DecryptionAllowed(algid); + + if(!decryptionAllowed) { + return PR_FALSE; + } + + return PR_TRUE; +} + +/* when we encounter a new safe bag during the decoding, we need + * to allocate space for the bag to be decoded to and set the + * state variables appropriately. all of the safe bags are allocated + * in a buffer in the outer SEC_PKCS12DecoderContext, however, + * a pointer to the safeBag is also used in the sec_PKCS12SafeContentsContext + * for the current bag. + */ +static SECStatus +sec_pkcs12_decoder_init_new_safe_bag(sec_PKCS12SafeContentsContext + *safeContentsCtx) +{ + void *mark = NULL; + SEC_PKCS12DecoderContext *p12dcx; + + /* make sure that the structures are defined, and there has + * not been an error in the decoding + */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx + || safeContentsCtx->p12dcx->error) { + return SECFailure; + } + + p12dcx = safeContentsCtx->p12dcx; + mark = PORT_ArenaMark(p12dcx->arena); + + /* allocate a new safe bag, if bags already exist, grow the + * list of bags, otherwise allocate a new list. the list is + * NULL terminated. + */ + if(p12dcx->safeBagCount) { + p12dcx->safeBags = + (sec_PKCS12SafeBag**)PORT_ArenaGrow(p12dcx->arena,p12dcx->safeBags, + (p12dcx->safeBagCount + 1) * sizeof(sec_PKCS12SafeBag *), + (p12dcx->safeBagCount + 2) * sizeof(sec_PKCS12SafeBag *)); + } else { + p12dcx->safeBags = (sec_PKCS12SafeBag**)PORT_ArenaZAlloc(p12dcx->arena, + 2 * sizeof(sec_PKCS12SafeBag *)); + } + if(!p12dcx->safeBags) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* append the bag to the end of the list and update the reference + * in the safeContentsCtx. + */ + p12dcx->safeBags[p12dcx->safeBagCount] = + (sec_PKCS12SafeBag*)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12SafeBag)); + safeContentsCtx->currentSafeBag = p12dcx->safeBags[p12dcx->safeBagCount]; + p12dcx->safeBags[++p12dcx->safeBagCount] = NULL; + if(!safeContentsCtx->currentSafeBag) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + safeContentsCtx->currentSafeBag->slot = safeContentsCtx->p12dcx->slot; + safeContentsCtx->currentSafeBag->pwitem = safeContentsCtx->p12dcx->pwitem; + safeContentsCtx->currentSafeBag->swapUnicodeBytes = + safeContentsCtx->p12dcx->swapUnicodeBytes; + safeContentsCtx->currentSafeBag->arena = safeContentsCtx->p12dcx->arena; + + PORT_ArenaUnmark(p12dcx->arena, mark); + return SECSuccess; + +loser: + + /* if an error occurred, release the memory and set the error flag + * the only possible errors triggered by this function are memory + * related. + */ + if(mark) { + PORT_ArenaRelease(p12dcx->arena, mark); + } + + p12dcx->error = PR_TRUE; + return SECFailure; +} + +/* A wrapper for updating the ASN1 context in which a safeBag is + * being decoded. This function is called as a callback from + * secasn1d when decoding SafeContents structures. + */ +static void +sec_pkcs12_decoder_safe_bag_update(void *arg, const char *data, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + sec_PKCS12SafeContentsContext *safeContentsCtx = + (sec_PKCS12SafeContentsContext *)arg; + SEC_PKCS12DecoderContext *p12dcx; + SECStatus rv; + + /* make sure that we are not skipping the current safeBag, + * and that there are no errors. If so, just return rather + * than continuing to process. + */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx + || safeContentsCtx->p12dcx->error + || safeContentsCtx->skipCurrentSafeBag) { + return; + } + p12dcx = safeContentsCtx->p12dcx; + + rv = SEC_ASN1DecoderUpdate(safeContentsCtx->currentSafeBagDcx, data, len); + if(rv != SECSuccess) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + return; + +loser: + /* set the error, and finish the decoder context. because there + * is not a way of returning an error message, it may be worth + * while to do a check higher up and finish any decoding contexts + * that are still open. + */ + p12dcx->error = PR_TRUE; + SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagDcx); + safeContentsCtx->currentSafeBagDcx = NULL; + return; +} + +/* forward declarations of functions that are used when decoding + * safeContents bags which are nested and when decoding the + * authenticatedSafes. + */ +static SECStatus +sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext + *safeContentsCtx); +static SECStatus +sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext + *safeContentsCtx); +static void +sec_pkcs12_decoder_safe_bag_update(void *arg, const char *data, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind); + +/* notify function for decoding safeBags. This function is + * used to filter safeBag types which are not supported, + * initiate the decoding of nested safe contents, and decode + * safeBags in general. this function is set when the decoder + * context for the safeBag is first created. + */ +static void +sec_pkcs12_decoder_safe_bag_notify(void *arg, PRBool before, + void *dest, int real_depth) +{ + sec_PKCS12SafeContentsContext *safeContentsCtx = + (sec_PKCS12SafeContentsContext *)arg; + SEC_PKCS12DecoderContext *p12dcx; + sec_PKCS12SafeBag *bag; + PRBool after; + + /* if an error is encountered, return */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx || + safeContentsCtx->p12dcx->error) { + return; + } + p12dcx = safeContentsCtx->p12dcx; + + /* to make things more readable */ + if(before) + after = PR_FALSE; + else + after = PR_TRUE; + + /* have we determined the safeBagType yet? */ + bag = safeContentsCtx->currentSafeBag; + if(bag->bagTypeTag == NULL) { + if(after && (dest == &(bag->safeBagType))) { + bag->bagTypeTag = SECOID_FindOID(&(bag->safeBagType)); + if(bag->bagTypeTag == NULL) { + p12dcx->error = PR_TRUE; + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE; + } + } + return; + } + + /* process the safeBag depending on it's type. those + * which we do not support, are ignored. we start a decoding + * context for a nested safeContents. + */ + switch(bag->bagTypeTag->offset) { + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + break; + case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: + /* if we are just starting to decode the safeContents, initialize + * a new safeContentsCtx to process it. + */ + if(before && (dest == &(bag->safeBagContent))) { + sec_pkcs12_decoder_begin_nested_safe_contents(safeContentsCtx); + } else if(after && (dest == &(bag->safeBagContent))) { + /* clean up the nested decoding */ + sec_pkcs12_decoder_finish_nested_safe_contents(safeContentsCtx); + } + break; + case SEC_OID_PKCS12_V1_CRL_BAG_ID: + case SEC_OID_PKCS12_V1_SECRET_BAG_ID: + default: + /* skip any safe bag types we don't understand or handle */ + safeContentsCtx->skipCurrentSafeBag = PR_TRUE; + break; + } + + return; +} + +/* notify function for decoding safe contents. each entry in the + * safe contents is a safeBag which needs to be allocated and + * the decoding context initialized at the beginning and then + * the context needs to be closed and finished at the end. + * + * this function is set when the safeContents decode context is + * initialized. + */ +static void +sec_pkcs12_decoder_safe_contents_notify(void *arg, PRBool before, + void *dest, int real_depth) +{ + sec_PKCS12SafeContentsContext *safeContentsCtx = + (sec_PKCS12SafeContentsContext*)arg; + SEC_PKCS12DecoderContext *p12dcx; + SECStatus rv; + + /* if there is an error we don't want to continue processing, + * just return and keep going. + */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx + || safeContentsCtx->p12dcx->error) { + return; + } + p12dcx = safeContentsCtx->p12dcx; + + /* if we are done with the current safeBag, then we need to + * finish the context and set the state variables appropriately. + */ + if(!before) { + SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsDcx); + SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagDcx); + safeContentsCtx->currentSafeBagDcx = NULL; + safeContentsCtx->skipCurrentSafeBag = PR_FALSE; + } else { + /* we are starting a new safe bag. we need to allocate space + * for the bag and initialize the decoding context. + */ + rv = sec_pkcs12_decoder_init_new_safe_bag(safeContentsCtx); + if(rv != SECSuccess) { + goto loser; + } + + /* set up the decoder context */ + safeContentsCtx->currentSafeBagDcx = SEC_ASN1DecoderStart(p12dcx->arena, + safeContentsCtx->currentSafeBag, + sec_PKCS12SafeBagTemplate); + if(!safeContentsCtx->currentSafeBagDcx) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* set the notify and filter procs so that the safe bag + * data gets sent to the proper location when decoding. + */ + SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->currentSafeBagDcx, + sec_pkcs12_decoder_safe_bag_notify, + safeContentsCtx); + SEC_ASN1DecoderSetFilterProc(safeContentsCtx->safeContentsDcx, + sec_pkcs12_decoder_safe_bag_update, + safeContentsCtx, PR_TRUE); + } + + return; + +loser: + /* in the event of an error, we want to close the decoding + * context and clear the filter and notify procedures. + */ + p12dcx->error = PR_TRUE; + + if(safeContentsCtx->currentSafeBagDcx) { + SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagDcx); + safeContentsCtx->currentSafeBagDcx = NULL; + } + + SEC_ASN1DecoderClearNotifyProc(safeContentsCtx->safeContentsDcx); + SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsDcx); + + return; +} + +/* initialize the safeContents for decoding. this routine + * is used for authenticatedSafes as well as nested safeContents. + */ +static sec_PKCS12SafeContentsContext * +sec_pkcs12_decoder_safe_contents_init_decode(SEC_PKCS12DecoderContext *p12dcx, + PRBool nestedSafe) +{ + sec_PKCS12SafeContentsContext *safeContentsCtx = NULL; + const SEC_ASN1Template *theTemplate; + + if(!p12dcx || p12dcx->error) { + return NULL; + } + + /* allocate a new safeContents list or grow the existing list and + * append the new safeContents onto the end. + */ + if(!p12dcx->safeContentsCnt) { + p12dcx->safeContentsList = + (sec_PKCS12SafeContentsContext**)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12SafeContentsContext *)); + } else { + p12dcx->safeContentsList = + (sec_PKCS12SafeContentsContext **) PORT_ArenaGrow(p12dcx->arena, + p12dcx->safeContentsList, + (p12dcx->safeContentsCnt * + sizeof(sec_PKCS12SafeContentsContext *)), + (1 + p12dcx->safeContentsCnt * + sizeof(sec_PKCS12SafeContentsContext *))); + } + if(!p12dcx->safeContentsList) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + p12dcx->safeContentsList[p12dcx->safeContentsCnt] = + (sec_PKCS12SafeContentsContext*)PORT_ArenaZAlloc( + p12dcx->arena, + sizeof(sec_PKCS12SafeContentsContext)); + p12dcx->safeContentsList[p12dcx->safeContentsCnt+1] = NULL; + if(!p12dcx->safeContentsList[p12dcx->safeContentsCnt]) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* set up the state variables */ + safeContentsCtx = p12dcx->safeContentsList[p12dcx->safeContentsCnt]; + p12dcx->safeContentsCnt++; + safeContentsCtx->p12dcx = p12dcx; + safeContentsCtx->arena = p12dcx->arena; + + /* begin the decoding -- the template is based on whether we are + * decoding a nested safeContents or not. + */ + if(nestedSafe == PR_TRUE) { + theTemplate = sec_PKCS12NestedSafeContentsDecodeTemplate; + } else { + theTemplate = sec_PKCS12SafeContentsDecodeTemplate; + } + + /* start the decoder context */ + safeContentsCtx->safeContentsDcx = SEC_ASN1DecoderStart(p12dcx->arena, + &safeContentsCtx->safeContents, + theTemplate); + + if(!safeContentsCtx->safeContentsDcx) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* set the safeContents notify procedure to look for + * and start the decode of safeBags. + */ + SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->safeContentsDcx, + sec_pkcs12_decoder_safe_contents_notify, + safeContentsCtx); + + return safeContentsCtx; + +loser: + /* in the case of an error, we want to finish the decoder + * context and set the error flag. + */ + if(safeContentsCtx && safeContentsCtx->safeContentsDcx) { + SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsDcx); + safeContentsCtx->safeContentsDcx = NULL; + } + + p12dcx->error = PR_TRUE; + + return NULL; +} + +/* wrapper for updating safeContents. this is set as the filter of + * safeBag when there is a nested safeContents. + */ +static void +sec_pkcs12_decoder_nested_safe_contents_update(void *arg, const char *buf, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + sec_PKCS12SafeContentsContext *safeContentsCtx = + (sec_PKCS12SafeContentsContext *)arg; + SEC_PKCS12DecoderContext *p12dcx; + SECStatus rv; + + /* check for an error */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx + || safeContentsCtx->p12dcx->error) { + return; + } + + /* no need to update if no data sent in */ + if(!len || !buf) { + return; + } + + /* update the decoding context */ + p12dcx = safeContentsCtx->p12dcx; + rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsDcx, buf, len); + if(rv != SECSuccess) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + return; + +loser: + /* handle any errors. If a decoding context is open, close it. */ + p12dcx->error = PR_TRUE; + if(safeContentsCtx->safeContentsDcx) { + SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsDcx); + safeContentsCtx->safeContentsDcx = NULL; + } +} + +/* whenever a new safeContentsSafeBag is encountered, we need + * to init a safeContentsContext. + */ +static SECStatus +sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext + *safeContentsCtx) +{ + /* check for an error */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx || + safeContentsCtx->p12dcx->error) { + return SECFailure; + } + + safeContentsCtx->nestedCtx = sec_pkcs12_decoder_safe_contents_init_decode( + safeContentsCtx->p12dcx, + PR_TRUE); + if(!safeContentsCtx->nestedCtx) { + return SECFailure; + } + + /* set up new filter proc */ + SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->nestedCtx->safeContentsDcx, + sec_pkcs12_decoder_safe_contents_notify, + safeContentsCtx->nestedCtx); + SEC_ASN1DecoderSetFilterProc(safeContentsCtx->currentSafeBagDcx, + sec_pkcs12_decoder_nested_safe_contents_update, + safeContentsCtx->nestedCtx, PR_TRUE); + + return SECSuccess; +} + +/* when the safeContents is done decoding, we need to reset the + * proper filter and notify procs and close the decoding context + */ +static SECStatus +sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext + *safeContentsCtx) +{ + /* check for error */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx || + safeContentsCtx->p12dcx->error) { + return SECFailure; + } + + /* clean up */ + SEC_ASN1DecoderClearFilterProc(safeContentsCtx->currentSafeBagDcx); + SEC_ASN1DecoderClearNotifyProc(safeContentsCtx->nestedCtx->safeContentsDcx); + SEC_ASN1DecoderFinish(safeContentsCtx->nestedCtx->safeContentsDcx); + safeContentsCtx->nestedCtx->safeContentsDcx = NULL; + safeContentsCtx->nestedCtx = NULL; + + return SECSuccess; +} + +/* wrapper for updating safeContents. This is used when decoding + * the nested safeContents and any authenticatedSafes. + */ +static void +sec_pkcs12_decoder_safe_contents_callback(void *arg, const char *buf, + unsigned long len) +{ + SECStatus rv; + sec_PKCS12SafeContentsContext *safeContentsCtx = + (sec_PKCS12SafeContentsContext *)arg; + SEC_PKCS12DecoderContext *p12dcx; + + /* check for error */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx + || safeContentsCtx->p12dcx->error) { + return; + } + p12dcx = safeContentsCtx->p12dcx; + + /* update the decoder */ + rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsDcx, buf, len); + if(rv != SECSuccess) { + /* if we fail while trying to decode a 'safe', it's probably because + * we didn't have the correct password. */ + PORT_SetError(SEC_ERROR_BAD_PASSWORD); + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE; + goto loser; + } + + return; + +loser: + /* set the error and finish the context */ + p12dcx->error = PR_TRUE; + if(safeContentsCtx->safeContentsDcx) { + SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsDcx); + safeContentsCtx->safeContentsDcx = NULL; + } + + return; +} + +/* this is a wrapper for the ASN1 decoder to call SEC_PKCS7DecoderUpdate + */ +static void +sec_pkcs12_decoder_wrap_p7_update(void *arg, const char *data, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + SEC_PKCS7DecoderContext *p7dcx = (SEC_PKCS7DecoderContext *)arg; + + SEC_PKCS7DecoderUpdate(p7dcx, data, len); +} + +/* notify function for decoding aSafes. at the beginning, + * of an authenticatedSafe, we start a decode of a safeContents. + * at the end, we clean up the safeContents decoder context and + * reset state variables + */ +static void +sec_pkcs12_decoder_asafes_notify(void *arg, PRBool before, void *dest, + int real_depth) +{ + SEC_PKCS12DecoderContext *p12dcx; + sec_PKCS12SafeContentsContext *safeContentsCtx; + + /* make sure no error occurred. */ + p12dcx = (SEC_PKCS12DecoderContext *)arg; + if(!p12dcx || p12dcx->error) { + return; + } + + if(before) { + + /* init a new safeContentsContext */ + safeContentsCtx = sec_pkcs12_decoder_safe_contents_init_decode(p12dcx, + PR_FALSE); + if(!safeContentsCtx) { + goto loser; + } + + /* set up password and encryption key information */ + p12dcx->currentASafeKeyPwd = + (SEC_PKCS5KeyAndPassword*)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(SEC_PKCS5KeyAndPassword)); + if(!p12dcx->currentASafeKeyPwd) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + p12dcx->currentASafeKeyPwd->pwitem = p12dcx->pwitem; + p12dcx->currentASafeKeyPwd->slot = p12dcx->slot; + p12dcx->currentASafeKeyPwd->wincx = p12dcx->wincx; + + /* initiate the PKCS7ContentInfo decode */ + p12dcx->currentASafeP7Dcx = SEC_PKCS7DecoderStart( + sec_pkcs12_decoder_safe_contents_callback, + safeContentsCtx, + p12dcx->pwfn, p12dcx->pwfnarg, + sec_pkcs12_decoder_get_decrypt_key, + p12dcx->currentASafeKeyPwd, + sec_pkcs12_decoder_decryption_allowed); + if(!p12dcx->currentASafeP7Dcx) { + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + SEC_ASN1DecoderSetFilterProc(p12dcx->aSafeDcx, + sec_pkcs12_decoder_wrap_p7_update, + p12dcx->currentASafeP7Dcx, PR_TRUE); + } + + if(!before) { + /* if one is being decoded, finish the decode */ + if(p12dcx->currentASafeP7Dcx != NULL) { + if(!SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx)) { + p12dcx->currentASafeP7Dcx = NULL; + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + p12dcx->currentASafeP7Dcx = NULL; + } + p12dcx->currentASafeP7Dcx = NULL; + if(p12dcx->currentASafeKeyPwd->key != NULL) { + p12dcx->currentASafeKeyPwd->key = NULL; + } + } + + + return; + +loser: + /* set the error flag */ + p12dcx->error = PR_TRUE; + return; +} + +/* wrapper for updating asafes decoding context. this function + * writes data being decoded to disk, so that a mac can be computed + * later. + */ +static void +sec_pkcs12_decoder_asafes_callback(void *arg, const char *buf, + unsigned long len) +{ + SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext *)arg; + SECStatus rv; + + if(!p12dcx || p12dcx->error) { + return; + } + + /* update the context */ + rv = SEC_ASN1DecoderUpdate(p12dcx->aSafeDcx, buf, len); + if(rv != SECSuccess) { + p12dcx->error = (PRBool)SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* if we are writing to a file, write out the new information */ + if(p12dcx->dWrite) { + unsigned long writeLen = (*p12dcx->dWrite)(p12dcx->dArg, + (unsigned char *)buf, len); + if(writeLen != len) { + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + } + + return; + +loser: + /* set the error flag */ + p12dcx->error = PR_TRUE; + SEC_ASN1DecoderFinish(p12dcx->aSafeDcx); + p12dcx->aSafeDcx = NULL; + + return; +} + +/* start the decode of an authenticatedSafe contentInfo. + */ +static SECStatus +sec_pkcs12_decode_start_asafes_cinfo(SEC_PKCS12DecoderContext *p12dcx) +{ + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + /* start the decode context */ + p12dcx->aSafeDcx = SEC_ASN1DecoderStart(p12dcx->arena, + &p12dcx->authSafe, + sec_PKCS12AuthenticatedSafeTemplate); + if(!p12dcx->aSafeDcx) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* set the notify function */ + SEC_ASN1DecoderSetNotifyProc(p12dcx->aSafeDcx, + sec_pkcs12_decoder_asafes_notify, p12dcx); + + /* begin the authSafe decoder context */ + p12dcx->aSafeP7Dcx = SEC_PKCS7DecoderStart( + sec_pkcs12_decoder_asafes_callback, p12dcx, + p12dcx->pwfn, p12dcx->pwfnarg, NULL, NULL, NULL); + if(!p12dcx->aSafeP7Dcx) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* open the temp file for writing, if the filter functions were set */ + if(p12dcx->dOpen && (*p12dcx->dOpen)(p12dcx->dArg, PR_FALSE) + != SECSuccess) { + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + + return SECSuccess; + +loser: + p12dcx->error = PR_TRUE; + + if(p12dcx->aSafeDcx) { + SEC_ASN1DecoderFinish(p12dcx->aSafeDcx); + p12dcx->aSafeDcx = NULL; + } + + if(p12dcx->aSafeP7Dcx) { + SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx); + p12dcx->aSafeP7Dcx = NULL; + } + + return SECFailure; +} + +/* wrapper for updating the safeContents. this function is used as + * a filter for the pfx when decoding the authenticated safes + */ +static void +sec_pkcs12_decode_asafes_cinfo_update(void *arg, const char *buf, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + SEC_PKCS12DecoderContext *p12dcx; + SECStatus rv; + + p12dcx = (SEC_PKCS12DecoderContext*)arg; + if(!p12dcx || p12dcx->error) { + return; + } + + /* update the safeContents decoder */ + rv = SEC_PKCS7DecoderUpdate(p12dcx->aSafeP7Dcx, buf, len); + if(rv != SECSuccess) { + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE; + goto loser; + } + + return; + +loser: + + /* did we find an error? if so, close the context and set the + * error flag. + */ + SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx); + p12dcx->aSafeP7Dcx = NULL; + p12dcx->error = PR_TRUE; +} + +/* notify procedure used while decoding the pfx. When we encounter + * the authSafes, we want to trigger the decoding of authSafes as well + * as when we encounter the macData, trigger the decoding of it. we do + * this because we we are streaming the decoder and not decoding in place. + * the pfx which is the destination, only has the version decoded into it. + */ +static void +sec_pkcs12_decoder_pfx_notify_proc(void *arg, PRBool before, void *dest, + int real_depth) +{ + SECStatus rv; + SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext*)arg; + + /* if an error occurrs, clear the notifyProc and the filterProc + * and continue. + */ + if(p12dcx->error) { + SEC_ASN1DecoderClearNotifyProc(p12dcx->pfxDcx); + SEC_ASN1DecoderClearFilterProc(p12dcx->pfxDcx); + return; + } + + if(before && (dest == &p12dcx->pfx.encodedAuthSafe)) { + + /* we want to make sure this is a version we support */ + if(!sec_pkcs12_proper_version(&p12dcx->pfx)) { + p12dcx->errorValue = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION; + goto loser; + } + + /* start the decode of the aSafes cinfo... */ + rv = sec_pkcs12_decode_start_asafes_cinfo(p12dcx); + if(rv != SECSuccess) { + goto loser; + } + + /* set the filter proc to update the authenticated safes. */ + SEC_ASN1DecoderSetFilterProc(p12dcx->pfxDcx, + sec_pkcs12_decode_asafes_cinfo_update, + p12dcx, PR_TRUE); + } + + if(!before && (dest == &p12dcx->pfx.encodedAuthSafe)) { + + /* we are done decoding the authenticatedSafes, so we need to + * finish the decoderContext and clear the filter proc + * and close the hmac callback, if present + */ + p12dcx->aSafeCinfo = SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx); + p12dcx->aSafeP7Dcx = NULL; + if(!p12dcx->aSafeCinfo) { + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + SEC_ASN1DecoderClearFilterProc(p12dcx->pfxDcx); + if(p12dcx->dClose && ((*p12dcx->dClose)(p12dcx->dArg, PR_FALSE) + != SECSuccess)) { + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + + } + + return; + +loser: + p12dcx->error = PR_TRUE; +} + +/* SEC_PKCS12DecoderStart + * Creates a decoder context for decoding a PKCS 12 PDU objct. + * This function sets up the initial decoding context for the + * PFX and sets the needed state variables. + * + * pwitem - the password for the hMac and any encoded safes. + * this should be changed to take a callback which retrieves + * the password. it may be possible for different safes to + * have different passwords. also, the password is already + * in unicode. it should probably be converted down below via + * a unicode conversion callback. + * slot - the slot to import the dataa into should multiple slots + * be supported based on key type and cert type? + * dOpen, dClose, dRead, dWrite - digest routines for writing data + * to a file so it could be read back and the hmack recomputed + * and verified. doesn't seem to be away for both encoding + * and decoding to be single pass, thus the need for these + * routines. + * dArg - the argument for dOpen, etc. + * + * This function returns the decoder context, if it was successful. + * Otherwise, null is returned. + */ +SEC_PKCS12DecoderContext * +SEC_PKCS12DecoderStart(SECItem *pwitem, PK11SlotInfo *slot, void *wincx, + digestOpenFn dOpen, digestCloseFn dClose, + digestIOFn dRead, digestIOFn dWrite, void *dArg) +{ + SEC_PKCS12DecoderContext *p12dcx; + PRArenaPool *arena; + + arena = PORT_NewArena(2048); /* different size? */ + if(!arena) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + /* allocate the decoder context and set the state variables */ + p12dcx = (SEC_PKCS12DecoderContext*)PORT_ArenaZAlloc(arena, sizeof(SEC_PKCS12DecoderContext)); + if(!p12dcx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + p12dcx->arena = arena; + p12dcx->pwitem = pwitem; + p12dcx->slot = (slot ? slot : PK11_GetInternalKeySlot()); + p12dcx->wincx = wincx; +#ifdef IS_LITTLE_ENDIAN + p12dcx->swapUnicodeBytes = PR_TRUE; +#else + p12dcx->swapUnicodeBytes = PR_FALSE; +#endif + p12dcx->errorValue = 0; + p12dcx->error = PR_FALSE; + + /* a slot is *required */ + if(!slot) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* start the decoding of the PFX and set the notify proc + * for the PFX item. + */ + p12dcx->pfxDcx = SEC_ASN1DecoderStart(p12dcx->arena, &p12dcx->pfx, + sec_PKCS12PFXItemTemplate); + if(!p12dcx->pfxDcx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + SEC_ASN1DecoderSetNotifyProc(p12dcx->pfxDcx, + sec_pkcs12_decoder_pfx_notify_proc, + p12dcx); + + /* set up digest functions */ + p12dcx->dOpen = dOpen; + p12dcx->dWrite = dWrite; + p12dcx->dClose = dClose; + p12dcx->dRead = dRead; + p12dcx->dArg = dArg; + + return p12dcx; + +loser: + PORT_FreeArena(arena, PR_TRUE); + return NULL; +} + +/* SEC_PKCS12DecoderUpdate + * Streaming update sending more data to the decoder. If + * an error occurs, SECFailure is returned. + * + * p12dcx - the decoder context + * data, len - the data buffer and length of data to send to + * the update functions. + */ +SECStatus +SEC_PKCS12DecoderUpdate(SEC_PKCS12DecoderContext *p12dcx, + unsigned char *data, unsigned long len) +{ + SECStatus rv; + + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + /* update the PFX decoder context */ + rv = SEC_ASN1DecoderUpdate(p12dcx->pfxDcx, (const char *)data, len); + if(rv != SECSuccess) { + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE; + goto loser; + } + + return SECSuccess; + +loser: + + p12dcx->error = PR_TRUE; + return SECFailure; +} + +/* IN_BUF_LEN should be larger than SHA1_LENGTH */ +#define IN_BUF_LEN 80 + +/* verify the hmac by reading the data from the temporary file + * using the routines specified when the decodingContext was + * created and return SECSuccess if the hmac matches. + */ +static SECStatus +sec_pkcs12_decoder_verify_mac(SEC_PKCS12DecoderContext *p12dcx) +{ + SECStatus rv = SECFailure; + PBEBitGenContext *pbeCtxt = NULL; + SECItem *hmacKey = NULL, hmacRes; + unsigned char buf[IN_BUF_LEN]; + unsigned int bufLen; + int iteration; + PK11Context *pk11cx; + SECOidTag algtag; + SECItem ignore = {0}; + + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + /* generate hmac key */ + if(p12dcx->macData.iter.data) { + iteration = (int)DER_GetInteger(&p12dcx->macData.iter); + } else { + iteration = 1; + } + pbeCtxt = PBE_CreateContext(SECOID_GetAlgorithmTag( + &p12dcx->macData.safeMac.digestAlgorithm), + pbeBitGenIntegrityKey, p12dcx->pwitem, + &p12dcx->macData.macSalt, 160, iteration); + if(!pbeCtxt) { + return SECFailure; + } + hmacKey = PBE_GenerateBits(pbeCtxt); + PBE_DestroyContext(pbeCtxt); + pbeCtxt = NULL; + if(!hmacKey) { + return SECFailure; + } + + /* init hmac */ + algtag = SECOID_GetAlgorithmTag(&p12dcx->macData.safeMac.digestAlgorithm); + pk11cx = PK11_CreateContextByRawKey(NULL, + sec_pkcs12_algtag_to_mech(algtag), + PK11_OriginDerive, CKA_SIGN, + hmacKey, &ignore, NULL); + SECITEM_ZfreeItem(hmacKey, PR_TRUE); + hmacKey = NULL; + if(!pk11cx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + rv = PK11_DigestBegin(pk11cx); + + /* try to open the data for readback */ + if(p12dcx->dOpen && ((*p12dcx->dOpen)(p12dcx->dArg, PR_TRUE) + != SECSuccess)) { + goto loser; + } + + /* read the data back IN_BUF_LEN bytes at a time and recompute + * the hmac. if fewer bytes are read than are requested, it is + * assumed that the end of file has been reached. if bytesRead + * is returned as -1, then an error occured reading from the + * file. + */ + while(1) { + int bytesRead = (*p12dcx->dRead)(p12dcx->dArg, buf, IN_BUF_LEN); + if(bytesRead == -1) { + goto loser; + } + + rv = PK11_DigestOp(pk11cx, buf, bytesRead); + if(bytesRead < IN_BUF_LEN) { + break; + } + } + + /* finish the hmac context */ + rv = PK11_DigestFinal(pk11cx, buf, &bufLen, IN_BUF_LEN); + + hmacRes.data = buf; + hmacRes.len = bufLen; + + /* is the hmac computed the same as the hmac which was decoded? */ + rv = SECSuccess; + if(SECITEM_CompareItem(&hmacRes, &p12dcx->macData.safeMac.digest) + != SECEqual) { + PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC); + rv = SECFailure; + } + +loser: + /* close the file and remove it */ + if(p12dcx->dClose) { + (*p12dcx->dClose)(p12dcx->dArg, PR_TRUE); + } + + if(pk11cx) { + PK11_DestroyContext(pk11cx, PR_TRUE); + } + + if(hmacKey) { + SECITEM_ZfreeItem(hmacKey, PR_TRUE); + } + + return rv; +} + +/* SEC_PKCS12DecoderVerify + * Verify the macData or the signature of the decoded PKCS 12 PDU. + * If the signature or the macData do not match, SECFailure is + * returned. + * + * p12dcx - the decoder context + */ +SECStatus +SEC_PKCS12DecoderVerify(SEC_PKCS12DecoderContext *p12dcx) +{ + SECStatus rv = SECSuccess; + + /* make sure that no errors have occured... */ + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + /* check the signature or the mac depending on the type of + * integrity used. + */ + if(p12dcx->pfx.encodedMacData.len) { + rv = SEC_ASN1DecodeItem(p12dcx->arena, &p12dcx->macData, + sec_PKCS12MacDataTemplate, + &p12dcx->pfx.encodedMacData); + if(rv == SECSuccess) { + return sec_pkcs12_decoder_verify_mac(p12dcx); + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } else { + if(SEC_PKCS7VerifySignature(p12dcx->aSafeCinfo, certUsageEmailSigner, + PR_FALSE)) { + return SECSuccess; + } else { + PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC); + } + } + + return SECFailure; +} + +/* SEC_PKCS12DecoderFinish + * Free any open ASN1 or PKCS7 decoder contexts and then + * free the arena pool which everything should be allocated + * from. This function should be called upon completion of + * decoding and installing of a pfx pdu. This should be + * called even if an error occurs. + * + * p12dcx - the decoder context + */ +void +SEC_PKCS12DecoderFinish(SEC_PKCS12DecoderContext *p12dcx) +{ + void *freedCtxt = NULL; + + if(!p12dcx) { + return; + } + + if(p12dcx->pfxDcx) { + SEC_ASN1DecoderFinish(p12dcx->pfxDcx); + p12dcx->pfxDcx = NULL; + } + + if(p12dcx->aSafeDcx) { + SEC_ASN1DecoderFinish(p12dcx->aSafeDcx); + p12dcx->aSafeDcx = NULL; + } + + if(p12dcx->currentASafeP7Dcx) { + SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx); + p12dcx->currentASafeP7Dcx = NULL; + } + + if(p12dcx->aSafeP7Dcx) { + SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx); + } + + if(p12dcx->hmacDcx) { + SEC_ASN1DecoderFinish(p12dcx->hmacDcx); + p12dcx->hmacDcx = NULL; + } + + if(p12dcx->arena) { + PORT_FreeArena(p12dcx->arena, PR_TRUE); + } +} + +static SECStatus +sec_pkcs12_decoder_set_attribute_value(sec_PKCS12SafeBag *bag, + SECOidTag attributeType, + SECItem *attrValue) +{ + int i = 0; + SECOidData *oid; + + if(!bag || !attrValue) { + return SECFailure; + } + + oid = SECOID_FindOIDByTag(attributeType); + if(!oid) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + if(!bag->attribs) { + bag->attribs = (sec_PKCS12Attribute**)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12Attribute *) * 2); + } else { + while(bag->attribs[i]) i++; + bag->attribs = (sec_PKCS12Attribute **)PORT_ArenaGrow(bag->arena, + bag->attribs, + (i + 1) * sizeof(sec_PKCS12Attribute *), + (i + 2) * sizeof(sec_PKCS12Attribute *)); + } + + if(!bag->attribs) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + bag->attribs[i] = (sec_PKCS12Attribute*)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12Attribute)); + if(!bag->attribs) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + bag->attribs[i]->attrValue = (SECItem**)PORT_ArenaZAlloc(bag->arena, + sizeof(SECItem *) * 2); + if(!bag->attribs[i]->attrValue) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + bag->attribs[i+1] = NULL; + bag->attribs[i]->attrValue[0] = attrValue; + bag->attribs[i]->attrValue[1] = NULL; + + if(SECITEM_CopyItem(bag->arena, &bag->attribs[i]->attrType, &oid->oid) + != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + return SECSuccess; +} + +static SECItem * +sec_pkcs12_get_attribute_value(sec_PKCS12SafeBag *bag, + SECOidTag attributeType) +{ + int i = 0; + + if(!bag->attribs) { + return NULL; + } + + while(bag->attribs[i] != NULL) { + if(SECOID_FindOIDTag(&bag->attribs[i]->attrType) + == attributeType) { + return bag->attribs[i]->attrValue[0]; + } + i++; + } + + return NULL; +} + +/* For now, this function will merely remove any ":" + * in the nickname which the PK11 functions may have + * placed there. This will keep dual certs from appearing + * twice under "Your" certificates when imported onto smart + * cards. Once with the name "Slot:Cert" and another with + * the nickname "Slot:Slot:Cert" + */ +static void +sec_pkcs12_sanitize_nickname(PK11SlotInfo *slot, SECItem *nick) +{ + char *nickname; + char *delimit; + int delimitlen; + + nickname = (char*)nick->data; /*Mac breaks without this type cast*/ + if ((delimit = PORT_Strchr(nickname, ':')) != NULL) { + char *slotName; + int slotNameLen; + + slotNameLen = delimit-nickname; + slotName = PORT_NewArray(char, (slotNameLen+1)); + PORT_Assert(slotName); + if (slotName == NULL) { + /* What else can we do?*/ + return; + } + PORT_Memcpy(slotName, nickname, slotNameLen); + slotName[slotNameLen] = '\0'; + if (PORT_Strcmp(PK11_GetTokenName(slot), slotName) == 0) { + delimitlen = PORT_Strlen(delimit+1); + PORT_Memmove(nickname, delimit+1, delimitlen+1); + nick->len = delimitlen; + } + PORT_Free(slotName); + } + +} + +static SECItem * +sec_pkcs12_get_nickname(sec_PKCS12SafeBag *bag) +{ + SECItem *src, *dest; + + if(!bag) { + bag->problem = PR_TRUE; + bag->error = SEC_ERROR_NO_MEMORY; + return NULL; + } + + src = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_FRIENDLY_NAME); + if(!src) { + return NULL; + } + + dest = (SECItem*)PORT_ZAlloc(sizeof(SECItem)); + if(!dest) { + goto loser; + } + if(!sec_pkcs12_convert_item_to_unicode(NULL, dest, src, PR_FALSE, + PR_FALSE, PR_FALSE)) { + goto loser; + } + + sec_pkcs12_sanitize_nickname(bag->slot, dest); + + return dest; + +loser: + if(dest) { + SECITEM_ZfreeItem(dest, PR_TRUE); + } + + bag->problem = PR_TRUE; + bag->error = PORT_GetError(); + return NULL; +} + +static SECStatus +sec_pkcs12_set_nickname(sec_PKCS12SafeBag *bag, SECItem *name) +{ + int i = 0; + sec_PKCS12Attribute *attr = NULL; + SECOidData *oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_FRIENDLY_NAME); + + if(!bag || !bag->arena || !name) { + return SECFailure; + } + + if(!bag->attribs) { + if(!oid) { + goto loser; + } + + bag->attribs = (sec_PKCS12Attribute**)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12Attribute *)*2); + if(!bag->attribs) { + goto loser; + } + bag->attribs[0] = (sec_PKCS12Attribute*)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12Attribute)); + if(!bag->attribs[0]) { + goto loser; + } + bag->attribs[1] = NULL; + + attr = bag->attribs[0]; + if(SECITEM_CopyItem(bag->arena, &attr->attrType, &oid->oid) + != SECSuccess) { + goto loser; + } + } else { + while(bag->attribs[i]) { + if(SECOID_FindOIDTag(&bag->attribs[i]->attrType) + == SEC_OID_PKCS9_FRIENDLY_NAME) { + attr = bag->attribs[i]; + goto have_attrib; + + } + i++; + } + if(!attr) { + bag->attribs = (sec_PKCS12Attribute **)PORT_ArenaGrow(bag->arena, + bag->attribs, + (i+1) * sizeof(sec_PKCS12Attribute *), + (i+2) * sizeof(sec_PKCS12Attribute *)); + if(!bag->attribs) { + goto loser; + } + bag->attribs[i] = + (sec_PKCS12Attribute *)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12Attribute)); + if(!bag->attribs[i]) { + goto loser; + } + bag->attribs[i+1] = NULL; + attr = bag->attribs[i]; + if(SECITEM_CopyItem(bag->arena, &attr->attrType, &oid->oid) + != SECSuccess) { + goto loser; + } + } + } +have_attrib: + PORT_Assert(attr); + if(!attr->attrValue) { + attr->attrValue = (SECItem **)PORT_ArenaZAlloc(bag->arena, + sizeof(SECItem *) * 2); + if(!attr->attrValue) { + goto loser; + } + attr->attrValue[0] = (SECItem*)PORT_ArenaZAlloc(bag->arena, + sizeof(SECItem)); + if(!attr->attrValue[0]) { + goto loser; + } + attr->attrValue[1] = NULL; + } + + name->len = PORT_Strlen((char *)name->data); + if(!sec_pkcs12_convert_item_to_unicode(bag->arena, attr->attrValue[0], + name, PR_FALSE, PR_FALSE, PR_TRUE)) { + goto loser; + } + + return SECSuccess; + +loser: + bag->problem = PR_TRUE; + bag->error = SEC_ERROR_NO_MEMORY; + return SECFailure; +} + +static SECStatus +sec_pkcs12_get_key_info(sec_PKCS12SafeBag *key) +{ + int i = 0; + SECKEYPrivateKeyInfo *pki = NULL; + + if(!key) { + return SECFailure; + } + + /* if the bag does *not* contain an unencrypted PrivateKeyInfo + * then we cannot convert the attributes. We are propagating + * attributes within the PrivateKeyInfo to the SafeBag level. + */ + if(SECOID_FindOIDTag(&(key->safeBagType)) != + SEC_OID_PKCS12_V1_KEY_BAG_ID) { + return SECSuccess; + } + + pki = key->safeBagContent.pkcs8KeyBag; + + if(!pki || !pki->attributes) { + return SECSuccess; + } + + while(pki->attributes[i]) { + SECItem *attrValue = NULL; + + if(SECOID_FindOIDTag(&pki->attributes[i]->attrType) == + SEC_OID_PKCS9_LOCAL_KEY_ID) { + attrValue = sec_pkcs12_get_attribute_value(key, + SEC_OID_PKCS9_LOCAL_KEY_ID); + if(!attrValue) { + if(sec_pkcs12_decoder_set_attribute_value(key, + SEC_OID_PKCS9_LOCAL_KEY_ID, + pki->attributes[i]->attrValue[0]) + != SECSuccess) { + key->problem = PR_TRUE; + key->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + } + } + + if(SECOID_FindOIDTag(&pki->attributes[i]->attrType) == + SEC_OID_PKCS9_FRIENDLY_NAME) { + attrValue = sec_pkcs12_get_attribute_value(key, + SEC_OID_PKCS9_FRIENDLY_NAME); + if(!attrValue) { + if(sec_pkcs12_decoder_set_attribute_value(key, + SEC_OID_PKCS9_FRIENDLY_NAME, + pki->attributes[i]->attrValue[0]) + != SECSuccess) { + key->problem = PR_TRUE; + key->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + } + } + + i++; + } + + return SECSuccess; +} + +/* retrieve the nickname for the certificate bag. first look + * in the cert bag, otherwise get it from the key. + */ +static SECItem * +sec_pkcs12_get_nickname_for_cert(sec_PKCS12SafeBag *cert, + sec_PKCS12SafeBag *key, + void *wincx) +{ + SECItem *nickname; + + if(!cert) { + return NULL; + } + + nickname = sec_pkcs12_get_nickname(cert); + if(nickname) { + return nickname; + } + + if(key) { + nickname = sec_pkcs12_get_nickname(key); + + if(nickname && sec_pkcs12_set_nickname(cert, nickname) + != SECSuccess) { + cert->error = SEC_ERROR_NO_MEMORY; + cert->problem = PR_TRUE; + if(nickname) { + SECITEM_ZfreeItem(nickname, PR_TRUE); + } + return NULL; + } + } + + return nickname; +} + +/* set the nickname for the certificate */ +static SECStatus +sec_pkcs12_set_nickname_for_cert(sec_PKCS12SafeBag *cert, + sec_PKCS12SafeBag *key, + SECItem *nickname, + void *wincx) +{ + if(!nickname || !cert) { + return SECFailure; + } + + if(sec_pkcs12_set_nickname(cert, nickname) != SECSuccess) { + cert->error = SEC_ERROR_NO_MEMORY; + cert->problem = PR_TRUE; + return SECFailure; + } + + if(key) { + if(sec_pkcs12_set_nickname(key, nickname) != SECSuccess) { + cert->error = SEC_ERROR_NO_MEMORY; + cert->problem = PR_TRUE; + return SECFailure; + } + } + + return SECSuccess; +} + +/* retrieve the DER cert from the cert bag */ +static SECItem * +sec_pkcs12_get_der_cert(sec_PKCS12SafeBag *cert) +{ + if(!cert) { + return NULL; + } + + if(SECOID_FindOIDTag(&cert->safeBagType) != SEC_OID_PKCS12_V1_CERT_BAG_ID) { + return NULL; + } + + /* only support X509 certs not SDSI */ + if(SECOID_FindOIDTag(&cert->safeBagContent.certBag->bagID) + != SEC_OID_PKCS9_X509_CERT) { + return NULL; + } + + return SECITEM_DupItem(&(cert->safeBagContent.certBag->value.x509Cert)); +} + +struct certNickInfo { + PRArenaPool *arena; + unsigned int nNicks; + SECItem **nickList; + unsigned int error; +}; + +/* callback for traversing certificates to gather the nicknames + * used in a particular traversal. for instance, when using + * CERT_TraversePermCertsForSubject, gather the nicknames and + * store them in the certNickInfo for a particular DN. + * + * this handles the case where multiple nicknames are allowed + * for the same dn, which is not currently allowed, but may be + * in the future. + */ +static SECStatus +gatherNicknames(CERTCertificate *cert, void *arg) +{ + struct certNickInfo *nickArg = (struct certNickInfo *)arg; + SECItem tempNick; + unsigned int i; + + if(!cert || !nickArg || nickArg->error) { + return SECFailure; + } + + if(!cert->nickname) { + return SECSuccess; + } + + tempNick.data = (unsigned char *)cert->nickname; + tempNick.len = PORT_Strlen(cert->nickname) + 1; + + /* do we already have the nickname in the list? */ + if(nickArg->nNicks > 0) { + + /* nicknames have been encountered, but there is no list -- bad */ + if(!nickArg->nickList) { + nickArg->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + + for(i = 0; i < nickArg->nNicks; i++) { + if(SECITEM_CompareItem(nickArg->nickList[i], &tempNick) + == SECEqual) { + return SECSuccess; + } + } + } + + /* add the nickname to the list */ + if(nickArg->nNicks == 0) { + nickArg->nickList = (SECItem **)PORT_ArenaZAlloc(nickArg->arena, + 2 * sizeof(SECItem *)); + } else { + nickArg->nickList = (SECItem **)PORT_ArenaGrow(nickArg->arena, + nickArg->nickList, + (nickArg->nNicks + 1) * sizeof(SECItem *), + (nickArg->nNicks + 2) * sizeof(SECItem *)); + } + if(!nickArg->nickList) { + nickArg->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + + nickArg->nickList[nickArg->nNicks] = + (SECItem *)PORT_ArenaZAlloc(nickArg->arena, sizeof(SECItem)); + if(!nickArg->nickList[nickArg->nNicks]) { + nickArg->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + + + if(SECITEM_CopyItem(nickArg->arena, nickArg->nickList[nickArg->nNicks], + &tempNick) != SECSuccess) { + nickArg->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + + nickArg->nNicks++; + + return SECSuccess; +} + +/* traverses the certs in the data base or in the token for the + * DN to see if any certs currently have a nickname set. + * If so, return it. + */ +static SECItem * +sec_pkcs12_get_existing_nick_for_dn(sec_PKCS12SafeBag *cert, void *wincx) +{ + struct certNickInfo *nickArg = NULL; + SECItem *derCert, *returnDn = NULL; + PRArenaPool *arena = NULL; + CERTCertificate *tempCert; + + if(!cert) { + return NULL; + } + + derCert = sec_pkcs12_get_der_cert(cert); + if(!derCert) { + return NULL; + } + + tempCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), derCert, NULL, + PR_FALSE, PR_TRUE); + if(!tempCert) { + returnDn = NULL; + goto loser; + } + + arena = PORT_NewArena(1024); + if(!arena) { + returnDn = NULL; + goto loser; + } + nickArg = (struct certNickInfo *)PORT_ArenaZAlloc(arena, + sizeof(struct certNickInfo)); + if(!nickArg) { + returnDn = NULL; + goto loser; + } + nickArg->error = 0; + nickArg->nNicks = 0; + nickArg->nickList = NULL; + nickArg->arena = arena; + + /* if the token is local, first traverse the cert database + * then traverse the token. + */ + if(PK11_IsInternal(cert->slot)) { + if(CERT_TraversePermCertsForSubject(CERT_GetDefaultCertDB(), + &tempCert->derSubject, gatherNicknames, + nickArg) != SECSuccess) { + returnDn = NULL; + goto loser; + } + } + + if(PK11_TraverseCertsForSubjectInSlot(tempCert, cert->slot, gatherNicknames, + (void *)nickArg) != SECSuccess) { + returnDn = NULL; + goto loser; + } + + if(nickArg->error) { + /* XXX do we want to set the error? */ + returnDn = NULL; + goto loser; + } + + if(nickArg->nNicks == 0) { + returnDn = NULL; + goto loser; + } + + /* set it to the first name, for now. handle multiple names? */ + returnDn = SECITEM_DupItem(nickArg->nickList[0]); + +loser: + if(arena) { + PORT_FreeArena(arena, PR_TRUE); + } + + if(tempCert) { + CERT_DestroyCertificate(tempCert); + } + + if(derCert) { + SECITEM_FreeItem(derCert, PR_TRUE); + } + + return (returnDn); +} + +/* counts certificates found for a given traversal function */ +static SECStatus +countCertificate(CERTCertificate *cert, void *arg) +{ + unsigned int *nCerts = (unsigned int *)arg; + + if(!cert || !arg) { + return SECFailure; + } + + (*nCerts)++; + return SECSuccess; +} + +static PRBool +sec_pkcs12_certs_for_nickname_exist(SECItem *nickname, PK11SlotInfo *slot) +{ + unsigned int nCerts = 0; + + if(!nickname || !slot) { + return PR_TRUE; + } + + /* we want to check the local database first if we are importing to it */ + if(PK11_IsInternal(slot)) { + CERT_TraversePermCertsForNickname(CERT_GetDefaultCertDB(), + (char *)nickname->data, + countCertificate, (void *)&nCerts); + } + + PK11_TraverseCertsForNicknameInSlot(nickname, slot, countCertificate, + (void *)&nCerts); + if(nCerts) return PR_TRUE; + + return PR_FALSE; +} + +/* validate cert nickname such that there is a one-to-one relation + * between nicknames and dn's. we want to enforce the case that the + * nickname is non-NULL and that there is only one nickname per DN. + * + * if there is a problem with a nickname or the nickname is not present, + * the user will be prompted for it. + */ +static void +sec_pkcs12_validate_cert_nickname(sec_PKCS12SafeBag *cert, + sec_PKCS12SafeBag *key, + SEC_PKCS12NicknameCollisionCallback nicknameCb, + void *wincx) +{ + SECItem *certNickname, *existingDNNick; + PRBool setNickname = PR_FALSE, cancel = PR_FALSE; + SECItem *newNickname = NULL; + + if(!cert || !cert->hasKey) { + return; + } + + if(!nicknameCb) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + return; + } + + if(cert->hasKey && !key) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + return; + } + + certNickname = sec_pkcs12_get_nickname_for_cert(cert, key, wincx); + existingDNNick = sec_pkcs12_get_existing_nick_for_dn(cert, wincx); + + /* nickname is already used w/ this dn, so it is safe to return */ + if(certNickname && existingDNNick && + SECITEM_CompareItem(certNickname, existingDNNick) == SECEqual) { + goto loser; + } + + /* nickname not set in pkcs 12 bags, but a nick is already used for + * this dn. set the nicks in the p12 bags and finish. + */ + if(existingDNNick) { + if(sec_pkcs12_set_nickname_for_cert(cert, key, existingDNNick, wincx) + != SECSuccess) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + } + goto loser; + } + + /* at this point, we have a certificate for which the DN is not located + * on the token. the nickname specified may or may not be NULL. if it + * is not null, we need to make sure that there are no other certificates + * with this nickname in the token for it to be valid. this imposes a + * one to one relationship between DN and nickname. + * + * if the nickname is null, we need the user to enter a nickname for + * the certificate. + * + * once we have a nickname, we make sure that the nickname is unique + * for the DN. if it is not, the user is reprompted to enter a new + * nickname. + * + * in order to exit this loop, the nickname entered is either unique + * or the user hits cancel and the certificate is not imported. + */ + setNickname = PR_FALSE; + while(1) { + if(certNickname && certNickname->data) { + /* we will use the nickname so long as no other certs have the + * same nickname. and the nickname is not NULL. + */ + if(!sec_pkcs12_certs_for_nickname_exist(certNickname, cert->slot)) { + if(setNickname) { + if(sec_pkcs12_set_nickname_for_cert(cert, key, certNickname, + wincx) != SECSuccess) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + } + } + goto loser; + } + } + + setNickname = PR_FALSE; + newNickname = (*nicknameCb)(certNickname, &cancel, wincx); + if(cancel) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_USER_CANCELLED; + goto loser; + } + + if(!newNickname) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* at this point we have a new nickname, if we have an existing + * certNickname, we need to free it and assign the new nickname + * to it to avoid a memory leak. happy? + */ + if(certNickname) { + SECITEM_ZfreeItem(certNickname, PR_TRUE); + certNickname = NULL; + } + + certNickname = newNickname; + setNickname = PR_TRUE; + /* go back and recheck the new nickname */ + } + +loser: + if(certNickname) { + SECITEM_ZfreeItem(certNickname, PR_TRUE); + } + + if(existingDNNick) { + SECITEM_ZfreeItem(existingDNNick, PR_TRUE); + } +} + +static void +sec_pkcs12_validate_cert(sec_PKCS12SafeBag *cert, + sec_PKCS12SafeBag *key, + SEC_PKCS12NicknameCollisionCallback nicknameCb, + void *wincx) +{ + CERTCertificate *leafCert, *testCert; + + if(!cert) { + return; + } + + cert->validated = PR_TRUE; + + if(!nicknameCb) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + cert->noInstall = PR_TRUE; + return; + } + + if(!cert->safeBagContent.certBag) { + cert->noInstall = PR_TRUE; + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE; + return; + } + + cert->noInstall = PR_FALSE; + cert->removeExisting = PR_FALSE; + cert->problem = PR_FALSE; + cert->error = 0; + + leafCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + &cert->safeBagContent.certBag->value.x509Cert, + NULL, PR_FALSE, PR_TRUE); + if(!leafCert) { + cert->noInstall = PR_TRUE; + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + return; + } + + testCert = PK11_FindCertFromDERCert(cert->slot, leafCert, wincx); + CERT_DestroyCertificate(leafCert); + /* if we can't find the certificate through the PKCS11 interface, + * we should check the cert database directly, if we are + * importing to an internal slot. + */ + if(!testCert && PK11_IsInternal(cert->slot)) { + testCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), + &cert->safeBagContent.certBag->value.x509Cert); + } + + if(testCert) { + if(!testCert->nickname) { + cert->removeExisting = PR_TRUE; + } else { + cert->noInstall = PR_TRUE; + } + CERT_DestroyCertificate(testCert); + if(cert->noInstall && !cert->removeExisting) { + return; + } + } + + sec_pkcs12_validate_cert_nickname(cert, key, nicknameCb, wincx); +} + +static void +sec_pkcs12_validate_key_by_cert(sec_PKCS12SafeBag *cert, sec_PKCS12SafeBag *key, + void *wincx) +{ + CERTCertificate *leafCert; + SECKEYPrivateKey *privk; + + if(!key) { + return; + } + + key->validated = PR_TRUE; + + if(!cert) { + key->problem = PR_TRUE; + key->noInstall = PR_TRUE; + key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY; + return; + } + + leafCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + &(cert->safeBagContent.certBag->value.x509Cert), + NULL, PR_FALSE, PR_TRUE); + if(!leafCert) { + key->problem = PR_TRUE; + key->noInstall = PR_TRUE; + key->error = SEC_ERROR_NO_MEMORY; + return; + } + + privk = PK11_FindPrivateKeyFromCert(key->slot, leafCert, wincx); + if(!privk) { + privk = PK11_FindKeyByDERCert(key->slot, leafCert, wincx); + } + + if(privk) { + SECKEY_DestroyPrivateKey(privk); + key->noInstall = PR_TRUE; + } + + CERT_DestroyCertificate(leafCert); +} + +static SECStatus +sec_pkcs12_remove_existing_cert(sec_PKCS12SafeBag *cert, + void *wincx) +{ + SECItem *derCert = NULL; + CERTCertificate *tempCert = NULL; + CK_OBJECT_HANDLE certObj; + PK11SlotInfo *slot = NULL; + PRBool removed = PR_FALSE; + + if(!cert) { + return SECFailure; + } + + PORT_Assert(cert->removeExisting); + + cert->removeExisting = PR_FALSE; + derCert = &cert->safeBagContent.certBag->value.x509Cert; + tempCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), derCert, + NULL, PR_FALSE, PR_TRUE); + if(!tempCert) { + return SECFailure; + } + + certObj = PK11_FindCertInSlot(cert->slot, tempCert, wincx); + CERT_DestroyCertificate(tempCert); + tempCert = NULL; + + if(certObj != CK_INVALID_KEY) { + PK11_DestroyObject(cert->slot, certObj); + removed = PR_TRUE; + } else if(PK11_IsInternal(cert->slot)) { + tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), derCert); + if(tempCert) { + if(SEC_DeletePermCertificate(tempCert) == SECSuccess) { + removed = PR_TRUE; + } + CERT_DestroyCertificate(tempCert); + tempCert = NULL; + } + } + + if(!removed) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + cert->noInstall = PR_TRUE; + } + + if(tempCert) { + CERT_DestroyCertificate(tempCert); + } + + return ((removed) ? SECSuccess : SECFailure); +} + +static SECStatus +sec_pkcs12_add_cert(sec_PKCS12SafeBag *cert, PRBool keyExists, void *wincx) +{ + SECItem *derCert, *nickName; + char *nickData = NULL; + SECStatus rv; + + if(!cert) { + return SECFailure; + } + + if(cert->problem || cert->noInstall || cert->installed) { + return SECSuccess; + } + + derCert = &cert->safeBagContent.certBag->value.x509Cert; + if(cert->removeExisting) { + if(sec_pkcs12_remove_existing_cert(cert, wincx) + != SECSuccess) { + return SECFailure; + } + cert->removeExisting = PR_FALSE; + } + + PORT_Assert(!cert->problem && !cert->removeExisting && !cert->noInstall); + + nickName = sec_pkcs12_get_nickname(cert); + if(nickName) { + nickData = (char *)nickName->data; + } + + if(keyExists) { + CERTCertificate *newCert; + + newCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + derCert, NULL, PR_FALSE, PR_TRUE); + if(!newCert) { + if(nickName) SECITEM_ZfreeItem(nickName, PR_TRUE); + cert->error = SEC_ERROR_NO_MEMORY; + cert->problem = PR_TRUE; + return SECFailure; + } + + rv = PK11_ImportCertForKeyToSlot(cert->slot, newCert, nickData, + PR_TRUE, wincx); + CERT_DestroyCertificate(newCert); + } else { + SECItem *certList[2]; + certList[0] = derCert; + certList[1] = NULL; + rv = CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageUserCertImport, + 1, certList, NULL, PR_TRUE, PR_FALSE, nickData); + } + + cert->installed = PR_TRUE; + if(nickName) SECITEM_ZfreeItem(nickName, PR_TRUE); + return rv; +} + +static SECStatus +sec_pkcs12_add_key(sec_PKCS12SafeBag *key, SECItem *publicValue, + KeyType keyType, unsigned int keyUsage, void *wincx) +{ + SECStatus rv; + SECItem *nickName; + + if(!key) { + return SECFailure; + } + + if(key->removeExisting) { + key->problem = PR_TRUE; + key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY; + return SECFailure; + } + + if(key->problem || key->noInstall) { + return SECSuccess; + } + + nickName = sec_pkcs12_get_nickname(key); + + switch(SECOID_FindOIDTag(&key->safeBagType)) + { + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + rv = PK11_ImportPrivateKeyInfo(key->slot, + key->safeBagContent.pkcs8KeyBag, + nickName, publicValue, PR_TRUE, PR_TRUE, + keyUsage, wincx); + break; + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + rv = PK11_ImportEncryptedPrivateKeyInfo(key->slot, + key->safeBagContent.pkcs8ShroudedKeyBag, + key->pwitem, nickName, publicValue, + PR_TRUE, PR_TRUE, keyType, keyUsage, + wincx); + break; + default: + key->error = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION; + key->problem = PR_TRUE; + if(nickName) { + SECITEM_ZfreeItem(nickName, PR_TRUE); + } + return SECFailure; + } + + key->installed = PR_TRUE; + + if(nickName) { + SECITEM_ZfreeItem(nickName, PR_TRUE); + } + + if(rv != SECSuccess) { + key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY; + key->problem = PR_TRUE; + } else { + key->installed = PR_TRUE; + } + + return rv; +} + +static SECStatus +sec_pkcs12_add_item_to_bag_list(sec_PKCS12SafeBag ***bagList, + sec_PKCS12SafeBag *bag) +{ + int i = 0; + + if(!bagList || !bag) { + return SECFailure; + } + + if(!(*bagList)) { + (*bagList) = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12SafeBag *) * 2); + } else { + while((*bagList)[i]) i++; + (*bagList) = (sec_PKCS12SafeBag **)PORT_ArenaGrow(bag->arena, *bagList, + sizeof(sec_PKCS12SafeBag *) * (i + 1), + sizeof(sec_PKCS12SafeBag *) * (i + 2)); + } + + if(!(*bagList)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + (*bagList)[i] = bag; + (*bagList)[i+1] = NULL; + + return SECSuccess; +} + +static sec_PKCS12SafeBag ** +sec_pkcs12_find_certs_for_key(sec_PKCS12SafeBag **safeBags, sec_PKCS12SafeBag *key ) +{ + sec_PKCS12SafeBag **certList = NULL; + SECItem *keyId; + int i; + + if(!safeBags || !safeBags[0]) { + return NULL; + } + + keyId = sec_pkcs12_get_attribute_value(key, SEC_OID_PKCS9_LOCAL_KEY_ID); + if(!keyId) { + return NULL; + } + + i = 0; + certList = NULL; + while(safeBags[i]) { + if(SECOID_FindOIDTag(&(safeBags[i]->safeBagType)) + == SEC_OID_PKCS12_V1_CERT_BAG_ID) { + SECItem *certKeyId = sec_pkcs12_get_attribute_value(safeBags[i], + SEC_OID_PKCS9_LOCAL_KEY_ID); + + if(certKeyId && (SECITEM_CompareItem(certKeyId, keyId) + == SECEqual)) { + if(sec_pkcs12_add_item_to_bag_list(&certList, safeBags[i]) + != SECSuccess) { + return NULL; + } + } + } + i++; + } + + return certList; +} + +CERTCertList * +SEC_PKCS12DecoderGetCerts(SEC_PKCS12DecoderContext *p12dcx) +{ + CERTCertList *certList = NULL; + sec_PKCS12SafeBag **safeBags = p12dcx->safeBags; + int i; + + if (!p12dcx || !p12dcx->safeBags || !p12dcx->safeBags[0]) { + return NULL; + } + + safeBags = p12dcx->safeBags; + i = 0; + certList = CERT_NewCertList(); + + if (certList == NULL) { + return NULL; + } + + while(safeBags[i]) { + if (SECOID_FindOIDTag(&(safeBags[i]->safeBagType)) + == SEC_OID_PKCS12_V1_CERT_BAG_ID) { + SECItem *derCert = sec_pkcs12_get_der_cert(safeBags[i]) ; + CERTCertificate *tempCert = NULL; + + if (derCert == NULL) continue; + tempCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + derCert, NULL, PR_FALSE, PR_TRUE); + + if (tempCert) { + CERT_AddCertToListTail(certList,tempCert); + } + SECITEM_FreeItem(derCert,PR_TRUE); + } + i++; + } + + return certList; +} +static sec_PKCS12SafeBag ** +sec_pkcs12_get_key_bags(sec_PKCS12SafeBag **safeBags) +{ + int i; + sec_PKCS12SafeBag **keyList = NULL; + SECOidTag bagType; + + if(!safeBags || !safeBags[0]) { + return NULL; + } + + i = 0; + while(safeBags[i]) { + bagType = SECOID_FindOIDTag(&(safeBags[i]->safeBagType)); + switch(bagType) { + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + if(sec_pkcs12_add_item_to_bag_list(&keyList, safeBags[i]) + != SECSuccess) { + return NULL; + } + break; + default: + break; + } + i++; + } + + return keyList; +} + +static SECStatus +sec_pkcs12_validate_bags(sec_PKCS12SafeBag **safeBags, + SEC_PKCS12NicknameCollisionCallback nicknameCb, + void *wincx) +{ + sec_PKCS12SafeBag **keyList; + int i; + + if(!safeBags || !nicknameCb) { + return SECFailure; + } + + if(!safeBags[0]) { + return SECSuccess; + } + + keyList = sec_pkcs12_get_key_bags(safeBags); + if(keyList) { + i = 0; + + while(keyList[i]) { + sec_PKCS12SafeBag **certList = sec_pkcs12_find_certs_for_key( + safeBags, keyList[i]); + if(certList) { + int j = 0; + + if(SECOID_FindOIDTag(&(keyList[i]->safeBagType)) == + SEC_OID_PKCS12_V1_KEY_BAG_ID) { + /* if it is an unencrypted private key then make sure + * the attributes are propageted to the appropriate + * level + */ + if(sec_pkcs12_get_key_info(keyList[i]) != SECSuccess) { + keyList[i]->problem = PR_TRUE; + keyList[i]->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + } + + sec_pkcs12_validate_key_by_cert(certList[0], keyList[i], wincx); + while(certList[j]) { + certList[j]->hasKey = PR_TRUE; + if(keyList[i]->problem) { + certList[j]->problem = PR_TRUE; + certList[j]->error = keyList[i]->error; + } else { + sec_pkcs12_validate_cert(certList[j], keyList[i], + nicknameCb, wincx); + if(certList[j]->problem) { + keyList[i]->problem = certList[j]->problem; + keyList[i]->error = certList[j]->error; + } + } + j++; + } + } + + i++; + } + } + + i = 0; + while(safeBags[i]) { + if(!safeBags[i]->validated) { + SECOidTag bagType = SECOID_FindOIDTag(&safeBags[i]->safeBagType); + + switch(bagType) { + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + sec_pkcs12_validate_cert(safeBags[i], NULL, nicknameCb, + wincx); + break; + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + safeBags[i]->noInstall = PR_TRUE; + safeBags[i]->problem = PR_TRUE; + safeBags[i]->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY; + break; + default: + safeBags[i]->noInstall = PR_TRUE; + } + } + i++; + } + + return SECSuccess; +} + +SECStatus +SEC_PKCS12DecoderValidateBags(SEC_PKCS12DecoderContext *p12dcx, + SEC_PKCS12NicknameCollisionCallback nicknameCb) +{ + SECStatus rv; + int i, noInstallCnt, probCnt, bagCnt, errorVal = 0; + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + rv = sec_pkcs12_validate_bags(p12dcx->safeBags, nicknameCb, p12dcx->wincx); + if(rv == SECSuccess) { + p12dcx->bagsVerified = PR_TRUE; + } + + noInstallCnt = probCnt = bagCnt = 0; + i = 0; + while(p12dcx->safeBags[i]) { + bagCnt++; + if(p12dcx->safeBags[i]->noInstall) noInstallCnt++; + if(p12dcx->safeBags[i]->problem) { + probCnt++; + errorVal = p12dcx->safeBags[i]->error; + } + i++; + } + + if(bagCnt == noInstallCnt) { + PORT_SetError(SEC_ERROR_PKCS12_DUPLICATE_DATA); + return SECFailure; + } + + if(probCnt) { + PORT_SetError(errorVal); + return SECFailure; + } + + return rv; +} + +static SECItem * +sec_pkcs12_get_public_value_and_type(sec_PKCS12SafeBag *certBag, + KeyType *type, unsigned int *usage) +{ + SECKEYPublicKey *pubKey = NULL; + CERTCertificate *cert = NULL; + SECItem *pubValue; + + *type = nullKey; + *usage = 0; + + if(!certBag) { + return NULL; + } + + cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + &certBag->safeBagContent.certBag->value.x509Cert, + NULL, PR_FALSE, PR_FALSE); + if(!cert) { + return NULL; + } + + *usage = cert->keyUsage; + pubKey = CERT_ExtractPublicKey(cert); + CERT_DestroyCertificate(cert); + if(!pubKey) { + return NULL; + } + + *type = pubKey->keyType; + switch(pubKey->keyType) { + case dsaKey: + pubValue = SECITEM_DupItem(&pubKey->u.dsa.publicValue); + break; + case dhKey: + pubValue = SECITEM_DupItem(&pubKey->u.dh.publicValue); + break; + case rsaKey: + pubValue = SECITEM_DupItem(&pubKey->u.rsa.modulus); + break; + default: + pubValue = NULL; + } + + SECKEY_DestroyPublicKey(pubKey); + + return pubValue; +} + +static SECStatus +sec_pkcs12_install_bags(sec_PKCS12SafeBag **safeBags, + void *wincx) +{ + sec_PKCS12SafeBag **keyList, **certList; + int i; + + if(!safeBags) { + return SECFailure; + } + + if(!safeBags[0]) { + return SECSuccess; + } + + keyList = sec_pkcs12_get_key_bags(safeBags); + if(keyList) { + i = 0; + + while(keyList[i]) { + SECStatus rv; + SECItem *publicValue = NULL; + KeyType keyType; + unsigned int keyUsage; + + if(keyList[i]->problem) { + goto next_key_bag; + } + + certList = sec_pkcs12_find_certs_for_key(safeBags, + keyList[i]); + if(certList) { + publicValue = sec_pkcs12_get_public_value_and_type(certList[0], + &keyType, &keyUsage); + } + rv = sec_pkcs12_add_key(keyList[i], publicValue, keyType, keyUsage, + wincx); + if(publicValue) { + SECITEM_FreeItem(publicValue, PR_TRUE); + } + if(rv != SECSuccess) { + PORT_SetError(keyList[i]->error); + return SECFailure; + } + + if(certList) { + int j = 0; + + while(certList[j]) { + SECStatus certRv; + + if(rv != SECSuccess) { + certList[j]->problem = keyList[i]->problem; + certList[j]->error = keyList[i]->error; + certList[j]->noInstall = PR_TRUE; + goto next_cert_bag; + } + + certRv = sec_pkcs12_add_cert(certList[j], + certList[j]->hasKey, wincx); + if(certRv != SECSuccess) { + keyList[i]->problem = certList[j]->problem; + keyList[i]->error = certList[j]->error; + PORT_SetError(certList[j]->error); + return SECFailure; + } +next_cert_bag: + j++; + } + } + +next_key_bag: + i++; + } + } + + i = 0; + while(safeBags[i]) { + if(!safeBags[i]->installed) { + SECStatus rv; + SECOidTag bagType = SECOID_FindOIDTag(&(safeBags[i]->safeBagType)); + + switch(bagType) { + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + rv = sec_pkcs12_add_cert(safeBags[i], safeBags[i]->hasKey, + wincx); + if(rv != SECSuccess) { + PORT_SetError(safeBags[i]->error); + return SECFailure; + } + break; + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + default: + break; + } + } + i++; + } + + return SECSuccess; +} + +SECStatus +SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx) +{ + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + if(!p12dcx->bagsVerified) { + return SECFailure; + } + + return sec_pkcs12_install_bags(p12dcx->safeBags, p12dcx->wincx); +} + +static SECStatus +sec_pkcs12_decoder_append_bag_to_context(SEC_PKCS12DecoderContext *p12dcx, + sec_PKCS12SafeBag *bag) +{ + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + if(!p12dcx->safeBagCount) { + p12dcx->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12SafeBag *) * 2); + } else { + p12dcx->safeBags = + (sec_PKCS12SafeBag **)PORT_ArenaGrow(p12dcx->arena, p12dcx->safeBags, + (p12dcx->safeBagCount + 1) * sizeof(sec_PKCS12SafeBag *), + (p12dcx->safeBagCount + 2) * sizeof(sec_PKCS12SafeBag *)); + } + + if(!p12dcx->safeBags) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + p12dcx->safeBags[p12dcx->safeBagCount] = bag; + p12dcx->safeBags[p12dcx->safeBagCount+1] = NULL; + p12dcx->safeBagCount++; + + return SECSuccess; +} + +static sec_PKCS12SafeBag * +sec_pkcs12_decoder_convert_old_key(SEC_PKCS12DecoderContext *p12dcx, + void *key, PRBool isEspvk) +{ + sec_PKCS12SafeBag *keyBag; + SECOidData *oid; + SECOidTag keyTag; + SECItem *keyID, *nickName, *newNickName; + + if(!p12dcx || p12dcx->error || !key) { + return NULL; + } + + newNickName =(SECItem *)PORT_ArenaZAlloc(p12dcx->arena, sizeof(SECItem)); + keyBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12SafeBag)); + if(!keyBag || !newNickName) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + keyBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes; + keyBag->slot = p12dcx->slot; + keyBag->arena = p12dcx->arena; + keyBag->pwitem = p12dcx->pwitem; + keyBag->oldBagType = PR_TRUE; + + keyTag = (isEspvk) ? SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID : + SEC_OID_PKCS12_V1_KEY_BAG_ID; + oid = SECOID_FindOIDByTag(keyTag); + if(!oid) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + if(SECITEM_CopyItem(p12dcx->arena, &keyBag->safeBagType, &oid->oid) + != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + if(isEspvk) { + SEC_PKCS12ESPVKItem *espvk = (SEC_PKCS12ESPVKItem *)key; + keyBag->safeBagContent.pkcs8ShroudedKeyBag = + espvk->espvkCipherText.pkcs8KeyShroud; + nickName = &(espvk->espvkData.uniNickName); + if(!espvk->espvkData.assocCerts || !espvk->espvkData.assocCerts[0]) { + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); + return NULL; + } + keyID = &espvk->espvkData.assocCerts[0]->digest; + } else { + SEC_PKCS12PrivateKey *pk = (SEC_PKCS12PrivateKey *)key; + keyBag->safeBagContent.pkcs8KeyBag = &pk->pkcs8data; + nickName= &(pk->pvkData.uniNickName); + if(!pk->pvkData.assocCerts || !pk->pvkData.assocCerts[0]) { + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); + return NULL; + } + keyID = &pk->pvkData.assocCerts[0]->digest; + } + + if(nickName->len) { + if(nickName->len >= 2) { + if(nickName->data[0] && nickName->data[1]) { + if(!sec_pkcs12_convert_item_to_unicode(p12dcx->arena, newNickName, + nickName, PR_FALSE, PR_FALSE, PR_TRUE)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + nickName = newNickName; + } else if(nickName->data[0] && !nickName->data[1]) { + unsigned int j = 0; + unsigned char t; + for(j = 0; j < nickName->len; j+=2) { + t = nickName->data[j+1]; + nickName->data[j+1] = nickName->data[j]; + nickName->data[j] = t; + } + } + } else { + if(!sec_pkcs12_convert_item_to_unicode(p12dcx->arena, newNickName, + nickName, PR_FALSE, PR_FALSE, PR_TRUE)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + nickName = newNickName; + } + } + + if(sec_pkcs12_decoder_set_attribute_value(keyBag, + SEC_OID_PKCS9_FRIENDLY_NAME, + nickName) != SECSuccess) { + return NULL; + } + + if(sec_pkcs12_decoder_set_attribute_value(keyBag,SEC_OID_PKCS9_LOCAL_KEY_ID, + keyID) != SECSuccess) { + return NULL; + } + + return keyBag; +} + +static sec_PKCS12SafeBag * +sec_pkcs12_decoder_create_cert(SEC_PKCS12DecoderContext *p12dcx, + SECItem *derCert) +{ + sec_PKCS12SafeBag *certBag; + SECOidData *oid; + SGNDigestInfo *digest; + SECItem *keyId; + SECStatus rv; + + if(!p12dcx || p12dcx->error || !derCert) { + return NULL; + } + + keyId = (SECItem *)PORT_ArenaZAlloc(p12dcx->arena, sizeof(SECItem)); + if(!keyId) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + digest = sec_pkcs12_compute_thumbprint(derCert); + if(!digest) { + return NULL; + } + + rv = SECITEM_CopyItem(p12dcx->arena, keyId, &digest->digest); + SGN_DestroyDigestInfo(digest); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + oid = SECOID_FindOIDByTag(SEC_OID_PKCS12_V1_CERT_BAG_ID); + certBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12SafeBag)); + if(!certBag || !oid || (SECITEM_CopyItem(p12dcx->arena, + &certBag->safeBagType, &oid->oid) != SECSuccess)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + certBag->slot = p12dcx->slot; + certBag->pwitem = p12dcx->pwitem; + certBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes; + certBag->arena = p12dcx->arena; + + oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_X509_CERT); + certBag->safeBagContent.certBag = + (sec_PKCS12CertBag *)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12CertBag)); + if(!certBag->safeBagContent.certBag || !oid || + (SECITEM_CopyItem(p12dcx->arena, + &certBag->safeBagContent.certBag->bagID, + &oid->oid) != SECSuccess)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + if(SECITEM_CopyItem(p12dcx->arena, + &(certBag->safeBagContent.certBag->value.x509Cert), + derCert) != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + if(sec_pkcs12_decoder_set_attribute_value(certBag, SEC_OID_PKCS9_LOCAL_KEY_ID, + keyId) != SECSuccess) { + return NULL; + } + + return certBag; +} + +static sec_PKCS12SafeBag ** +sec_pkcs12_decoder_convert_old_cert(SEC_PKCS12DecoderContext *p12dcx, + SEC_PKCS12CertAndCRL *oldCert) +{ + sec_PKCS12SafeBag **certList; + SECItem **derCertList; + int i, j; + + if(!p12dcx || p12dcx->error || !oldCert) { + return NULL; + } + + derCertList = SEC_PKCS7GetCertificateList(&oldCert->value.x509->certOrCRL); + if(!derCertList) { + return NULL; + } + + i = 0; + while(derCertList[i]) i++; + + certList = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(p12dcx->arena, + (i + 1) * sizeof(sec_PKCS12SafeBag *)); + if(!certList) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + for(j = 0; j < i; j++) { + certList[j] = sec_pkcs12_decoder_create_cert(p12dcx, derCertList[j]); + if(!certList[j]) { + return NULL; + } + } + + return certList; +} + +static SECStatus +sec_pkcs12_decoder_convert_old_key_and_certs(SEC_PKCS12DecoderContext *p12dcx, + void *oldKey, PRBool isEspvk, + SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage) +{ + sec_PKCS12SafeBag *key, **certList; + SEC_PKCS12CertAndCRL *oldCert; + SEC_PKCS12PVKSupportingData *pvkData; + int i; + SECItem *keyName; + + if(!p12dcx || !oldKey) { + return SECFailure; + } + + if(isEspvk) { + pvkData = &((SEC_PKCS12ESPVKItem *)(oldKey))->espvkData; + } else { + pvkData = &((SEC_PKCS12PrivateKey *)(oldKey))->pvkData; + } + + if(!pvkData->assocCerts || !pvkData->assocCerts[0]) { + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); + return SECFailure; + } + + oldCert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage, + SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID, NULL, + pvkData->assocCerts[0]); + if(!oldCert) { + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); + return SECFailure; + } + + key = sec_pkcs12_decoder_convert_old_key(p12dcx,oldKey, isEspvk); + certList = sec_pkcs12_decoder_convert_old_cert(p12dcx, oldCert); + if(!key || !certList) { + return SECFailure; + } + + if(sec_pkcs12_decoder_append_bag_to_context(p12dcx, key) != SECSuccess) { + return SECFailure; + } + + keyName = sec_pkcs12_get_nickname(key); + if(!keyName) { + return SECFailure; + } + + i = 0; + while(certList[i]) { + if(sec_pkcs12_decoder_append_bag_to_context(p12dcx, certList[i]) + != SECSuccess) { + return SECFailure; + } + i++; + } + + certList = sec_pkcs12_find_certs_for_key(p12dcx->safeBags, key); + if(!certList) { + return SECFailure; + } + + i = 0; + while(certList[i] != 0) { + if(sec_pkcs12_set_nickname(certList[i], keyName) != SECSuccess) { + return SECFailure; + } + i++; + } + + return SECSuccess; +} + +static SECStatus +sec_pkcs12_decoder_convert_old_safe_to_bags(SEC_PKCS12DecoderContext *p12dcx, + SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage) +{ + SECStatus rv; + + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + if(safe && safe->contents) { + int i = 0; + while(safe->contents[i] != NULL) { + if(SECOID_FindOIDTag(&safe->contents[i]->safeBagType) + == SEC_OID_PKCS12_KEY_BAG_ID) { + int j = 0; + SEC_PKCS12PrivateKeyBag *privBag = + safe->contents[i]->safeContent.keyBag; + + while(privBag->privateKeys[j] != NULL) { + SEC_PKCS12PrivateKey *pk = privBag->privateKeys[j]; + rv = sec_pkcs12_decoder_convert_old_key_and_certs(p12dcx,pk, + PR_FALSE, safe, baggage); + if(rv != SECSuccess) { + goto loser; + } + j++; + } + } + i++; + } + } + + if(baggage && baggage->bags) { + int i = 0; + while(baggage->bags[i] != NULL) { + SEC_PKCS12BaggageItem *bag = baggage->bags[i]; + int j = 0; + + if(!bag->espvks) { + i++; + continue; + } + + while(bag->espvks[j] != NULL) { + SEC_PKCS12ESPVKItem *espvk = bag->espvks[j]; + rv = sec_pkcs12_decoder_convert_old_key_and_certs(p12dcx, espvk, + PR_TRUE, safe, baggage); + if(rv != SECSuccess) { + goto loser; + } + j++; + } + i++; + } + } + + return SECSuccess; + +loser: + return SECFailure; +} + +SEC_PKCS12DecoderContext * +sec_PKCS12ConvertOldSafeToNew(PRArenaPool *arena, PK11SlotInfo *slot, + PRBool swapUnicode, SECItem *pwitem, + void *wincx, SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage) +{ + SEC_PKCS12DecoderContext *p12dcx; + + if(!arena || !slot || !pwitem) { + return NULL; + } + + if(!safe && !baggage) { + return NULL; + } + + p12dcx = (SEC_PKCS12DecoderContext *)PORT_ArenaZAlloc(arena, + sizeof(SEC_PKCS12DecoderContext)); + if(!p12dcx) { + return NULL; + } + + p12dcx->arena = arena; + p12dcx->slot = slot; + p12dcx->wincx = wincx; + p12dcx->error = PR_FALSE; + p12dcx->swapUnicodeBytes = swapUnicode; + p12dcx->pwitem = pwitem; + + if(sec_pkcs12_decoder_convert_old_safe_to_bags(p12dcx, safe, baggage) + != SECSuccess) { + p12dcx->error = PR_TRUE; + return NULL; + } + + return p12dcx; +} diff --git a/security/nss/lib/softoken/keydb.c b/security/nss/lib/softoken/keydb.c new file mode 100644 index 000000000..90da85ac0 --- /dev/null +++ b/security/nss/lib/softoken/keydb.c @@ -0,0 +1,2482 @@ +/* + * 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. + * + * Private Key Database code + * + * $Id$ + */ + +#include "keylow.h" +#include "keydbt.h" +#include "seccomon.h" +#include "sechash.h" +#include "secder.h" +#include "secasn1.h" +#include "secoid.h" +#include "blapi.h" +#include "secitem.h" +#include "cert.h" +#include "mcom_db.h" +#include "secpkcs5.h" +#include "secerr.h" + +#include "private.h" + +/* + * Record keys for keydb + */ +#define SALT_STRING "global-salt" +#define VERSION_STRING "Version" +#define KEYDB_PW_CHECK_STRING "password-check" +#define KEYDB_PW_CHECK_LEN 14 +#define KEYDB_FAKE_PW_CHECK_STRING "fake-password-check" +#define KEYDB_FAKE_PW_CHECK_LEN 19 + +/* Size of the global salt for key database */ +#define SALT_LENGTH 16 + +/* ASN1 Templates for new decoder/encoder */ +/* + * Attribute value for PKCS8 entries (static?) + */ +const SEC_ASN1Template SECKEY_AttributeTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SECKEYAttribute) }, + { SEC_ASN1_OBJECT_ID, offsetof(SECKEYAttribute, attrType) }, + { SEC_ASN1_SET_OF, offsetof(SECKEYAttribute, attrValue), + SEC_AnyTemplate }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_SetOfAttributeTemplate[] = { + { SEC_ASN1_SET_OF, 0, SECKEY_AttributeTemplate }, +}; + +const SEC_ASN1Template SECKEY_PrivateKeyInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SECKEYPrivateKeyInfo) }, + { SEC_ASN1_INTEGER, + offsetof(SECKEYPrivateKeyInfo,version) }, + { SEC_ASN1_INLINE, + offsetof(SECKEYPrivateKeyInfo,algorithm), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_OCTET_STRING, + offsetof(SECKEYPrivateKeyInfo,privateKey) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SECKEYPrivateKeyInfo,attributes), + SECKEY_SetOfAttributeTemplate }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_PointerToPrivateKeyInfoTemplate[] = { + { SEC_ASN1_POINTER, 0, SECKEY_PrivateKeyInfoTemplate } +}; + +const SEC_ASN1Template SECKEY_EncryptedPrivateKeyInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SECKEYEncryptedPrivateKeyInfo) }, + { SEC_ASN1_INLINE, + offsetof(SECKEYEncryptedPrivateKeyInfo,algorithm), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_OCTET_STRING, + offsetof(SECKEYEncryptedPrivateKeyInfo,encryptedData) }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_PointerToEncryptedPrivateKeyInfoTemplate[] = { + { SEC_ASN1_POINTER, 0, SECKEY_EncryptedPrivateKeyInfoTemplate } +}; + +/* ====== Default key databse encryption algorithm ====== */ + +static SECOidTag defaultKeyDBAlg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC; + +/* + * Default algorithm for encrypting data in the key database + */ +SECOidTag +SECKEY_GetDefaultKeyDBAlg(void) +{ + return(defaultKeyDBAlg); +} + +void +SECKEY_SetDefaultKeyDBAlg(SECOidTag alg) +{ + defaultKeyDBAlg = alg; + + return; +} + +static void +sec_destroy_dbkey(SECKEYDBKey *dbkey) +{ + if ( dbkey && dbkey->arena ) { + PORT_FreeArena(dbkey->arena, PR_FALSE); + } +} + +static void +free_dbt(DBT *dbt) +{ + if ( dbt ) { + PORT_Free(dbt->data); + PORT_Free(dbt); + } + + return; +} + +/* + * format of key database entries for version 3 of database: + * byte offset field + * ----------- ----- + * 0 version + * 1 salt-len + * 2 nn-len + * 3.. salt-data + * ... nickname + * ... encrypted-key-data + */ +static DBT * +encode_dbkey(SECKEYDBKey *dbkey) +{ + DBT *bufitem = NULL; + unsigned char *buf; + int nnlen; + char *nn; + + bufitem = (DBT *)PORT_ZAlloc(sizeof(DBT)); + if ( bufitem == NULL ) { + goto loser; + } + + if ( dbkey->nickname ) { + nn = dbkey->nickname; + nnlen = PORT_Strlen(nn) + 1; + } else { + nn = ""; + nnlen = 1; + } + + /* compute the length of the record */ + /* 1 + 1 + 1 == version number header + salt length + nn len */ + bufitem->size = dbkey->salt.len + nnlen + dbkey->derPK.len + 1 + 1 + 1; + + bufitem->data = (void *)PORT_ZAlloc(bufitem->size); + if ( bufitem->data == NULL ) { + goto loser; + } + + buf = (unsigned char *)bufitem->data; + + /* set version number */ + buf[0] = PRIVATE_KEY_DB_FILE_VERSION; + + /* set length of salt */ + PORT_Assert(dbkey->salt.len < 256); + buf[1] = dbkey->salt.len; + + /* set length of nickname */ + PORT_Assert(nnlen < 256); + buf[2] = nnlen; + + /* copy salt */ + PORT_Memcpy(&buf[3], dbkey->salt.data, dbkey->salt.len); + + /* copy nickname */ + PORT_Memcpy(&buf[3 + dbkey->salt.len], nn, nnlen); + + /* copy encrypted key */ + PORT_Memcpy(&buf[3 + dbkey->salt.len + nnlen], dbkey->derPK.data, + dbkey->derPK.len); + + return(bufitem); + +loser: + if ( bufitem ) { + free_dbt(bufitem); + } + + return(NULL); +} + +static SECKEYDBKey * +decode_dbkey(DBT *bufitem, int expectedVersion) +{ + SECKEYDBKey *dbkey; + PLArenaPool *arena = NULL; + unsigned char *buf; + int version; + int keyoff; + int nnlen; + int saltoff; + + buf = (unsigned char *)bufitem->data; + + version = buf[0]; + + if ( version != expectedVersion ) { + goto loser; + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( arena == NULL ) { + goto loser; + } + + dbkey = (SECKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(SECKEYDBKey)); + if ( dbkey == NULL ) { + goto loser; + } + + dbkey->arena = arena; + dbkey->salt.data = NULL; + dbkey->derPK.data = NULL; + + dbkey->salt.len = buf[1]; + dbkey->salt.data = (unsigned char *)PORT_ArenaZAlloc(arena, dbkey->salt.len); + if ( dbkey->salt.data == NULL ) { + goto loser; + } + + saltoff = 2; + keyoff = 2 + dbkey->salt.len; + + if ( expectedVersion == PRIVATE_KEY_DB_FILE_VERSION ) { + nnlen = buf[2]; + if ( nnlen ) { + dbkey->nickname = (char *)PORT_ArenaZAlloc(arena, nnlen + 1); + if ( dbkey->nickname ) { + PORT_Memcpy(dbkey->nickname, &buf[keyoff+1], nnlen); + } + } + keyoff += ( nnlen + 1 ); + saltoff = 3; + } + + PORT_Memcpy(dbkey->salt.data, &buf[saltoff], dbkey->salt.len); + + dbkey->derPK.len = bufitem->size - keyoff; + dbkey->derPK.data = (unsigned char *)PORT_ArenaZAlloc(arena,dbkey->derPK.len); + if ( dbkey->derPK.data == NULL ) { + goto loser; + } + + PORT_Memcpy(dbkey->derPK.data, &buf[keyoff], dbkey->derPK.len); + + return(dbkey); + +loser: + + if ( arena ) { + PORT_FreeArena(arena, PR_FALSE); + } + + return(NULL); +} + +static SECKEYDBKey * +get_dbkey(SECKEYKeyDBHandle *handle, DBT *index) +{ + SECKEYDBKey *dbkey; + DBT entry; + int ret; + + /* get it from the database */ + ret = (* handle->db->get)(handle->db, index, &entry, 0); + if ( ret ) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + return NULL; + } + + /* set up dbkey struct */ + + dbkey = decode_dbkey(&entry, PRIVATE_KEY_DB_FILE_VERSION); + + return(dbkey); +} + +static SECStatus +put_dbkey(SECKEYKeyDBHandle *handle, DBT *index, SECKEYDBKey *dbkey, PRBool update) +{ + DBT *keydata = NULL; + int status; + + keydata = encode_dbkey(dbkey); + if ( keydata == NULL ) { + goto loser; + } + + /* put it in the database */ + if ( update ) { + status = (* handle->db->put)(handle->db, index, keydata, 0); + } else { + status = (* handle->db->put)(handle->db, index, keydata, + R_NOOVERWRITE); + } + + if ( status ) { + goto loser; + } + + /* sync the database */ + status = (* handle->db->sync)(handle->db, 0); + if ( status ) { + goto loser; + } + + free_dbt(keydata); + return(SECSuccess); + +loser: + if ( keydata ) { + free_dbt(keydata); + } + + return(SECFailure); +} + +SECStatus +SECKEY_TraverseKeys(SECKEYKeyDBHandle *handle, + SECStatus (* keyfunc)(DBT *k, DBT *d, void *pdata), + void *udata ) +{ + DBT data; + DBT key; + SECStatus status; + int ret; + + if (handle == NULL) { + return(SECFailure); + } + + ret = (* handle->db->seq)(handle->db, &key, &data, R_FIRST); + if ( ret ) { + return(SECFailure); + } + + do { + /* skip version record */ + if ( data.size > 1 ) { + if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) { + if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) { + continue; + } + } + + /* skip password check */ + if ( key.size == KEYDB_PW_CHECK_LEN ) { + if ( PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING, + KEYDB_PW_CHECK_LEN) == 0 ) { + continue; + } + } + + status = (* keyfunc)(&key, &data, udata); + if (status != SECSuccess) { + return(status); + } + } + } while ( (* handle->db->seq)(handle->db, &key, &data, R_NEXT) == 0 ); + + return(SECSuccess); +} + +typedef struct keyNode { + struct keyNode *next; + DBT key; +} keyNode; + +typedef struct { + PLArenaPool *arena; + keyNode *head; +} keyList; + +static SECStatus +sec_add_key_to_list(DBT *key, DBT *data, void *arg) +{ + keyList *keylist; + keyNode *node; + void *keydata; + + keylist = (keyList *)arg; + + /* allocate the node struct */ + node = (keyNode*)PORT_ArenaZAlloc(keylist->arena, sizeof(keyNode)); + if ( node == NULL ) { + return(SECFailure); + } + + /* allocate room for key data */ + keydata = PORT_ArenaZAlloc(keylist->arena, key->size); + if ( keydata == NULL ) { + return(SECFailure); + } + + /* link node into list */ + node->next = keylist->head; + keylist->head = node; + + /* copy key into node */ + PORT_Memcpy(keydata, key->data, key->size); + node->key.size = key->size; + node->key.data = keydata; + + return(SECSuccess); +} + +static SECItem * +decodeKeyDBGlobalSalt(DBT *saltData) +{ + SECItem *saltitem; + + saltitem = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if ( saltitem == NULL ) { + return(NULL); + } + + saltitem->data = (unsigned char *)PORT_ZAlloc(saltData->size); + if ( saltitem->data == NULL ) { + PORT_Free(saltitem); + return(NULL); + } + + saltitem->len = saltData->size; + PORT_Memcpy(saltitem->data, saltData->data, saltitem->len); + + return(saltitem); +} + +static SECItem * +GetKeyDBGlobalSalt(SECKEYKeyDBHandle *handle) +{ + DBT saltKey; + DBT saltData; + int ret; + + saltKey.data = SALT_STRING; + saltKey.size = sizeof(SALT_STRING) - 1; + + ret = (* handle->db->get)(handle->db, &saltKey, &saltData, 0); + if ( ret ) { + return(NULL); + } + + return(decodeKeyDBGlobalSalt(&saltData)); +} + +static SECStatus +makeGlobalVersion(SECKEYKeyDBHandle *handle) +{ + unsigned char version; + DBT versionData; + DBT versionKey; + int status; + + version = PRIVATE_KEY_DB_FILE_VERSION; + versionData.data = &version; + versionData.size = 1; + versionKey.data = VERSION_STRING; + versionKey.size = sizeof(VERSION_STRING)-1; + + /* put version string into the database now */ + status = (* handle->db->put)(handle->db, &versionKey, &versionData, 0); + if ( status ) { + return(SECFailure); + } + + return(SECSuccess); +} + + +static SECStatus +makeGlobalSalt(SECKEYKeyDBHandle *handle) +{ + DBT saltKey; + DBT saltData; + unsigned char saltbuf[16]; + int status; + + saltKey.data = SALT_STRING; + saltKey.size = sizeof(SALT_STRING) - 1; + + saltData.data = (void *)saltbuf; + saltData.size = sizeof(saltbuf); + RNG_GenerateGlobalRandomBytes(saltbuf, sizeof(saltbuf)); + + /* put global salt into the database now */ + status = (* handle->db->put)( handle->db, &saltKey, &saltData, 0); + if ( status ) { + return(SECFailure); + } + + return(SECSuccess); +} + +static char * +keyDBFilenameCallback(void *arg, int dbVersion) +{ + return(PORT_Strdup((char *)arg)); +} + +SECKEYKeyDBHandle * +SECKEY_OpenKeyDBFilename(char *dbname, PRBool readOnly) +{ + return(SECKEY_OpenKeyDB(readOnly, keyDBFilenameCallback, + (void *)dbname)); +} + +SECKEYKeyDBHandle * +SECKEY_OpenKeyDB(PRBool readOnly, SECKEYDBNameFunc namecb, void *cbarg) +{ + SECKEYKeyDBHandle *handle; + DBT versionKey; + DBT versionData; + int ret; + SECStatus rv; + int openflags; + char *dbname = NULL; + PRBool updated = PR_FALSE; + + handle = (SECKEYKeyDBHandle *)PORT_ZAlloc (sizeof(SECKEYKeyDBHandle)); + if (handle == NULL) { + PORT_SetError (SEC_ERROR_NO_MEMORY); + return NULL; + } + + versionKey.data = VERSION_STRING; + versionKey.size = sizeof(VERSION_STRING)-1; + + if ( readOnly ) { + openflags = O_RDONLY; + } else { + openflags = O_RDWR; + } + + dbname = (*namecb)(cbarg, PRIVATE_KEY_DB_FILE_VERSION); + if ( dbname == NULL ) { + goto loser; + } + + handle->db = dbopen( dbname, openflags, 0600, DB_HASH, 0 ); + + /* check for correct version number */ + if (handle->db != NULL) { + /* lookup version string in database */ + ret = (* handle->db->get)( handle->db, &versionKey, &versionData, 0 ); + + /* error accessing the database */ + if ( ret < 0 ) { + goto loser; + } + + if ( ret == 1 ) { + /* no version number record, reset the database */ + (* handle->db->close)( handle->db ); + handle->db = NULL; + + goto newdb; + + } + + handle->version = *( (unsigned char *)versionData.data); + + if (handle->version != PRIVATE_KEY_DB_FILE_VERSION ) { + /* bogus version number record, reset the database */ + (* handle->db->close)( handle->db ); + handle->db = NULL; + + goto newdb; + } + + } + +newdb: + + /* if first open fails, try to create a new DB */ + if ( handle->db == NULL ) { + if ( readOnly ) { + goto loser; + } + + handle->db = dbopen( dbname, + O_RDWR | O_CREAT | O_TRUNC, 0600, DB_HASH, 0 ); + + PORT_Free( dbname ); + dbname = NULL; + + /* if create fails then we lose */ + if ( handle->db == NULL ) { + goto loser; + } + + rv = makeGlobalVersion(handle); + if ( rv != SECSuccess ) { + goto loser; + } + + /* + * try to update from v2 db + */ + dbname = (*namecb)(cbarg, 2); + if ( dbname != NULL ) { + handle->updatedb = dbopen( dbname, O_RDONLY, 0600, DB_HASH, 0 ); + + if ( handle->updatedb ) { + + + /* + * Try to update the db using a null password. If the db + * doesn't have a password, then this will work. If it does + * have a password, then this will fail and we will do the + * update later + */ + rv = SECKEY_UpdateKeyDBPass1(handle); + if ( rv == SECSuccess ) { + updated = PR_TRUE; + } + } + + PORT_Free( dbname ); + dbname = NULL; + } + + /* we are using the old salt if we updated from an old db */ + if ( ! updated ) { + rv = makeGlobalSalt(handle); + if ( rv != SECSuccess ) { + goto loser; + } + } + + /* sync the database */ + ret = (* handle->db->sync)(handle->db, 0); + if ( ret ) { + goto loser; + } + } + + handle->global_salt = GetKeyDBGlobalSalt(handle); + if ( dbname ) + PORT_Free( dbname ); + return handle; + +loser: + + if ( dbname ) + PORT_Free( dbname ); + PORT_SetError(SEC_ERROR_BAD_DATABASE); + + if ( handle->db ) { + (* handle->db->close)(handle->db); + } + if ( handle->updatedb ) { + (* handle->updatedb->close)(handle->updatedb); + } + PORT_Free(handle); + return NULL; +} + +/* + * Close the database + */ +void +SECKEY_CloseKeyDB(SECKEYKeyDBHandle *handle) +{ + if (handle != NULL) { + if (handle == SECKEY_GetDefaultKeyDB()) { + SECKEY_SetDefaultKeyDB(NULL); + } + if (handle->db != NULL) { + (* handle->db->close)(handle->db); + } + PORT_Free(handle); + } +} + +/* Get the key database version */ +int +SECKEY_GetKeyDBVersion(SECKEYKeyDBHandle *handle) +{ + PORT_Assert(handle != NULL); + + return handle->version; +} + +/* + * Allow use of default key database, so that apps (such as mozilla) do + * not have to pass the handle all over the place. + */ + +static SECKEYKeyDBHandle *sec_default_key_db = NULL; + +void +SECKEY_SetDefaultKeyDB(SECKEYKeyDBHandle *handle) +{ + sec_default_key_db = handle; +} + +SECKEYKeyDBHandle * +SECKEY_GetDefaultKeyDB(void) +{ + return sec_default_key_db; +} + +/* + * Delete a private key that was stored in the database + */ +SECStatus +SECKEY_DeleteKey(SECKEYKeyDBHandle *handle, SECItem *pubkey) +{ + DBT namekey; + int ret; + + if (handle == NULL) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + return(SECFailure); + } + + /* set up db key and data */ + namekey.data = pubkey->data; + namekey.size = pubkey->len; + + /* delete it from the database */ + ret = (* handle->db->del)(handle->db, &namekey, 0); + if ( ret ) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + return(SECFailure); + } + + /* sync the database */ + ret = (* handle->db->sync)(handle->db, 0); + if ( ret ) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + return(SECFailure); + } + + return(SECSuccess); +} + +/* + * Store a key in the database, indexed by its public key modulus.(value!) + */ +SECStatus +SECKEY_StoreKeyByPublicKey(SECKEYKeyDBHandle *handle, + SECKEYLowPrivateKey *privkey, + SECItem *pubKeyData, + char *nickname, + SECKEYGetPasswordKey f, void *arg) +{ + return SECKEY_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData, nickname, + f, arg, SECKEY_GetDefaultKeyDBAlg()); +} + +/* see if the public key for this cert is in the database filed + * by modulus + */ +SECStatus +SECKEY_KeyForCertExists(SECKEYKeyDBHandle *handle, CERTCertificate *cert) +{ + SECKEYPublicKey *pubkey = NULL; + DBT namekey; + DBT dummy; + int status; + + /* get cert's public key */ + pubkey = CERT_ExtractPublicKey(cert); + if ( pubkey == NULL ) { + return SECFailure; + } + + /* TNH - make key from SECKEYPublicKey */ + switch (pubkey->keyType) { + case rsaKey: + namekey.data = pubkey->u.rsa.modulus.data; + namekey.size = pubkey->u.rsa.modulus.len; + break; + case dsaKey: + namekey.data = pubkey->u.dsa.publicValue.data; + namekey.size = pubkey->u.dsa.publicValue.len; + break; + case dhKey: + namekey.data = pubkey->u.dh.publicValue.data; + namekey.size = pubkey->u.dh.publicValue.len; + break; + default: + /* XXX We don't do Fortezza or DH yet. */ + return SECFailure; + } + + status = (* handle->db->get)(handle->db, &namekey, &dummy, 0); + SECKEY_DestroyPublicKey(pubkey); + if ( status ) { + /* TNH - should this really set an error? */ + PORT_SetError(SEC_ERROR_BAD_DATABASE); + return SECFailure; + } + + return SECSuccess; +} + +/* + * find the private key for a cert + */ +SECKEYLowPrivateKey * +SECKEY_FindKeyByCert(SECKEYKeyDBHandle *handle, CERTCertificate *cert, + SECKEYGetPasswordKey f, void *arg) +{ + SECKEYPublicKey *pubkey = NULL; + SECItem *keyItem; + SECKEYLowPrivateKey *privKey = NULL; + + /* get cert's public key */ + pubkey = CERT_ExtractPublicKey(cert); + if ( !pubkey ) { + goto loser; + } + + /* TNH - make record key from SECKEYPublicKey (again) */ + switch (pubkey->keyType) { + case rsaKey: + keyItem = &pubkey->u.rsa.modulus; + break; + case dsaKey: + keyItem = &pubkey->u.dsa.publicValue; + break; + case dhKey: + keyItem = &pubkey->u.dh.publicValue; + break; + /* fortezza an NULL keys are not stored in the data base */ + case fortezzaKey: + case nullKey: + goto loser; + } + + privKey = SECKEY_FindKeyByPublicKey(handle, keyItem, f, arg); + + /* success falls through */ +loser: + SECKEY_DestroyPublicKey(pubkey); + return(privKey); +} + +/* + * check to see if the user has a password + */ +SECStatus +SECKEY_HasKeyDBPassword(SECKEYKeyDBHandle *handle) +{ + DBT checkkey, checkdata; + int ret; + + if (handle == NULL) { + return(SECFailure); + } + + checkkey.data = KEYDB_PW_CHECK_STRING; + checkkey.size = KEYDB_PW_CHECK_LEN; + + ret = (* handle->db->get)(handle->db, &checkkey, &checkdata, 0 ); + if ( ret ) { + /* see if this was an updated DB first */ + checkkey.data = KEYDB_FAKE_PW_CHECK_STRING; + checkkey.size = KEYDB_FAKE_PW_CHECK_LEN; + ret = (* handle->db->get)(handle->db, &checkkey, &checkdata, 0 ); + if ( ret ) { + return(SECFailure); + } + } + + return(SECSuccess); +} + +/* + * Set up the password checker in the key database. + * This is done by encrypting a known plaintext with the user's key. + */ +SECStatus +SECKEY_SetKeyDBPassword(SECKEYKeyDBHandle *handle, SECItem *pwitem) +{ + return SECKEY_SetKeyDBPasswordAlg(handle, pwitem, + SECKEY_GetDefaultKeyDBAlg()); +} + +/* + * Re-encrypt the entire key database with a new password. + * NOTE: This really should create a new database rather than doing it + * in place in the original + */ +SECStatus +SECKEY_ChangeKeyDBPassword(SECKEYKeyDBHandle *handle, + SECItem *oldpwitem, SECItem *newpwitem) +{ + return SECKEY_ChangeKeyDBPasswordAlg(handle, oldpwitem, newpwitem, + SECKEY_GetDefaultKeyDBAlg()); +} + +static SECStatus +HashPassword(unsigned char *hashresult, char *pw, SECItem *salt) +{ + SHA1Context *cx; + unsigned int outlen; + cx = SHA1_NewContext(); + if ( cx == NULL ) { + return(SECFailure); + } + + SHA1_Begin(cx); + if ( ( salt != NULL ) && ( salt->data != NULL ) ) { + SHA1_Update(cx, salt->data, salt->len); + } + + SHA1_Update(cx, (unsigned char *)pw, PORT_Strlen(pw)); + SHA1_End(cx, hashresult, &outlen, SHA1_LENGTH); + + SHA1_DestroyContext(cx, PR_TRUE); + + return(SECSuccess); +} + +SECItem * +SECKEY_HashPassword(char *pw, SECItem *salt) +{ + SECItem *pwitem; + SECStatus rv; + + pwitem = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if ( pwitem == NULL ) { + return(NULL); + } + pwitem->len = SHA1_LENGTH; + pwitem->data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH); + if ( pwitem->data == NULL ) { + PORT_Free(pwitem); + return(NULL); + } + if ( pw ) { + rv = HashPassword(pwitem->data, pw, salt); + if ( rv != SECSuccess ) { + SECITEM_ZfreeItem(pwitem, PR_TRUE); + return(NULL); + } + } + + return(pwitem); +} + +/* Derive the actual password value for the database from a pw string */ +SECItem * +SECKEY_DeriveKeyDBPassword(SECKEYKeyDBHandle *keydb, char *pw) +{ + PORT_Assert(keydb != NULL); + PORT_Assert(pw != NULL); + if (keydb == NULL || pw == NULL) return(NULL); + + return SECKEY_HashPassword(pw, keydb->global_salt); +} + +#if 0 +/* Appears obsolete - TNH */ +/* get the algorithm with which a private key + * is encrypted. + */ +SECOidTag +seckey_get_private_key_algorithm(SECKEYKeyDBHandle *keydb, DBT *index) +{ + SECKEYDBKey *dbkey = NULL; + SECOidTag algorithm = SEC_OID_UNKNOWN; + SECKEYEncryptedPrivateKeyInfo *epki = NULL; + PLArenaPool *poolp = NULL; + SECStatus rv; + + poolp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if(poolp == NULL) + return (SECOidTag)SECFailure; /* TNH - this is bad */ + + dbkey = get_dbkey(keydb, index); + if(dbkey == NULL) + return (SECOidTag)SECFailure; + + epki = (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(poolp, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + if(epki == NULL) + goto loser; + rv = SEC_ASN1DecodeItem(poolp, epki, + SECKEY_EncryptedPrivateKeyInfoTemplate, &dbkey->derPK); + if(rv == SECFailure) + goto loser; + + algorithm = SECOID_GetAlgorithmTag(&epki->algorithm); + + /* let success fall through */ +loser: + if(poolp != NULL) + PORT_FreeArena(poolp, PR_TRUE);\ + if(dbkey != NULL) + sec_destroy_dbkey(dbkey); + + return algorithm; +} +#endif + +/* + * Derive an RC4 key from a password key and a salt. This + * was the method to used to encrypt keys in the version 2? + * database + */ +SECItem * +seckey_create_rc4_key(SECItem *pwitem, SECItem *salt) +{ + MD5Context *md5 = NULL; + unsigned int part; + SECStatus rv = SECFailure; + SECItem *key = NULL; + + key = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(key != NULL) + { + key->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) * + MD5_LENGTH); + key->len = MD5_LENGTH; + if(key->data != NULL) + { + md5 = MD5_NewContext(); + if ( md5 != NULL ) + { + MD5_Begin(md5); + MD5_Update(md5, salt->data, salt->len); + MD5_Update(md5, pwitem->data, pwitem->len); + MD5_End(md5, key->data, &part, MD5_LENGTH); + MD5_DestroyContext(md5, PR_TRUE); + rv = SECSuccess; + } + } + + if(rv != SECSuccess) + { + SECITEM_FreeItem(key, PR_TRUE); + key = NULL; + } + } + + return key; +} + +SECItem * +seckey_create_rc4_salt(void) +{ + SECItem *salt = NULL; + SECStatus rv = SECFailure; + + salt = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(salt == NULL) + return NULL; + + salt->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) * + SALT_LENGTH); + if(salt->data != NULL) + { + salt->len = SALT_LENGTH; + RNG_GenerateGlobalRandomBytes(salt->data, salt->len); + rv = SECSuccess; + } + + if(rv == SECFailure) + { + SECITEM_FreeItem(salt, PR_TRUE); + salt = NULL; + } + + return salt; +} + +SECItem * +seckey_rc4_cipher(SECItem *key, SECItem *src, PRBool encrypt) +{ + SECItem *dest = NULL; + RC4Context *ctxt = NULL; + SECStatus rv = SECFailure; + + if((key == NULL) || (src == NULL)) + return NULL; + + dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(dest == NULL) + return NULL; + + dest->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) * + (src->len + 64)); /* TNH - padding? */ + if(dest->data != NULL) + { + ctxt = RC4_CreateContext(key->data, key->len); + if(ctxt != NULL) + { + if(encrypt == PR_TRUE) + rv = RC4_Encrypt(ctxt, dest->data, &dest->len, + src->len + 64, src->data, src->len); + else + rv = RC4_Decrypt(ctxt, dest->data, &dest->len, + src->len + 64, src->data, src->len); + RC4_DestroyContext(ctxt, PR_TRUE); + } + } + + if(rv == SECFailure) + if(dest != NULL) + { + SECITEM_FreeItem(dest, PR_TRUE); + dest = NULL; + } + + return dest; +} + +/* TNH - keydb is unused */ +/* TNH - the pwitem should be the derived key for RC4 */ +SECKEYEncryptedPrivateKeyInfo * +seckey_encrypt_private_key( + SECKEYLowPrivateKey *pk, SECItem *pwitem, SECKEYKeyDBHandle *keydb, + SECOidTag algorithm) +{ + SECKEYEncryptedPrivateKeyInfo *epki = NULL; + SECKEYPrivateKeyInfo *pki = NULL; + SECStatus rv = SECFailure; + SECAlgorithmID *algid = NULL; + PLArenaPool *temparena = NULL, *permarena = NULL; + SECItem *key = NULL, *salt = NULL, *der_item = NULL; + SECItem *dummy = NULL, *dest = NULL; + + permarena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if(permarena == NULL) + return NULL; + + temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if(temparena == NULL) + goto loser; + + /* allocate structures */ + epki = (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(permarena, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + pki = (SECKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena, + sizeof(SECKEYPrivateKeyInfo)); + der_item = (SECItem *)PORT_ArenaZAlloc(temparena, sizeof(SECItem)); + if((epki == NULL) || (pki == NULL) || (der_item == NULL)) + goto loser; + + epki->arena = permarena; + + /* setup private key info */ + dummy = SEC_ASN1EncodeInteger(temparena, &(pki->version), + SEC_PRIVATE_KEY_INFO_VERSION); + if(dummy == NULL) + goto loser; + + /* Encode the key, and set the algorithm (with params) */ + switch (pk->keyType) { + case rsaKey: + dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk, + SECKEY_RSAPrivateKeyTemplate); + if (dummy == NULL) { + rv = SECFailure; + goto loser; + } + + rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm), + SEC_OID_PKCS1_RSA_ENCRYPTION, 0); + if (rv == SECFailure) { + goto loser; + } + + break; + case dsaKey: + dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk, + SECKEY_DSAPrivateKeyTemplate); + if (dummy == NULL) { + rv = SECFailure; + goto loser; + } + + dummy = SEC_ASN1EncodeItem(temparena, NULL, &pk->u.dsa.params, + SECKEY_PQGParamsTemplate); + if (dummy == NULL) { + rv = SECFailure; + goto loser; + } + + rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm), + SEC_OID_ANSIX9_DSA_SIGNATURE, dummy); + if (rv == SECFailure) { + goto loser; + } + + break; + case dhKey: + dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk, + SECKEY_DHPrivateKeyTemplate); + if (dummy == NULL) { + rv = SECFailure; + goto loser; + } + + rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm), + SEC_OID_X942_DIFFIE_HELMAN_KEY, dummy); + if (rv == SECFailure) { + goto loser; + } + break; + default: + /* We don't support DH or Fortezza private keys yet */ + PORT_Assert(PR_FALSE); + break; + } + + /* setup encrypted private key info */ + dummy = SEC_ASN1EncodeItem(temparena, der_item, pki, + SECKEY_PrivateKeyInfoTemplate); + if(dummy == NULL) { + rv = SECFailure; + goto loser; + } + + rv = SECFailure; /* assume failure */ + switch(algorithm) + { + case SEC_OID_RC4: + salt = seckey_create_rc4_salt(); + if(salt != NULL) + { + key = seckey_create_rc4_key(pwitem, salt); + if(key != NULL) + { + dest = seckey_rc4_cipher(key, der_item, PR_TRUE); + if(dest != NULL) + { + rv = SECITEM_CopyItem(permarena, &epki->encryptedData, + dest); + if(rv == SECSuccess) + rv = SECOID_SetAlgorithmID(permarena, + &epki->algorithm, SEC_OID_RC4, salt); + } + } + } + if(dest != NULL) + SECITEM_FreeItem(dest, PR_TRUE); + if(key != NULL) + SECITEM_ZfreeItem(key, PR_TRUE); + break; + default: + algid = SEC_PKCS5CreateAlgorithmID(algorithm, NULL, 1); + if(algid != NULL) + { + dest = SEC_PKCS5CipherData(algid, pwitem, + der_item, PR_TRUE, NULL); + if(dest != NULL) + { + rv = SECITEM_CopyItem(permarena, &epki->encryptedData, + dest); + if(rv == SECSuccess) + rv = SECOID_CopyAlgorithmID(permarena, + &epki->algorithm, algid); + } + } + if(dest != NULL) + SECITEM_FreeItem(dest, PR_TRUE); + if(algid != NULL) + SECOID_DestroyAlgorithmID(algid, PR_TRUE); + break; + } + + /* let success fall through */ +loser: + + if(rv == SECFailure) + { + PORT_FreeArena(permarena, PR_TRUE); + epki = NULL; + } + + if(temparena != NULL) + PORT_FreeArena(temparena, PR_TRUE); + + if(salt != NULL) + SECITEM_FreeItem(salt, PR_TRUE); + + return epki; +} + +SECStatus +seckey_put_private_key(SECKEYKeyDBHandle *keydb, DBT *index, SECItem *pwitem, + SECKEYLowPrivateKey *pk, char *nickname, PRBool update, + SECOidTag algorithm) +{ + SECKEYDBKey *dbkey = NULL; + SECKEYEncryptedPrivateKeyInfo *epki = NULL; + PLArenaPool *temparena = NULL, *permarena = NULL; + SECItem *dummy = NULL; + SECItem *salt = NULL; + SECStatus rv = SECFailure; + + if((keydb == NULL) || (index == NULL) || (pwitem == NULL) || + (pk == NULL)) + return SECFailure; + + permarena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if(permarena == NULL) + return SECFailure; + + dbkey = (SECKEYDBKey *)PORT_ArenaZAlloc(permarena, sizeof(SECKEYDBKey)); + if(dbkey == NULL) + goto loser; + dbkey->arena = permarena; + dbkey->nickname = nickname; + + /* TNH - for RC4, the salt should be created here */ + + epki = seckey_encrypt_private_key(pk, pwitem, keydb, algorithm); + if(epki == NULL) + goto loser; + temparena = epki->arena; + + /* extract salt for db key */ + switch(algorithm) + { + case SEC_OID_RC4: + rv = SECITEM_CopyItem(permarena, &(dbkey->salt), + &(epki->algorithm.parameters)); + epki->algorithm.parameters.len = 0; + epki->algorithm.parameters.data = NULL; + break; + default: + /* TNH - this should not be necessary */ + salt = SEC_PKCS5GetSalt(&epki->algorithm); + if(salt != NULL) + { + rv = SECITEM_CopyItem(permarena, &(dbkey->salt), salt); + SECITEM_ZfreeItem(salt, PR_TRUE); + } + break; + } + + dummy = SEC_ASN1EncodeItem(permarena, &(dbkey->derPK), epki, + SECKEY_EncryptedPrivateKeyInfoTemplate); + if(dummy == NULL) + rv = SECFailure; + else + rv = put_dbkey(keydb, index, dbkey, update); + + /* let success fall through */ +loser: + if(rv != SECSuccess) + if(permarena != NULL) + PORT_FreeArena(permarena, PR_TRUE); + if(temparena != NULL) + PORT_FreeArena(temparena, PR_TRUE); + + return rv; +} + +/* + * Store a key in the database, indexed by its public key modulus. + * Note that the nickname is optional. It was only used by keyutil. + */ +SECStatus +SECKEY_StoreKeyByPublicKeyAlg(SECKEYKeyDBHandle *handle, + SECKEYLowPrivateKey *privkey, + SECItem *pubKeyData, + char *nickname, + SECKEYGetPasswordKey f, void *arg, + SECOidTag algorithm) +{ + DBT namekey; + SECItem *pwitem = NULL; + SECStatus rv; + + if (handle == NULL) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + return(SECFailure); + } + + /* set up db key and data */ + namekey.data = pubKeyData->data; + namekey.size = pubKeyData->len; + + pwitem = (*f )(arg, handle); + if ( pwitem == NULL ) { + return(SECFailure); + } + + /* encrypt the private key */ + rv = seckey_put_private_key(handle, &namekey, pwitem, privkey, nickname, + PR_FALSE, algorithm); + SECITEM_ZfreeItem(pwitem, PR_TRUE); + + return(rv); +} + +SECKEYLowPrivateKey * +seckey_decrypt_private_key(SECKEYEncryptedPrivateKeyInfo *epki, + SECItem *pwitem) +{ + SECKEYLowPrivateKey *pk = NULL; + SECKEYPrivateKeyInfo *pki = NULL; + SECStatus rv = SECFailure; + SECOidTag algorithm; + PLArenaPool *temparena = NULL, *permarena = NULL; + SECItem *salt = NULL, *dest = NULL, *key = NULL; + + if((epki == NULL) || (pwitem == NULL)) + goto loser; + + temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + permarena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if((temparena == NULL) || (permarena == NULL)) + goto loser; + + /* allocate temporary items */ + pki = (SECKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena, + sizeof(SECKEYPrivateKeyInfo)); + + /* allocate permanent arena items */ + pk = (SECKEYLowPrivateKey *)PORT_ArenaZAlloc(permarena, + sizeof(SECKEYLowPrivateKey)); + + if((pk == NULL) || (pki == NULL)) + goto loser; + + pk->arena = permarena; + + algorithm = SECOID_GetAlgorithmTag(&(epki->algorithm)); + switch(algorithm) + { + case SEC_OID_RC4: + salt = SECITEM_DupItem(&epki->algorithm.parameters); + if(salt != NULL) + { + key = seckey_create_rc4_key(pwitem, salt); + if(key != NULL) + { + dest = seckey_rc4_cipher(key, &epki->encryptedData, + PR_FALSE); + } + } + if(salt != NULL) + SECITEM_ZfreeItem(salt, PR_TRUE); + if(key != NULL) + SECITEM_ZfreeItem(key, PR_TRUE); + break; + default: + /* we depend on the fact that if this key was encoded with + * DES, that the pw was also encoded with DES, so we don't have + * to do the update here, the password code will handle it. */ + dest = SEC_PKCS5CipherData(&epki->algorithm, pwitem, + &epki->encryptedData, PR_FALSE, NULL); + break; + } + + if(dest != NULL) + { + rv = SEC_ASN1DecodeItem(temparena, pki, + SECKEY_PrivateKeyInfoTemplate, dest); + if(rv == SECSuccess) + { + switch(SECOID_GetAlgorithmTag(&pki->algorithm)) { + case SEC_OID_X500_RSA_ENCRYPTION: + case SEC_OID_PKCS1_RSA_ENCRYPTION: + pk->keyType = rsaKey; + rv = SEC_ASN1DecodeItem(permarena, pk, + SECKEY_RSAPrivateKeyTemplate, + &pki->privateKey); + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + pk->keyType = dsaKey; + rv = SEC_ASN1DecodeItem(permarena, pk, + SECKEY_DSAPrivateKeyTemplate, + &pki->privateKey); + if (rv != SECSuccess) + goto loser; + rv = SEC_ASN1DecodeItem(permarena, &pk->u.dsa.params, + SECKEY_PQGParamsTemplate, + &pki->algorithm.parameters); + break; + case SEC_OID_X942_DIFFIE_HELMAN_KEY: + pk->keyType = dhKey; + rv = SEC_ASN1DecodeItem(permarena, pk, + SECKEY_DHPrivateKeyTemplate, + &pki->privateKey); + break; + default: + rv = SECFailure; + break; + } + } + else if(PORT_GetError() == SEC_ERROR_BAD_DER) + { + PORT_SetError(SEC_ERROR_BAD_PASSWORD); + goto loser; + } + } + + /* let success fall through */ +loser: + if(temparena != NULL) + PORT_FreeArena(temparena, PR_TRUE); + if(dest != NULL) + SECITEM_ZfreeItem(dest, PR_TRUE); + + if(rv != SECSuccess) + { + if(permarena != NULL) + PORT_FreeArena(permarena, PR_TRUE); + pk = NULL; + } + + return pk; +} + +static SECKEYLowPrivateKey * +seckey_decode_encrypted_private_key(SECKEYDBKey *dbkey, SECItem *pwitem) +{ + SECKEYLowPrivateKey *pk = NULL; + SECKEYEncryptedPrivateKeyInfo *epki; + PLArenaPool *temparena = NULL; + SECStatus rv; + SECOidTag algorithm; + + if( ( dbkey == NULL ) || ( pwitem == NULL ) ) { + return NULL; + } + + temparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if(temparena == NULL) { + return NULL; + } + + epki = (SECKEYEncryptedPrivateKeyInfo *) + PORT_ArenaZAlloc(temparena, sizeof(SECKEYEncryptedPrivateKeyInfo)); + + if(epki == NULL) { + goto loser; + } + + rv = SEC_ASN1DecodeItem(temparena, epki, + SECKEY_EncryptedPrivateKeyInfoTemplate, + &(dbkey->derPK)); + if(rv != SECSuccess) { + goto loser; + } + + algorithm = SECOID_GetAlgorithmTag(&(epki->algorithm)); + switch(algorithm) + { + case SEC_OID_RC4: + /* TNH - this code should derive the actual RC4 key from salt and + pwitem */ + rv = SECITEM_CopyItem(temparena, &(epki->algorithm.parameters), + &(dbkey->salt)); + break; + default: + break; + } + + pk = seckey_decrypt_private_key(epki, pwitem); + + /* let success fall through */ +loser: + + PORT_FreeArena(temparena, PR_TRUE); + return pk; +} + +SECKEYLowPrivateKey * +seckey_get_private_key(SECKEYKeyDBHandle *keydb, DBT *index, char **nickname, + SECItem *pwitem) +{ + SECKEYDBKey *dbkey = NULL; + SECKEYLowPrivateKey *pk = NULL; + + if( ( keydb == NULL ) || ( index == NULL ) || ( pwitem == NULL ) ) { + return NULL; + } + + dbkey = get_dbkey(keydb, index); + if(dbkey == NULL) { + goto loser; + } + + if ( nickname ) { + if ( dbkey->nickname && ( dbkey->nickname[0] != 0 ) ) { + *nickname = PORT_Strdup(dbkey->nickname); + } else { + *nickname = NULL; + } + } + + pk = seckey_decode_encrypted_private_key(dbkey, pwitem); + + /* let success fall through */ +loser: + + if ( dbkey != NULL ) { + sec_destroy_dbkey(dbkey); + } + + return pk; +} + +/* + * used by pkcs11 to import keys into it's object format... In the future + * we really need a better way to tie in... + */ +SECKEYLowPrivateKey * +SECKEY_DecryptKey(DBT *key, SECItem *pwitem, + SECKEYKeyDBHandle *handle) { + return seckey_get_private_key(handle,key,NULL,pwitem); +} + +/* + * Find a key in the database, indexed by its public key modulus + * This is used to find keys that have been stored before their + * certificate arrives. Once the certificate arrives the key + * is looked up by the public modulus in the certificate, and the + * re-stored by its nickname. + */ +SECKEYLowPrivateKey * +SECKEY_FindKeyByPublicKey(SECKEYKeyDBHandle *handle, SECItem *modulus, + SECKEYGetPasswordKey f, void *arg) +{ + DBT namekey; + SECKEYLowPrivateKey *pk = NULL; + SECItem *pwitem = NULL; + + if (handle == NULL) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + return NULL; + } + + /* set up db key */ + namekey.data = modulus->data; + namekey.size = modulus->len; + + pwitem = (*f )(arg, handle); + if ( pwitem == NULL ) { + return(NULL); + } + + pk = seckey_get_private_key(handle, &namekey, NULL, pwitem); + SECITEM_ZfreeItem(pwitem, PR_TRUE); + + /* no need to free dbkey, since its on the stack, and the data it + * points to is owned by the database + */ + return(pk); +} + +/* ===== ENCODING ROUTINES ===== */ + +static SECStatus +encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg, + SECItem *encCheck) +{ + SECOidData *oidData; + SECStatus rv; + + oidData = SECOID_FindOIDByTag(alg); + if ( oidData == NULL ) { + rv = SECFailure; + goto loser; + } + + entry->len = 1 + oidData->oid.len + encCheck->len; + if ( arena ) { + entry->data = (unsigned char *)PORT_ArenaAlloc(arena, entry->len); + } else { + entry->data = (unsigned char *)PORT_Alloc(entry->len); + } + + if ( entry->data == NULL ) { + goto loser; + } + + /* first length of oid */ + entry->data[0] = (unsigned char)oidData->oid.len; + /* next oid itself */ + PORT_Memcpy(&entry->data[1], oidData->oid.data, oidData->oid.len); + /* finally the encrypted check string */ + PORT_Memcpy(&entry->data[1+oidData->oid.len], encCheck->data, + encCheck->len); + + return(SECSuccess); + +loser: + return(SECFailure); +} + +/* + * Set up the password checker in the key database. + * This is done by encrypting a known plaintext with the user's key. + */ +SECStatus +SECKEY_SetKeyDBPasswordAlg(SECKEYKeyDBHandle *handle, + SECItem *pwitem, SECOidTag algorithm) +{ + DBT checkkey; + SECAlgorithmID *algid = NULL; + SECStatus rv = SECFailure; + SECKEYDBKey *dbkey = NULL; + PLArenaPool *arena; + SECItem *key = NULL, *salt = NULL; + SECItem *dest = NULL, test_key; + + if (handle == NULL) { + return(SECFailure); + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( arena == NULL ) { + rv = SECFailure; + goto loser; + } + + dbkey = (SECKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(SECKEYDBKey)); + if ( dbkey == NULL ) { + rv = SECFailure; + goto loser; + } + + dbkey->arena = arena; + + /* encrypt key */ + checkkey.data = test_key.data = (unsigned char *)KEYDB_PW_CHECK_STRING; + checkkey.size = test_key.len = KEYDB_PW_CHECK_LEN; + + salt = seckey_create_rc4_salt(); + if(salt == NULL) { + rv = SECFailure; + goto loser; + } + + switch(algorithm) + { + case SEC_OID_RC4: + key = seckey_create_rc4_key(pwitem, salt); + if(key != NULL) + { + dest = seckey_rc4_cipher(key, &test_key, PR_TRUE); + SECITEM_FreeItem(key, PR_TRUE); + } + break; + default: + algid = SEC_PKCS5CreateAlgorithmID(algorithm, salt, 1); + if(algid != NULL) + dest = SEC_PKCS5CipherData(algid, pwitem, &test_key, + PR_TRUE, NULL); + break; + } + + if(dest != NULL) + { + rv = SECITEM_CopyItem(arena, &dbkey->salt, salt); + if(rv == SECFailure) + goto loser; + + rv = encodePWCheckEntry(arena, &dbkey->derPK, algorithm, dest); + + if ( rv != SECSuccess ) { + goto loser; + } + + rv = put_dbkey(handle, &checkkey, dbkey, PR_TRUE); + } else { + rv = SECFailure; + } + + /* let success fall through */ +loser: + if ( arena != NULL ) { + PORT_FreeArena(arena, PR_TRUE); + } + + if ( dest != NULL ) { + SECITEM_ZfreeItem(dest, PR_TRUE); + } + + if ( salt != NULL ) { + SECITEM_ZfreeItem(salt, PR_TRUE); + } + + return(rv); +} + +static PRBool +seckey_HasAServerKey(DB *db) +{ + DBT checkKey; + DBT checkData; + DBT key; + DBT data; + int ret; + PRBool found = PR_FALSE; + + ret = (* db->seq)(db, &key, &data, R_FIRST); + if ( ret ) { + return PR_FALSE; + } + + do { + /* skip version record */ + if ( data.size > 1 ) { + /* skip salt */ + if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) { + if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) { + continue; + } + } + /* skip pw check entry */ + if ( key.size == KEYDB_PW_CHECK_LEN ) { + if ( PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING, + KEYDB_PW_CHECK_LEN) == 0 ) { + continue; + } + } + + /* keys stored by nickname will have 0 as the last byte of the + * db key. Other keys must be stored by modulus. We will not + * update those because they are left over from a keygen that + * never resulted in a cert. + */ + if ( ((unsigned char *)key.data)[key.size-1] != 0 ) { + continue; + } + + if (PORT_Strcmp(key.data,"Server-Key") == 0) { + found = PR_TRUE; + break; + } + + } + } while ( (* db->seq)(db, &key, &data, R_NEXT) == 0 ); + + return found; +} + +static SECStatus +seckey_CheckKeyDB1Password(SECKEYKeyDBHandle *handle, SECItem *pwitem) +{ + SECStatus rv = SECFailure; + keyList keylist; + keyNode *node = NULL; + SECKEYLowPrivateKey *privkey = NULL; + + + /* + * first find a key + */ + + /* traverse the database, collecting the keys of all records */ + keylist.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( keylist.arena == NULL ) + { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return(SECFailure); + } + keylist.head = NULL; + + /* TNH - TraverseKeys should not be public, since it exposes + the underlying DBT data type. */ + rv = SECKEY_TraverseKeys(handle, sec_add_key_to_list, (void *)&keylist); + if ( rv != SECSuccess ) + goto done; + + /* just get the first key from the list */ + node = keylist.head; + + /* no private keys, accept any password */ + if (node == NULL) { + rv = SECSuccess; + goto done; + } + privkey = seckey_get_private_key(handle, &node->key, NULL, pwitem); + if (privkey == NULL) { + rv = SECFailure; + goto done; + } + + /* if we can decrypt the private key, then we had the correct password */ + rv = SECSuccess; + SECKEY_LowDestroyPrivateKey(privkey); + +done: + + /* free the arena */ + if ( keylist.arena ) { + PORT_FreeArena(keylist.arena, PR_FALSE); + } + + return(rv); +} + +/* + * check to see if the user has typed the right password + */ +SECStatus +SECKEY_CheckKeyDBPassword(SECKEYKeyDBHandle *handle, SECItem *pwitem) +{ + DBT checkkey; + DBT checkdata; + SECAlgorithmID *algid = NULL; + SECStatus rv = SECFailure; + SECKEYDBKey *dbkey = NULL; + SECItem *key = NULL; + SECItem *dest = NULL; + SECOidTag algorithm; + SECItem oid; + SECItem encstring; + PRBool update = PR_FALSE; + int ret; + + if (handle == NULL) { + goto loser; + } + + checkkey.data = KEYDB_PW_CHECK_STRING; + checkkey.size = KEYDB_PW_CHECK_LEN; + + dbkey = get_dbkey(handle, &checkkey); + + if ( dbkey == NULL ) { + checkkey.data = KEYDB_FAKE_PW_CHECK_STRING; + checkkey.size = KEYDB_FAKE_PW_CHECK_LEN; + ret = (* handle->db->get)(handle->db, &checkkey, + &checkdata, 0 ); + if (ret) { + goto loser; + } + /* if we have the fake PW_CHECK, then try to decode the key + * rather than the pwcheck item. + */ + rv = seckey_CheckKeyDB1Password(handle,pwitem); + if (rv == SECSuccess) { + /* OK we have enough to complete our conversion */ + SECKEY_UpdateKeyDBPass2(handle,pwitem); + } + return rv; + } + + /* build the oid item */ + oid.len = dbkey->derPK.data[0]; + oid.data = &dbkey->derPK.data[1]; + + /* make sure entry is the correct length + * since we are probably using a block cipher, the block will be + * padded, so we may get a bit more than the exact size we need. + */ + if ( dbkey->derPK.len < (KEYDB_PW_CHECK_LEN + 1 + oid.len ) ) { + goto loser; + } + + /* find the algorithm tag */ + algorithm = SECOID_FindOIDTag(&oid); + + /* make a secitem of the encrypted check string */ + encstring.len = dbkey->derPK.len - ( oid.len + 1 ); + encstring.data = &dbkey->derPK.data[oid.len+1]; + + switch(algorithm) + { + case SEC_OID_RC4: + key = seckey_create_rc4_key(pwitem, &dbkey->salt); + if(key != NULL) { + dest = seckey_rc4_cipher(key, &encstring, PR_FALSE); + SECITEM_FreeItem(key, PR_TRUE); + } + break; + default: + algid = SEC_PKCS5CreateAlgorithmID(algorithm, + &dbkey->salt, 1); + if(algid != NULL) { + /* Decrypt - this function implements a workaround for + * a previous coding error. It will decrypt values using + * DES rather than 3DES, if the initial try at 3DES + * decryption fails. In this case, the update flag is + * set to TRUE. This indication is used later to force + * an update of the database to "real" 3DES encryption. + */ + dest = SEC_PKCS5CipherData(algid, pwitem, + &encstring, PR_FALSE, &update); + SECOID_DestroyAlgorithmID(algid, PR_TRUE); + } + break; + } + + if(dest == NULL) { + goto loser; + } + + if ((dest->len == KEYDB_PW_CHECK_LEN) && + (PORT_Memcmp(dest->data, + KEYDB_PW_CHECK_STRING, KEYDB_PW_CHECK_LEN) == 0)) { + rv = SECSuccess; + /* we succeeded */ + if ( algorithm == SEC_OID_RC4 ) { + /* partially updated database */ + SECKEY_UpdateKeyDBPass2(handle, pwitem); + } + /* Force an update of the password to remove the incorrect DES + * encryption (see the note above) + */ + if (update && + (algorithm == SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC)) { + /* data base was encoded with DES not triple des, fix it */ + SECKEY_UpdateKeyDBPass2(handle,pwitem); + } + } + +loser: + sec_destroy_dbkey(dbkey); + if(dest != NULL) { + SECITEM_ZfreeItem(dest, PR_TRUE); + } + + return(rv); +} + +/* + * Change the database password and/or algorithm. This internal + * routine does not check the old password. That must be done by + * the caller. + */ +static SECStatus +ChangeKeyDBPasswordAlg(SECKEYKeyDBHandle *handle, + SECItem *oldpwitem, SECItem *newpwitem, + SECOidTag new_algorithm) +{ + SECStatus rv; + keyList keylist; + keyNode *node = NULL; + SECKEYLowPrivateKey *privkey = NULL; + char *nickname; + DBT newkey; + int ret; + + /* traverse the database, collecting the keys of all records */ + keylist.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( keylist.arena == NULL ) + { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return(SECFailure); + } + keylist.head = NULL; + + /* TNH - TraverseKeys should not be public, since it exposes + the underlying DBT data type. */ + rv = SECKEY_TraverseKeys(handle, sec_add_key_to_list, (void *)&keylist); + if ( rv != SECSuccess ) + goto loser; + + /* walk the list, re-encrypting each entry */ + node = keylist.head; + while ( node != NULL ) + { + privkey = seckey_get_private_key(handle, &node->key, &nickname, + oldpwitem); + + if (privkey == NULL) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + rv = SECFailure; + goto loser; + } + + /* delete the old record */ + ret = (* handle->db->del)(handle->db, &node->key, 0); + if ( ret ) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + rv = SECFailure; + goto loser; + } + + /* get the public key, which we use as the database index */ + + switch (privkey->keyType) { + case rsaKey: + newkey.data = privkey->u.rsa.modulus.data; + newkey.size = privkey->u.rsa.modulus.len; + break; + case dsaKey: + newkey.data = privkey->u.dsa.publicValue.data; + newkey.size = privkey->u.dsa.publicValue.len; + break; + case dhKey: + newkey.data = privkey->u.dh.publicValue.data; + newkey.size = privkey->u.dh.publicValue.len; + break; + default: + /* XXX We don't do Fortezza. */ + return SECFailure; + } + + rv = seckey_put_private_key(handle, &newkey, newpwitem, privkey, + nickname, PR_TRUE, new_algorithm); + + if ( rv != SECSuccess ) + { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + rv = SECFailure; + goto loser; + } + + /* next node */ + node = node->next; + } + + rv = SECKEY_SetKeyDBPasswordAlg(handle, newpwitem, new_algorithm); + +loser: + + /* free the arena */ + if ( keylist.arena ) { + PORT_FreeArena(keylist.arena, PR_FALSE); + } + + return(rv); +} + +/* + * Re-encrypt the entire key database with a new password. + * NOTE: The really should create a new database rather than doing it + * in place in the original + */ +SECStatus +SECKEY_ChangeKeyDBPasswordAlg(SECKEYKeyDBHandle *handle, + SECItem *oldpwitem, SECItem *newpwitem, + SECOidTag new_algorithm) +{ + SECStatus rv; + + if (handle == NULL) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + rv = SECFailure; + goto loser; + } + + rv = SECKEY_CheckKeyDBPassword(handle, oldpwitem); + if ( rv != SECSuccess ) { + return(SECFailure); /* return rv? */ + } + + rv = ChangeKeyDBPasswordAlg(handle, oldpwitem, newpwitem, new_algorithm); + +loser: + return(rv); +} + +/* + * Second pass of updating the key db. This time we have a password. + */ +SECStatus +SECKEY_UpdateKeyDBPass2(SECKEYKeyDBHandle *handle, SECItem *pwitem) +{ + SECStatus rv; + + rv = ChangeKeyDBPasswordAlg(handle, pwitem, pwitem, + SECKEY_GetDefaultKeyDBAlg()); + + return(rv); +} + +/* + * currently updates key database from v2 to v3 + */ +SECStatus +SECKEY_UpdateKeyDBPass1(SECKEYKeyDBHandle *handle) +{ + SECStatus rv; + DBT versionKey; + DBT versionData; + DBT checkKey; + DBT checkData; + DBT saltKey; + DBT saltData; + DBT key; + DBT data; + SECItem *rc4key = NULL; + SECKEYDBKey *dbkey = NULL; + SECItem *oldSalt = NULL; + int ret; + SECItem checkitem; + + if ( handle->updatedb == NULL ) { + return(SECSuccess); + } + + /* + * check the version record + */ + versionKey.data = VERSION_STRING; + versionKey.size = sizeof(VERSION_STRING)-1; + + ret = (* handle->updatedb->get)(handle->updatedb, &versionKey, + &versionData, 0 ); + + if (ret) { + /* no version record, so old db never used */ + goto done; + } + + if ( ( versionData.size != 1 ) || + ( *((unsigned char *)versionData.data) != 2 ) ) { + /* corrupt or wrong version number so don't update */ + goto done; + } + + saltKey.data = SALT_STRING; + saltKey.size = sizeof(SALT_STRING) - 1; + + ret = (* handle->updatedb->get)(handle->updatedb, &saltKey, &saltData, 0); + if ( ret ) { + /* no salt in old db, so it is corrupted */ + goto done; + } + + oldSalt = decodeKeyDBGlobalSalt(&saltData); + if ( oldSalt == NULL ) { + /* bad salt in old db, so it is corrupted */ + goto done; + } + + /* + * look for a pw check entry + */ + checkKey.data = KEYDB_PW_CHECK_STRING; + checkKey.size = KEYDB_PW_CHECK_LEN; + + ret = (* handle->updatedb->get)(handle->updatedb, &checkKey, + &checkData, 0 ); + if (ret) { + /* + * if we have a key, but no KEYDB_PW_CHECK_STRING, then this must + * be an old server database, and it does have a password associated + * with it. Put a fake entry in so we can identify this db when we do + * get the password for it. + */ + if (seckey_HasAServerKey(handle->updatedb)) { + DBT fcheckKey; + DBT fcheckData; + + /* + * include a fake string + */ + fcheckKey.data = KEYDB_FAKE_PW_CHECK_STRING; + fcheckKey.size = KEYDB_FAKE_PW_CHECK_LEN; + fcheckData.data = "1"; + fcheckData.size = 1; + /* put global salt into the new database now */ + ret = (* handle->db->put)( handle->db, &saltKey, &saltData, 0); + if ( ret ) { + goto done; + } + ret = (* handle->db->put)( handle->db, &fcheckKey, &fcheckData, 0); + if ( ret ) { + goto done; + } + } else { + goto done; + } + } else { + /* put global salt into the new database now */ + ret = (* handle->db->put)( handle->db, &saltKey, &saltData, 0); + if ( ret ) { + goto done; + } + + dbkey = decode_dbkey(&checkData, 2); + if ( dbkey == NULL ) { + goto done; + } + checkitem = dbkey->derPK; + dbkey->derPK.data = NULL; + + /* format the new pw check entry */ + rv = encodePWCheckEntry(NULL, &dbkey->derPK, SEC_OID_RC4, &checkitem); + if ( rv != SECSuccess ) { + goto done; + } + + rv = put_dbkey(handle, &checkKey, dbkey, PR_TRUE); + if ( rv != SECSuccess ) { + goto done; + } + + /* free the dbkey */ + sec_destroy_dbkey(dbkey); + dbkey = NULL; + } + + + /* now traverse the database */ + ret = (* handle->updatedb->seq)(handle->updatedb, &key, &data, R_FIRST); + if ( ret ) { + goto done; + } + + do { + /* skip version record */ + if ( data.size > 1 ) { + /* skip salt */ + if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) { + if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) { + continue; + } + } + /* skip pw check entry */ + if ( key.size == checkKey.size ) { + if ( PORT_Memcmp(key.data, checkKey.data, key.size) == 0 ) { + continue; + } + } + + /* keys stored by nickname will have 0 as the last byte of the + * db key. Other keys must be stored by modulus. We will not + * update those because they are left over from a keygen that + * never resulted in a cert. + */ + if ( ((unsigned char *)key.data)[key.size-1] != 0 ) { + continue; + } + + dbkey = decode_dbkey(&data, 2); + if ( dbkey == NULL ) { + continue; + } + + /* This puts the key into the new database with the same + * index (nickname) that it had before. The second pass + * of the update will have the password. It will decrypt + * and re-encrypt the entries using a new algorithm. + */ + dbkey->nickname = (char *)key.data; + rv = put_dbkey(handle, &key, dbkey, PR_FALSE); + dbkey->nickname = NULL; + + sec_destroy_dbkey(dbkey); + } + } while ( (* handle->updatedb->seq)(handle->updatedb, &key, &data, + R_NEXT) == 0 ); + + dbkey = NULL; + +done: + /* sync the database */ + ret = (* handle->db->sync)(handle->db, 0); + + (* handle->updatedb->close)(handle->updatedb); + handle->updatedb = NULL; + + if ( rc4key ) { + SECITEM_FreeItem(rc4key, PR_TRUE); + } + + if ( oldSalt ) { + SECITEM_FreeItem(oldSalt, PR_TRUE); + } + + if ( dbkey ) { + sec_destroy_dbkey(dbkey); + } + + return(SECSuccess); +} + +/* + * Clear out all the keys in the existing database + */ +SECStatus +SECKEY_ResetKeyDB(SECKEYKeyDBHandle *handle) +{ + SECStatus rv; + DBT key; + DBT data; + int ret; + int errors = 0; + + if ( handle->db == NULL ) { + return(SECSuccess); + } + + + /* now traverse the database */ + ret = (* handle->db->seq)(handle->db, &key, &data, R_FIRST); + if ( ret ) { + goto done; + } + + do { + /* delete each entry */ + ret = (* handle->db->del)(handle->db, &key, 0); + if ( ret ) errors++; + + } while ( (* handle->db->seq)(handle->db, &key, &data, + R_NEXT) == 0 ); + rv = makeGlobalVersion(handle); + if ( rv != SECSuccess ) { + errors++; + goto done; + } + + rv = makeGlobalSalt(handle); + if ( rv != SECSuccess ) { + errors++; + goto done; + } + + if (handle->global_salt) { + SECITEM_FreeItem(handle->global_salt,PR_TRUE); + } + handle->global_salt = GetKeyDBGlobalSalt(handle); + +done: + /* sync the database */ + ret = (* handle->db->sync)(handle->db, 0); + + return (errors == 0 ? SECSuccess : SECFailure); +} + +/* These functions simply return the address of the above-declared templates. +** This is necessary for Windows DLLs. Sigh. +*/ +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PrivateKeyInfoTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PointerToPrivateKeyInfoTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_EncryptedPrivateKeyInfoTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PointerToEncryptedPrivateKeyInfoTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_DSAPublicKeyTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_RSAPublicKeyTemplate) + diff --git a/security/nss/lib/softoken/private.h b/security/nss/lib/softoken/private.h new file mode 100644 index 000000000..b90ceaaea --- /dev/null +++ b/security/nss/lib/softoken/private.h @@ -0,0 +1,78 @@ +/* + * private.h - Private data structures for the software token library + * + * 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. + * + * $Id$ + */ + +#ifndef _PRIVATE_H_ +#define _PRIVATE_H_ + +#include "nspr.h" +#include "seccomon.h" +#include "mcom_db.h" + +/* + * Handle structure for open key databases + */ +struct SECKEYKeyDBHandleStr { + DB *db; + DB *updatedb; /* used when updating an old version */ + SECItem *global_salt; /* password hashing salt for this db */ + int version; /* version of the database */ +}; + +/* +** Typedef for callback for traversing key database. +** "key" is the key used to index the data in the database (nickname) +** "data" is the key data +** "pdata" is the user's data +*/ +typedef SECStatus (* SECKEYTraverseKeysFunc)(DBT *key, DBT *data, void *pdata); + + +SEC_BEGIN_PROTOS + +/* +** Traverse the entire key database, and pass the nicknames and keys to a +** user supplied function. +** "f" is the user function to call for each key +** "udata" is the user's data, which is passed through to "f" +*/ +extern SECStatus SECKEY_TraverseKeys(SECKEYKeyDBHandle *handle, + SECKEYTraverseKeysFunc f, + void *udata); + +SEC_END_PROTOS + +#endif /* _PRIVATE_H_ */ diff --git a/security/nss/lib/util/secerr.h b/security/nss/lib/util/secerr.h new file mode 100644 index 000000000..8b152f8e4 --- /dev/null +++ b/security/nss/lib/util/secerr.h @@ -0,0 +1,187 @@ +/* + * 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. + */ + +#ifndef __SEC_ERR_H_ +#define __SEC_ERR_H_ + + +#define SEC_ERROR_BASE (-0x2000) +#define SEC_ERROR_LIMIT (SEC_ERROR_BASE + 1000) + +#define IS_SEC_ERROR(code) \ + (((code) >= SEC_ERROR_BASE) && ((code) < SEC_ERROR_LIMIT)) + +#ifndef NO_SECURITY_ERROR_ENUM +typedef enum { +SEC_ERROR_IO = SEC_ERROR_BASE + 0, +SEC_ERROR_LIBRARY_FAILURE = SEC_ERROR_BASE + 1, +SEC_ERROR_BAD_DATA = SEC_ERROR_BASE + 2, +SEC_ERROR_OUTPUT_LEN = SEC_ERROR_BASE + 3, +SEC_ERROR_INPUT_LEN = SEC_ERROR_BASE + 4, +SEC_ERROR_INVALID_ARGS = SEC_ERROR_BASE + 5, +SEC_ERROR_INVALID_ALGORITHM = SEC_ERROR_BASE + 6, +SEC_ERROR_INVALID_AVA = SEC_ERROR_BASE + 7, +SEC_ERROR_INVALID_TIME = SEC_ERROR_BASE + 8, +SEC_ERROR_BAD_DER = SEC_ERROR_BASE + 9, +SEC_ERROR_BAD_SIGNATURE = SEC_ERROR_BASE + 10, +SEC_ERROR_EXPIRED_CERTIFICATE = SEC_ERROR_BASE + 11, +SEC_ERROR_REVOKED_CERTIFICATE = SEC_ERROR_BASE + 12, +SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13, +SEC_ERROR_BAD_KEY = SEC_ERROR_BASE + 14, +SEC_ERROR_BAD_PASSWORD = SEC_ERROR_BASE + 15, +SEC_ERROR_RETRY_PASSWORD = SEC_ERROR_BASE + 16, +SEC_ERROR_NO_NODELOCK = SEC_ERROR_BASE + 17, +SEC_ERROR_BAD_DATABASE = SEC_ERROR_BASE + 18, +SEC_ERROR_NO_MEMORY = SEC_ERROR_BASE + 19, +SEC_ERROR_UNTRUSTED_ISSUER = SEC_ERROR_BASE + 20, +SEC_ERROR_UNTRUSTED_CERT = SEC_ERROR_BASE + 21, +SEC_ERROR_DUPLICATE_CERT = (SEC_ERROR_BASE + 22), +SEC_ERROR_DUPLICATE_CERT_NAME = (SEC_ERROR_BASE + 23), +SEC_ERROR_ADDING_CERT = (SEC_ERROR_BASE + 24), +SEC_ERROR_FILING_KEY = (SEC_ERROR_BASE + 25), +SEC_ERROR_NO_KEY = (SEC_ERROR_BASE + 26), +SEC_ERROR_CERT_VALID = (SEC_ERROR_BASE + 27), +SEC_ERROR_CERT_NOT_VALID = (SEC_ERROR_BASE + 28), +SEC_ERROR_CERT_NO_RESPONSE = (SEC_ERROR_BASE + 29), +SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = (SEC_ERROR_BASE + 30), +SEC_ERROR_CRL_EXPIRED = (SEC_ERROR_BASE + 31), +SEC_ERROR_CRL_BAD_SIGNATURE = (SEC_ERROR_BASE + 32), +SEC_ERROR_CRL_INVALID = (SEC_ERROR_BASE + 33), +SEC_ERROR_EXTENSION_VALUE_INVALID = (SEC_ERROR_BASE + 34), +SEC_ERROR_EXTENSION_NOT_FOUND = (SEC_ERROR_BASE + 35), +SEC_ERROR_CA_CERT_INVALID = (SEC_ERROR_BASE + 36), +SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID = (SEC_ERROR_BASE + 37), +SEC_ERROR_CERT_USAGES_INVALID = (SEC_ERROR_BASE + 38), +SEC_INTERNAL_ONLY = (SEC_ERROR_BASE + 39), +SEC_ERROR_INVALID_KEY = (SEC_ERROR_BASE + 40), +SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION = (SEC_ERROR_BASE + 41), +SEC_ERROR_OLD_CRL = (SEC_ERROR_BASE + 42), +SEC_ERROR_NO_EMAIL_CERT = (SEC_ERROR_BASE + 43), +SEC_ERROR_NO_RECIPIENT_CERTS_QUERY = (SEC_ERROR_BASE + 44), +SEC_ERROR_NOT_A_RECIPIENT = (SEC_ERROR_BASE + 45), +SEC_ERROR_PKCS7_KEYALG_MISMATCH = (SEC_ERROR_BASE + 46), +SEC_ERROR_PKCS7_BAD_SIGNATURE = (SEC_ERROR_BASE + 47), +SEC_ERROR_UNSUPPORTED_KEYALG = (SEC_ERROR_BASE + 48), +SEC_ERROR_DECRYPTION_DISALLOWED = (SEC_ERROR_BASE + 49), +/* Fortezza Alerts */ +XP_SEC_FORTEZZA_BAD_CARD = (SEC_ERROR_BASE + 50), +XP_SEC_FORTEZZA_NO_CARD = (SEC_ERROR_BASE + 51), +XP_SEC_FORTEZZA_NONE_SELECTED = (SEC_ERROR_BASE + 52), +XP_SEC_FORTEZZA_MORE_INFO = (SEC_ERROR_BASE + 53), +XP_SEC_FORTEZZA_PERSON_NOT_FOUND = (SEC_ERROR_BASE + 54), +XP_SEC_FORTEZZA_NO_MORE_INFO = (SEC_ERROR_BASE + 55), +XP_SEC_FORTEZZA_BAD_PIN = (SEC_ERROR_BASE + 56), +XP_SEC_FORTEZZA_PERSON_ERROR = (SEC_ERROR_BASE + 57), +SEC_ERROR_NO_KRL = (SEC_ERROR_BASE + 58), +SEC_ERROR_KRL_EXPIRED = (SEC_ERROR_BASE + 59), +SEC_ERROR_KRL_BAD_SIGNATURE = (SEC_ERROR_BASE + 60), +SEC_ERROR_REVOKED_KEY = (SEC_ERROR_BASE + 61), +SEC_ERROR_KRL_INVALID = (SEC_ERROR_BASE + 62), +SEC_ERROR_NEED_RANDOM = (SEC_ERROR_BASE + 63), +SEC_ERROR_NO_MODULE = (SEC_ERROR_BASE + 64), +SEC_ERROR_NO_TOKEN = (SEC_ERROR_BASE + 65), +SEC_ERROR_READ_ONLY = (SEC_ERROR_BASE + 66), +SEC_ERROR_NO_SLOT_SELECTED = (SEC_ERROR_BASE + 67), +SEC_ERROR_CERT_NICKNAME_COLLISION = (SEC_ERROR_BASE + 68), +SEC_ERROR_KEY_NICKNAME_COLLISION = (SEC_ERROR_BASE + 69), +SEC_ERROR_SAFE_NOT_CREATED = (SEC_ERROR_BASE + 70), +SEC_ERROR_BAGGAGE_NOT_CREATED = (SEC_ERROR_BASE + 71), +XP_JAVA_REMOVE_PRINCIPAL_ERROR = (SEC_ERROR_BASE + 72), +XP_JAVA_DELETE_PRIVILEGE_ERROR = (SEC_ERROR_BASE + 73), +XP_JAVA_CERT_NOT_EXISTS_ERROR = (SEC_ERROR_BASE + 74), +SEC_ERROR_BAD_EXPORT_ALGORITHM = (SEC_ERROR_BASE + 75), +SEC_ERROR_EXPORTING_CERTIFICATES = (SEC_ERROR_BASE + 76), +SEC_ERROR_IMPORTING_CERTIFICATES = (SEC_ERROR_BASE + 77), +SEC_ERROR_PKCS12_DECODING_PFX = (SEC_ERROR_BASE + 78), +SEC_ERROR_PKCS12_INVALID_MAC = (SEC_ERROR_BASE + 79), +SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM = (SEC_ERROR_BASE + 80), +SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE = (SEC_ERROR_BASE + 81), +SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE = (SEC_ERROR_BASE + 82), +SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM = (SEC_ERROR_BASE + 83), +SEC_ERROR_PKCS12_UNSUPPORTED_VERSION = (SEC_ERROR_BASE + 84), +SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT = (SEC_ERROR_BASE + 85), +SEC_ERROR_PKCS12_CERT_COLLISION = (SEC_ERROR_BASE + 86), +SEC_ERROR_USER_CANCELLED = (SEC_ERROR_BASE + 87), +SEC_ERROR_PKCS12_DUPLICATE_DATA = (SEC_ERROR_BASE + 88), +SEC_ERROR_MESSAGE_SEND_ABORTED = (SEC_ERROR_BASE + 89), +SEC_ERROR_INADEQUATE_KEY_USAGE = (SEC_ERROR_BASE + 90), +SEC_ERROR_INADEQUATE_CERT_TYPE = (SEC_ERROR_BASE + 91), +SEC_ERROR_CERT_ADDR_MISMATCH = (SEC_ERROR_BASE + 92), +SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY = (SEC_ERROR_BASE + 93), +SEC_ERROR_PKCS12_IMPORTING_CERT_CHAIN = (SEC_ERROR_BASE + 94), +SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME = (SEC_ERROR_BASE + 95), +SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY = (SEC_ERROR_BASE + 96), +SEC_ERROR_PKCS12_UNABLE_TO_WRITE = (SEC_ERROR_BASE + 97), +SEC_ERROR_PKCS12_UNABLE_TO_READ = (SEC_ERROR_BASE + 98), +SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED = (SEC_ERROR_BASE + 99), +SEC_ERROR_KEYGEN_FAIL = (SEC_ERROR_BASE + 100), +SEC_ERROR_INVALID_PASSWORD = (SEC_ERROR_BASE + 101), +SEC_ERROR_RETRY_OLD_PASSWORD = (SEC_ERROR_BASE + 102), +SEC_ERROR_BAD_NICKNAME = (SEC_ERROR_BASE + 103), +SEC_ERROR_NOT_FORTEZZA_ISSUER = (SEC_ERROR_BASE + 104), +/* UNUSED (SEC_ERROR_BASE + 105) */ +SEC_ERROR_JS_INVALID_MODULE_NAME = (SEC_ERROR_BASE + 106), +SEC_ERROR_JS_INVALID_DLL = (SEC_ERROR_BASE + 107), +SEC_ERROR_JS_ADD_MOD_FAILURE = (SEC_ERROR_BASE + 108), +SEC_ERROR_JS_DEL_MOD_FAILURE = (SEC_ERROR_BASE + 109), +SEC_ERROR_OLD_KRL = (SEC_ERROR_BASE + 110), +SEC_ERROR_CKL_CONFLICT = (SEC_ERROR_BASE + 111), +SEC_ERROR_CERT_NOT_IN_NAME_SPACE = (SEC_ERROR_BASE + 112), +SEC_ERROR_KRL_NOT_YET_VALID = (SEC_ERROR_BASE + 113), +SEC_ERROR_CRL_NOT_YET_VALID = (SEC_ERROR_BASE + 114), +SEC_ERROR_UNKNOWN_CERT = (SEC_ERROR_BASE + 115), +SEC_ERROR_UNKNOWN_SIGNER = (SEC_ERROR_BASE + 116), +SEC_ERROR_CERT_BAD_ACCESS_LOCATION = (SEC_ERROR_BASE + 117), +SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE = (SEC_ERROR_BASE + 118), +SEC_ERROR_OCSP_BAD_HTTP_RESPONSE = (SEC_ERROR_BASE + 119), +SEC_ERROR_OCSP_MALFORMED_REQUEST = (SEC_ERROR_BASE + 120), +SEC_ERROR_OCSP_SERVER_ERROR = (SEC_ERROR_BASE + 121), +SEC_ERROR_OCSP_TRY_SERVER_LATER = (SEC_ERROR_BASE + 122), +SEC_ERROR_OCSP_REQUEST_NEEDS_SIG = (SEC_ERROR_BASE + 123), +SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST = (SEC_ERROR_BASE + 124), +SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS = (SEC_ERROR_BASE + 125), +SEC_ERROR_OCSP_UNKNOWN_CERT = (SEC_ERROR_BASE + 126), +SEC_ERROR_OCSP_NOT_ENABLED = (SEC_ERROR_BASE + 127), +SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER = (SEC_ERROR_BASE + 128), +SEC_ERROR_OCSP_MALFORMED_RESPONSE = (SEC_ERROR_BASE + 129), +SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE = (SEC_ERROR_BASE + 130), +SEC_ERROR_OCSP_FUTURE_RESPONSE = (SEC_ERROR_BASE + 131), +SEC_ERROR_OCSP_OLD_RESPONSE = (SEC_ERROR_BASE + 132), +/* smime stuff */ +SEC_ERROR_DIGEST_NOT_FOUND = (SEC_ERROR_BASE + 133), +SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE = (SEC_ERROR_BASE + 134) + +} SECErrorCodes; +#endif /* NO_SECURITY_ERROR_ENUM */ + +#endif /* __SEC_ERR_H_ */ |