security/nss/lib/softoken/sftkdb.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 /*
michael@0 5 * The following code handles the storage of PKCS 11 modules used by the
michael@0 6 * NSS. For the rest of NSS, only one kind of database handle exists:
michael@0 7 *
michael@0 8 * SFTKDBHandle
michael@0 9 *
michael@0 10 * There is one SFTKDBHandle for the each key database and one for each cert
michael@0 11 * database. These databases are opened as associated pairs, one pair per
michael@0 12 * slot. SFTKDBHandles are reference counted objects.
michael@0 13 *
michael@0 14 * Each SFTKDBHandle points to a low level database handle (SDB). This handle
michael@0 15 * represents the underlying physical database. These objects are not
michael@0 16 * reference counted, an are 'owned' by their respective SFTKDBHandles.
michael@0 17 *
michael@0 18 *
michael@0 19 */
michael@0 20 #include "sftkdb.h"
michael@0 21 #include "sftkdbti.h"
michael@0 22 #include "pkcs11t.h"
michael@0 23 #include "pkcs11i.h"
michael@0 24 #include "sdb.h"
michael@0 25 #include "prprf.h"
michael@0 26 #include "pratom.h"
michael@0 27 #include "lgglue.h"
michael@0 28 #include "utilpars.h"
michael@0 29 #include "secerr.h"
michael@0 30 #include "softoken.h"
michael@0 31
michael@0 32 /*
michael@0 33 * We want all databases to have the same binary representation independent of
michael@0 34 * endianness or length of the host architecture. In general PKCS #11 attributes
michael@0 35 * are endian/length independent except those attributes that pass CK_ULONG.
michael@0 36 *
michael@0 37 * The following functions fixes up the CK_ULONG type attributes so that the data
michael@0 38 * base sees a machine independent view. CK_ULONGs are stored as 4 byte network
michael@0 39 * byte order values (big endian).
michael@0 40 */
michael@0 41 #define BBP 8
michael@0 42
michael@0 43 static PRBool
michael@0 44 sftkdb_isULONGAttribute(CK_ATTRIBUTE_TYPE type)
michael@0 45 {
michael@0 46 switch(type) {
michael@0 47 case CKA_CERTIFICATE_CATEGORY:
michael@0 48 case CKA_CERTIFICATE_TYPE:
michael@0 49 case CKA_CLASS:
michael@0 50 case CKA_JAVA_MIDP_SECURITY_DOMAIN:
michael@0 51 case CKA_KEY_GEN_MECHANISM:
michael@0 52 case CKA_KEY_TYPE:
michael@0 53 case CKA_MECHANISM_TYPE:
michael@0 54 case CKA_MODULUS_BITS:
michael@0 55 case CKA_PRIME_BITS:
michael@0 56 case CKA_SUBPRIME_BITS:
michael@0 57 case CKA_VALUE_BITS:
michael@0 58 case CKA_VALUE_LEN:
michael@0 59
michael@0 60 case CKA_TRUST_DIGITAL_SIGNATURE:
michael@0 61 case CKA_TRUST_NON_REPUDIATION:
michael@0 62 case CKA_TRUST_KEY_ENCIPHERMENT:
michael@0 63 case CKA_TRUST_DATA_ENCIPHERMENT:
michael@0 64 case CKA_TRUST_KEY_AGREEMENT:
michael@0 65 case CKA_TRUST_KEY_CERT_SIGN:
michael@0 66 case CKA_TRUST_CRL_SIGN:
michael@0 67
michael@0 68 case CKA_TRUST_SERVER_AUTH:
michael@0 69 case CKA_TRUST_CLIENT_AUTH:
michael@0 70 case CKA_TRUST_CODE_SIGNING:
michael@0 71 case CKA_TRUST_EMAIL_PROTECTION:
michael@0 72 case CKA_TRUST_IPSEC_END_SYSTEM:
michael@0 73 case CKA_TRUST_IPSEC_TUNNEL:
michael@0 74 case CKA_TRUST_IPSEC_USER:
michael@0 75 case CKA_TRUST_TIME_STAMPING:
michael@0 76 case CKA_TRUST_STEP_UP_APPROVED:
michael@0 77 return PR_TRUE;
michael@0 78 default:
michael@0 79 break;
michael@0 80 }
michael@0 81 return PR_FALSE;
michael@0 82
michael@0 83 }
michael@0 84
michael@0 85 /* are the attributes private? */
michael@0 86 static PRBool
michael@0 87 sftkdb_isPrivateAttribute(CK_ATTRIBUTE_TYPE type)
michael@0 88 {
michael@0 89 switch(type) {
michael@0 90 case CKA_VALUE:
michael@0 91 case CKA_PRIVATE_EXPONENT:
michael@0 92 case CKA_PRIME_1:
michael@0 93 case CKA_PRIME_2:
michael@0 94 case CKA_EXPONENT_1:
michael@0 95 case CKA_EXPONENT_2:
michael@0 96 case CKA_COEFFICIENT:
michael@0 97 return PR_TRUE;
michael@0 98 default:
michael@0 99 break;
michael@0 100 }
michael@0 101 return PR_FALSE;
michael@0 102 }
michael@0 103
michael@0 104 /* These attributes must be authenticated with an hmac. */
michael@0 105 static PRBool
michael@0 106 sftkdb_isAuthenticatedAttribute(CK_ATTRIBUTE_TYPE type)
michael@0 107 {
michael@0 108 switch(type) {
michael@0 109 case CKA_MODULUS:
michael@0 110 case CKA_PUBLIC_EXPONENT:
michael@0 111 case CKA_CERT_SHA1_HASH:
michael@0 112 case CKA_CERT_MD5_HASH:
michael@0 113 case CKA_TRUST_SERVER_AUTH:
michael@0 114 case CKA_TRUST_CLIENT_AUTH:
michael@0 115 case CKA_TRUST_EMAIL_PROTECTION:
michael@0 116 case CKA_TRUST_CODE_SIGNING:
michael@0 117 case CKA_TRUST_STEP_UP_APPROVED:
michael@0 118 case CKA_NSS_OVERRIDE_EXTENSIONS:
michael@0 119 return PR_TRUE;
michael@0 120 default:
michael@0 121 break;
michael@0 122 }
michael@0 123 return PR_FALSE;
michael@0 124 }
michael@0 125
michael@0 126 /*
michael@0 127 * convert a native ULONG to a database ulong. Database ulong's
michael@0 128 * are all 4 byte big endian values.
michael@0 129 */
michael@0 130 void
michael@0 131 sftk_ULong2SDBULong(unsigned char *data, CK_ULONG value)
michael@0 132 {
michael@0 133 int i;
michael@0 134
michael@0 135 for (i=0; i < SDB_ULONG_SIZE; i++) {
michael@0 136 data[i] = (value >> (SDB_ULONG_SIZE-1-i)*BBP) & 0xff;
michael@0 137 }
michael@0 138 }
michael@0 139
michael@0 140 /*
michael@0 141 * convert a database ulong back to a native ULONG. (reverse of the above
michael@0 142 * function.
michael@0 143 */
michael@0 144 static CK_ULONG
michael@0 145 sftk_SDBULong2ULong(unsigned char *data)
michael@0 146 {
michael@0 147 int i;
michael@0 148 CK_ULONG value = 0;
michael@0 149
michael@0 150 for (i=0; i < SDB_ULONG_SIZE; i++) {
michael@0 151 value |= (((CK_ULONG)data[i]) << (SDB_ULONG_SIZE-1-i)*BBP);
michael@0 152 }
michael@0 153 return value;
michael@0 154 }
michael@0 155
michael@0 156 /*
michael@0 157 * fix up the input templates. Our fixed up ints are stored in data and must
michael@0 158 * be freed by the caller. The new template must also be freed. If there are no
michael@0 159 * CK_ULONG attributes, the orignal template is passed in as is.
michael@0 160 */
michael@0 161 static CK_ATTRIBUTE *
michael@0 162 sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count,
michael@0 163 unsigned char **dataOut)
michael@0 164 {
michael@0 165 int i;
michael@0 166 int ulongCount = 0;
michael@0 167 unsigned char *data;
michael@0 168 CK_ATTRIBUTE *ntemplate;
michael@0 169
michael@0 170 *dataOut = NULL;
michael@0 171
michael@0 172 /* first count the number of CK_ULONG attributes */
michael@0 173 for (i=0; i < count; i++) {
michael@0 174 /* Don't 'fixup' NULL values */
michael@0 175 if (!template[i].pValue) {
michael@0 176 continue;
michael@0 177 }
michael@0 178 if (template[i].ulValueLen == sizeof (CK_ULONG)) {
michael@0 179 if ( sftkdb_isULONGAttribute(template[i].type)) {
michael@0 180 ulongCount++;
michael@0 181 }
michael@0 182 }
michael@0 183 }
michael@0 184 /* no attributes to fixup, just call on through */
michael@0 185 if (ulongCount == 0) {
michael@0 186 return (CK_ATTRIBUTE *)template;
michael@0 187 }
michael@0 188
michael@0 189 /* allocate space for new ULONGS */
michael@0 190 data = (unsigned char *)PORT_Alloc(SDB_ULONG_SIZE*ulongCount);
michael@0 191 if (!data) {
michael@0 192 return NULL;
michael@0 193 }
michael@0 194
michael@0 195 /* allocate new template */
michael@0 196 ntemplate = PORT_NewArray(CK_ATTRIBUTE,count);
michael@0 197 if (!ntemplate) {
michael@0 198 PORT_Free(data);
michael@0 199 return NULL;
michael@0 200 }
michael@0 201 *dataOut = data;
michael@0 202 /* copy the old template, fixup the actual ulongs */
michael@0 203 for (i=0; i < count; i++) {
michael@0 204 ntemplate[i] = template[i];
michael@0 205 /* Don't 'fixup' NULL values */
michael@0 206 if (!template[i].pValue) {
michael@0 207 continue;
michael@0 208 }
michael@0 209 if (template[i].ulValueLen == sizeof (CK_ULONG)) {
michael@0 210 if ( sftkdb_isULONGAttribute(template[i].type) ) {
michael@0 211 CK_ULONG value = *(CK_ULONG *) template[i].pValue;
michael@0 212 sftk_ULong2SDBULong(data, value);
michael@0 213 ntemplate[i].pValue = data;
michael@0 214 ntemplate[i].ulValueLen = SDB_ULONG_SIZE;
michael@0 215 data += SDB_ULONG_SIZE;
michael@0 216 }
michael@0 217 }
michael@0 218 }
michael@0 219 return ntemplate;
michael@0 220 }
michael@0 221
michael@0 222
michael@0 223 static const char SFTKDB_META_SIG_TEMPLATE[] = "sig_%s_%08x_%08x";
michael@0 224
michael@0 225 /*
michael@0 226 * return a string describing the database type (key or cert)
michael@0 227 */
michael@0 228 const char *
michael@0 229 sftkdb_TypeString(SFTKDBHandle *handle)
michael@0 230 {
michael@0 231 return (handle->type == SFTK_KEYDB_TYPE) ? "key" : "cert";
michael@0 232 }
michael@0 233
michael@0 234 /*
michael@0 235 * Some attributes are signed with an Hmac and a pbe key generated from
michael@0 236 * the password. This signature is stored indexed by object handle and
michael@0 237 * attribute type in the meta data table in the key database.
michael@0 238 *
michael@0 239 * Signature entries are indexed by the string
michael@0 240 * sig_[cert/key]_{ObjectID}_{Attribute}
michael@0 241 *
michael@0 242 * This function fetches that pkcs5 signature. Caller supplies a SECItem
michael@0 243 * pre-allocated to the appropriate size if the SECItem is too small the
michael@0 244 * function will fail with CKR_BUFFER_TOO_SMALL.
michael@0 245 */
michael@0 246 static CK_RV
michael@0 247 sftkdb_getAttributeSignature(SFTKDBHandle *handle, SFTKDBHandle *keyHandle,
michael@0 248 CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type,
michael@0 249 SECItem *signText)
michael@0 250 {
michael@0 251 SDB *db;
michael@0 252 char id[30];
michael@0 253 CK_RV crv;
michael@0 254
michael@0 255 db = SFTK_GET_SDB(keyHandle);
michael@0 256
michael@0 257 sprintf(id, SFTKDB_META_SIG_TEMPLATE,
michael@0 258 sftkdb_TypeString(handle),
michael@0 259 (unsigned int)objectID, (unsigned int)type);
michael@0 260
michael@0 261 crv = (*db->sdb_GetMetaData)(db, id, signText, NULL);
michael@0 262 return crv;
michael@0 263 }
michael@0 264
michael@0 265 /*
michael@0 266 * Some attributes are signed with an Hmac and a pbe key generated from
michael@0 267 * the password. This signature is stored indexed by object handle and
michael@0 268 * attribute type in the meta data table in the key database.
michael@0 269 *
michael@0 270 * Signature entries are indexed by the string
michael@0 271 * sig_[cert/key]_{ObjectID}_{Attribute}
michael@0 272 *
michael@0 273 * This function stores that pkcs5 signature.
michael@0 274 */
michael@0 275 CK_RV
michael@0 276 sftkdb_PutAttributeSignature(SFTKDBHandle *handle, SDB *keyTarget,
michael@0 277 CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type,
michael@0 278 SECItem *signText)
michael@0 279 {
michael@0 280 char id[30];
michael@0 281 CK_RV crv;
michael@0 282
michael@0 283 sprintf(id, SFTKDB_META_SIG_TEMPLATE,
michael@0 284 sftkdb_TypeString(handle),
michael@0 285 (unsigned int)objectID, (unsigned int)type);
michael@0 286
michael@0 287 crv = (*keyTarget->sdb_PutMetaData)(keyTarget, id, signText, NULL);
michael@0 288 return crv;
michael@0 289 }
michael@0 290
michael@0 291 /*
michael@0 292 * fix up returned data. NOTE: sftkdb_fixupTemplateIn has already allocated
michael@0 293 * separate data sections for the database ULONG values.
michael@0 294 */
michael@0 295 static CK_RV
michael@0 296 sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_OBJECT_HANDLE objectID,
michael@0 297 CK_ATTRIBUTE *ntemplate, int count, SFTKDBHandle *handle)
michael@0 298 {
michael@0 299 int i;
michael@0 300 CK_RV crv = CKR_OK;
michael@0 301 SFTKDBHandle *keyHandle;
michael@0 302 PRBool checkSig = PR_TRUE;
michael@0 303 PRBool checkEnc = PR_TRUE;
michael@0 304
michael@0 305 PORT_Assert(handle);
michael@0 306
michael@0 307 /* find the key handle */
michael@0 308 keyHandle = handle;
michael@0 309 if (handle->type != SFTK_KEYDB_TYPE) {
michael@0 310 checkEnc = PR_FALSE;
michael@0 311 keyHandle = handle->peerDB;
michael@0 312 }
michael@0 313
michael@0 314 if ((keyHandle == NULL) ||
michael@0 315 ((SFTK_GET_SDB(keyHandle)->sdb_flags & SDB_HAS_META) == 0) ||
michael@0 316 (keyHandle->passwordKey.data == NULL)) {
michael@0 317 checkSig = PR_FALSE;
michael@0 318 }
michael@0 319
michael@0 320 for (i=0; i < count; i++) {
michael@0 321 CK_ULONG length = template[i].ulValueLen;
michael@0 322 template[i].ulValueLen = ntemplate[i].ulValueLen;
michael@0 323 /* fixup ulongs */
michael@0 324 if (ntemplate[i].ulValueLen == SDB_ULONG_SIZE) {
michael@0 325 if (sftkdb_isULONGAttribute(template[i].type)) {
michael@0 326 if (template[i].pValue) {
michael@0 327 CK_ULONG value;
michael@0 328 unsigned char *data;
michael@0 329
michael@0 330 data = (unsigned char *)ntemplate[i].pValue;
michael@0 331 value = sftk_SDBULong2ULong(ntemplate[i].pValue);
michael@0 332 if (length < sizeof(CK_ULONG)) {
michael@0 333 template[i].ulValueLen = -1;
michael@0 334 crv = CKR_BUFFER_TOO_SMALL;
michael@0 335 continue;
michael@0 336 }
michael@0 337 PORT_Memcpy(template[i].pValue,&value,sizeof(CK_ULONG));
michael@0 338 }
michael@0 339 template[i].ulValueLen = sizeof(CK_ULONG);
michael@0 340 }
michael@0 341 }
michael@0 342
michael@0 343 /* if no data was retrieved, no need to process encrypted or signed
michael@0 344 * attributes */
michael@0 345 if ((template[i].pValue == NULL) || (template[i].ulValueLen == -1)) {
michael@0 346 continue;
michael@0 347 }
michael@0 348
michael@0 349 /* fixup private attributes */
michael@0 350 if (checkEnc && sftkdb_isPrivateAttribute(ntemplate[i].type)) {
michael@0 351 /* we have a private attribute */
michael@0 352 /* This code depends on the fact that the cipherText is bigger
michael@0 353 * than the plain text */
michael@0 354 SECItem cipherText;
michael@0 355 SECItem *plainText;
michael@0 356 SECStatus rv;
michael@0 357
michael@0 358 cipherText.data = ntemplate[i].pValue;
michael@0 359 cipherText.len = ntemplate[i].ulValueLen;
michael@0 360 PZ_Lock(handle->passwordLock);
michael@0 361 if (handle->passwordKey.data == NULL) {
michael@0 362 PZ_Unlock(handle->passwordLock);
michael@0 363 template[i].ulValueLen = -1;
michael@0 364 crv = CKR_USER_NOT_LOGGED_IN;
michael@0 365 continue;
michael@0 366 }
michael@0 367 rv = sftkdb_DecryptAttribute(&handle->passwordKey,
michael@0 368 &cipherText, &plainText);
michael@0 369 PZ_Unlock(handle->passwordLock);
michael@0 370 if (rv != SECSuccess) {
michael@0 371 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
michael@0 372 template[i].ulValueLen = -1;
michael@0 373 crv = CKR_GENERAL_ERROR;
michael@0 374 continue;
michael@0 375 }
michael@0 376 PORT_Assert(template[i].ulValueLen >= plainText->len);
michael@0 377 if (template[i].ulValueLen < plainText->len) {
michael@0 378 SECITEM_FreeItem(plainText,PR_TRUE);
michael@0 379 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
michael@0 380 template[i].ulValueLen = -1;
michael@0 381 crv = CKR_GENERAL_ERROR;
michael@0 382 continue;
michael@0 383 }
michael@0 384
michael@0 385 /* copy the plain text back into the template */
michael@0 386 PORT_Memcpy(template[i].pValue, plainText->data, plainText->len);
michael@0 387 template[i].ulValueLen = plainText->len;
michael@0 388 SECITEM_FreeItem(plainText,PR_TRUE);
michael@0 389 }
michael@0 390 /* make sure signed attributes are valid */
michael@0 391 if (checkSig && sftkdb_isAuthenticatedAttribute(ntemplate[i].type)) {
michael@0 392 SECStatus rv;
michael@0 393 SECItem signText;
michael@0 394 SECItem plainText;
michael@0 395 unsigned char signData[SDB_MAX_META_DATA_LEN];
michael@0 396
michael@0 397 signText.data = signData;
michael@0 398 signText.len = sizeof(signData);
michael@0 399
michael@0 400 rv = sftkdb_getAttributeSignature(handle, keyHandle,
michael@0 401 objectID, ntemplate[i].type, &signText);
michael@0 402 if (rv != SECSuccess) {
michael@0 403 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
michael@0 404 template[i].ulValueLen = -1;
michael@0 405 crv = CKR_DATA_INVALID; /* better error code? */
michael@0 406 continue;
michael@0 407 }
michael@0 408
michael@0 409 plainText.data = ntemplate[i].pValue;
michael@0 410 plainText.len = ntemplate[i].ulValueLen;
michael@0 411
michael@0 412 /*
michael@0 413 * we do a second check holding the lock just in case the user
michael@0 414 * loggout while we were trying to get the signature.
michael@0 415 */
michael@0 416 PZ_Lock(keyHandle->passwordLock);
michael@0 417 if (keyHandle->passwordKey.data == NULL) {
michael@0 418 /* if we are no longer logged in, no use checking the other
michael@0 419 * Signatures either. */
michael@0 420 checkSig = PR_FALSE;
michael@0 421 PZ_Unlock(keyHandle->passwordLock);
michael@0 422 continue;
michael@0 423 }
michael@0 424
michael@0 425 rv = sftkdb_VerifyAttribute(&keyHandle->passwordKey,
michael@0 426 objectID, ntemplate[i].type,
michael@0 427 &plainText, &signText);
michael@0 428 PZ_Unlock(keyHandle->passwordLock);
michael@0 429 if (rv != SECSuccess) {
michael@0 430 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
michael@0 431 template[i].ulValueLen = -1;
michael@0 432 crv = CKR_SIGNATURE_INVALID; /* better error code? */
michael@0 433 }
michael@0 434 /* This Attribute is fine */
michael@0 435 }
michael@0 436 }
michael@0 437 return crv;
michael@0 438 }
michael@0 439
michael@0 440 /*
michael@0 441 * Some attributes are signed with an HMAC and a pbe key generated from
michael@0 442 * the password. This signature is stored indexed by object handle and
michael@0 443 *
michael@0 444 * Those attributes are:
michael@0 445 * 1) Trust object hashes and trust values.
michael@0 446 * 2) public key values.
michael@0 447 *
michael@0 448 * Certs themselves are considered properly authenticated by virtue of their
michael@0 449 * signature, or their matching hash with the trust object.
michael@0 450 *
michael@0 451 * These signature is only checked for objects coming from shared databases.
michael@0 452 * Older dbm style databases have such no signature checks. HMACs are also
michael@0 453 * only checked when the token is logged in, as it requires a pbe generated
michael@0 454 * from the password.
michael@0 455 *
michael@0 456 * Tokens which have no key database (and therefore no master password) do not
michael@0 457 * have any stored signature values. Signature values are stored in the key
michael@0 458 * database, since the signature data is tightly coupled to the key database
michael@0 459 * password.
michael@0 460 *
michael@0 461 * This function takes a template of attributes that were either created or
michael@0 462 * modified. These attributes are checked to see if the need to be signed.
michael@0 463 * If they do, then this function signs the attributes and writes them
michael@0 464 * to the meta data store.
michael@0 465 *
michael@0 466 * This function can fail if there are attributes that must be signed, but
michael@0 467 * the token is not logged in.
michael@0 468 *
michael@0 469 * The caller is expected to abort any transaction he was in in the
michael@0 470 * event of a failure of this function.
michael@0 471 */
michael@0 472 static CK_RV
michael@0 473 sftk_signTemplate(PLArenaPool *arena, SFTKDBHandle *handle,
michael@0 474 PRBool mayBeUpdateDB,
michael@0 475 CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template,
michael@0 476 CK_ULONG count)
michael@0 477 {
michael@0 478 int i;
michael@0 479 CK_RV crv;
michael@0 480 SFTKDBHandle *keyHandle = handle;
michael@0 481 SDB *keyTarget = NULL;
michael@0 482 PRBool usingPeerDB = PR_FALSE;
michael@0 483 PRBool inPeerDBTransaction = PR_FALSE;
michael@0 484
michael@0 485 PORT_Assert(handle);
michael@0 486
michael@0 487 if (handle->type != SFTK_KEYDB_TYPE) {
michael@0 488 keyHandle = handle->peerDB;
michael@0 489 usingPeerDB = PR_TRUE;
michael@0 490 }
michael@0 491
michael@0 492 /* no key DB defined? then no need to sign anything */
michael@0 493 if (keyHandle == NULL) {
michael@0 494 crv = CKR_OK;
michael@0 495 goto loser;
michael@0 496 }
michael@0 497
michael@0 498 /* When we are in a middle of an update, we have an update database set,
michael@0 499 * but we want to write to the real database. The bool mayBeUpdateDB is
michael@0 500 * set to TRUE if it's possible that we want to write an update database
michael@0 501 * rather than a primary */
michael@0 502 keyTarget = (mayBeUpdateDB && keyHandle->update) ?
michael@0 503 keyHandle->update : keyHandle->db;
michael@0 504
michael@0 505 /* skip the the database does not support meta data */
michael@0 506 if ((keyTarget->sdb_flags & SDB_HAS_META) == 0) {
michael@0 507 crv = CKR_OK;
michael@0 508 goto loser;
michael@0 509 }
michael@0 510
michael@0 511 /* If we had to switch databases, we need to initialize a transaction. */
michael@0 512 if (usingPeerDB) {
michael@0 513 crv = (*keyTarget->sdb_Begin)(keyTarget);
michael@0 514 if (crv != CKR_OK) {
michael@0 515 goto loser;
michael@0 516 }
michael@0 517 inPeerDBTransaction = PR_TRUE;
michael@0 518 }
michael@0 519
michael@0 520 for (i=0; i < count; i ++) {
michael@0 521 if (sftkdb_isAuthenticatedAttribute(template[i].type)) {
michael@0 522 SECStatus rv;
michael@0 523 SECItem *signText;
michael@0 524 SECItem plainText;
michael@0 525
michael@0 526 plainText.data = template[i].pValue;
michael@0 527 plainText.len = template[i].ulValueLen;
michael@0 528 PZ_Lock(keyHandle->passwordLock);
michael@0 529 if (keyHandle->passwordKey.data == NULL) {
michael@0 530 PZ_Unlock(keyHandle->passwordLock);
michael@0 531 crv = CKR_USER_NOT_LOGGED_IN;
michael@0 532 goto loser;
michael@0 533 }
michael@0 534 rv = sftkdb_SignAttribute(arena, &keyHandle->passwordKey,
michael@0 535 objectID, template[i].type,
michael@0 536 &plainText, &signText);
michael@0 537 PZ_Unlock(keyHandle->passwordLock);
michael@0 538 if (rv != SECSuccess) {
michael@0 539 crv = CKR_GENERAL_ERROR; /* better error code here? */
michael@0 540 goto loser;
michael@0 541 }
michael@0 542 rv = sftkdb_PutAttributeSignature(handle, keyTarget,
michael@0 543 objectID, template[i].type, signText);
michael@0 544 if (rv != SECSuccess) {
michael@0 545 crv = CKR_GENERAL_ERROR; /* better error code here? */
michael@0 546 goto loser;
michael@0 547 }
michael@0 548 }
michael@0 549 }
michael@0 550 crv = CKR_OK;
michael@0 551
michael@0 552 /* If necessary, commit the transaction */
michael@0 553 if (inPeerDBTransaction) {
michael@0 554 crv = (*keyTarget->sdb_Commit)(keyTarget);
michael@0 555 if (crv != CKR_OK) {
michael@0 556 goto loser;
michael@0 557 }
michael@0 558 inPeerDBTransaction = PR_FALSE;
michael@0 559 }
michael@0 560
michael@0 561 loser:
michael@0 562 if (inPeerDBTransaction) {
michael@0 563 /* The transaction must have failed. Abort. */
michael@0 564 (*keyTarget->sdb_Abort)(keyTarget);
michael@0 565 PORT_Assert(crv != CKR_OK);
michael@0 566 if (crv == CKR_OK) crv = CKR_GENERAL_ERROR;
michael@0 567 }
michael@0 568 return crv;
michael@0 569 }
michael@0 570
michael@0 571 static CK_RV
michael@0 572 sftkdb_CreateObject(PLArenaPool *arena, SFTKDBHandle *handle,
michael@0 573 SDB *db, CK_OBJECT_HANDLE *objectID,
michael@0 574 CK_ATTRIBUTE *template, CK_ULONG count)
michael@0 575 {
michael@0 576 PRBool inTransaction = PR_FALSE;
michael@0 577 CK_RV crv;
michael@0 578
michael@0 579 inTransaction = PR_TRUE;
michael@0 580
michael@0 581 crv = (*db->sdb_CreateObject)(db, objectID, template, count);
michael@0 582 if (crv != CKR_OK) {
michael@0 583 goto loser;
michael@0 584 }
michael@0 585 crv = sftk_signTemplate(arena, handle, (db == handle->update),
michael@0 586 *objectID, template, count);
michael@0 587 loser:
michael@0 588
michael@0 589 return crv;
michael@0 590 }
michael@0 591
michael@0 592
michael@0 593 CK_ATTRIBUTE *
michael@0 594 sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object,
michael@0 595 SFTKDBHandle *handle,CK_ULONG *pcount,
michael@0 596 CK_RV *crv)
michael@0 597 {
michael@0 598 int count;
michael@0 599 CK_ATTRIBUTE *template;
michael@0 600 int i, templateIndex;
michael@0 601 SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
michael@0 602 PRBool doEnc = PR_TRUE;
michael@0 603
michael@0 604 *crv = CKR_OK;
michael@0 605
michael@0 606 if (sessObject == NULL) {
michael@0 607 *crv = CKR_GENERAL_ERROR; /* internal programming error */
michael@0 608 return NULL;
michael@0 609 }
michael@0 610
michael@0 611 PORT_Assert(handle);
michael@0 612 /* find the key handle */
michael@0 613 if (handle->type != SFTK_KEYDB_TYPE) {
michael@0 614 doEnc = PR_FALSE;
michael@0 615 }
michael@0 616
michael@0 617 PZ_Lock(sessObject->attributeLock);
michael@0 618 count = 0;
michael@0 619 for (i=0; i < sessObject->hashSize; i++) {
michael@0 620 SFTKAttribute *attr;
michael@0 621 for (attr=sessObject->head[i]; attr; attr=attr->next) {
michael@0 622 count++;
michael@0 623 }
michael@0 624 }
michael@0 625 template = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, count);
michael@0 626 if (template == NULL) {
michael@0 627 PZ_Unlock(sessObject->attributeLock);
michael@0 628 *crv = CKR_HOST_MEMORY;
michael@0 629 return NULL;
michael@0 630 }
michael@0 631 templateIndex = 0;
michael@0 632 for (i=0; i < sessObject->hashSize; i++) {
michael@0 633 SFTKAttribute *attr;
michael@0 634 for (attr=sessObject->head[i]; attr; attr=attr->next) {
michael@0 635 CK_ATTRIBUTE *tp = &template[templateIndex++];
michael@0 636 /* copy the attribute */
michael@0 637 *tp = attr->attrib;
michael@0 638
michael@0 639 /* fixup ULONG s */
michael@0 640 if ((tp->ulValueLen == sizeof (CK_ULONG)) &&
michael@0 641 (sftkdb_isULONGAttribute(tp->type)) ) {
michael@0 642 CK_ULONG value = *(CK_ULONG *) tp->pValue;
michael@0 643 unsigned char *data;
michael@0 644
michael@0 645 tp->pValue = PORT_ArenaAlloc(arena, SDB_ULONG_SIZE);
michael@0 646 data = (unsigned char *)tp->pValue;
michael@0 647 if (data == NULL) {
michael@0 648 *crv = CKR_HOST_MEMORY;
michael@0 649 break;
michael@0 650 }
michael@0 651 sftk_ULong2SDBULong(data, value);
michael@0 652 tp->ulValueLen = SDB_ULONG_SIZE;
michael@0 653 }
michael@0 654
michael@0 655 /* encrypt private attributes */
michael@0 656 if (doEnc && sftkdb_isPrivateAttribute(tp->type)) {
michael@0 657 /* we have a private attribute */
michael@0 658 SECItem *cipherText;
michael@0 659 SECItem plainText;
michael@0 660 SECStatus rv;
michael@0 661
michael@0 662 plainText.data = tp->pValue;
michael@0 663 plainText.len = tp->ulValueLen;
michael@0 664 PZ_Lock(handle->passwordLock);
michael@0 665 if (handle->passwordKey.data == NULL) {
michael@0 666 PZ_Unlock(handle->passwordLock);
michael@0 667 *crv = CKR_USER_NOT_LOGGED_IN;
michael@0 668 break;
michael@0 669 }
michael@0 670 rv = sftkdb_EncryptAttribute(arena, &handle->passwordKey,
michael@0 671 &plainText, &cipherText);
michael@0 672 PZ_Unlock(handle->passwordLock);
michael@0 673 if (rv == SECSuccess) {
michael@0 674 tp->pValue = cipherText->data;
michael@0 675 tp->ulValueLen = cipherText->len;
michael@0 676 } else {
michael@0 677 *crv = CKR_GENERAL_ERROR; /* better error code here? */
michael@0 678 break;
michael@0 679 }
michael@0 680 PORT_Memset(plainText.data, 0, plainText.len);
michael@0 681 }
michael@0 682 }
michael@0 683 }
michael@0 684 PORT_Assert(templateIndex <= count);
michael@0 685 PZ_Unlock(sessObject->attributeLock);
michael@0 686
michael@0 687 if (*crv != CKR_OK) {
michael@0 688 return NULL;
michael@0 689 }
michael@0 690 if (pcount) {
michael@0 691 *pcount = count;
michael@0 692 }
michael@0 693 return template;
michael@0 694
michael@0 695 }
michael@0 696
michael@0 697 /*
michael@0 698 * return a pointer to the attribute in the give template.
michael@0 699 * The return value is not const, as the caller may modify
michael@0 700 * the given attribute value, but such modifications will
michael@0 701 * modify the actual value in the template.
michael@0 702 */
michael@0 703 static CK_ATTRIBUTE *
michael@0 704 sftkdb_getAttributeFromTemplate(CK_ATTRIBUTE_TYPE attribute,
michael@0 705 CK_ATTRIBUTE *ptemplate, CK_ULONG len)
michael@0 706 {
michael@0 707 CK_ULONG i;
michael@0 708
michael@0 709 for (i=0; i < len; i++) {
michael@0 710 if (attribute == ptemplate[i].type) {
michael@0 711 return &ptemplate[i];
michael@0 712 }
michael@0 713 }
michael@0 714 return NULL;
michael@0 715 }
michael@0 716
michael@0 717 static const CK_ATTRIBUTE *
michael@0 718 sftkdb_getAttributeFromConstTemplate(CK_ATTRIBUTE_TYPE attribute,
michael@0 719 const CK_ATTRIBUTE *ptemplate, CK_ULONG len)
michael@0 720 {
michael@0 721 CK_ULONG i;
michael@0 722
michael@0 723 for (i=0; i < len; i++) {
michael@0 724 if (attribute == ptemplate[i].type) {
michael@0 725 return &ptemplate[i];
michael@0 726 }
michael@0 727 }
michael@0 728 return NULL;
michael@0 729 }
michael@0 730
michael@0 731
michael@0 732 /*
michael@0 733 * fetch a template which identifies 'unique' entries based on object type
michael@0 734 */
michael@0 735 static CK_RV
michael@0 736 sftkdb_getFindTemplate(CK_OBJECT_CLASS objectType, unsigned char *objTypeData,
michael@0 737 CK_ATTRIBUTE *findTemplate, CK_ULONG *findCount,
michael@0 738 CK_ATTRIBUTE *ptemplate, int len)
michael@0 739 {
michael@0 740 CK_ATTRIBUTE *attr;
michael@0 741 CK_ULONG count = 1;
michael@0 742
michael@0 743 sftk_ULong2SDBULong(objTypeData, objectType);
michael@0 744 findTemplate[0].type = CKA_CLASS;
michael@0 745 findTemplate[0].pValue = objTypeData;
michael@0 746 findTemplate[0].ulValueLen = SDB_ULONG_SIZE;
michael@0 747
michael@0 748 switch (objectType) {
michael@0 749 case CKO_CERTIFICATE:
michael@0 750 case CKO_NSS_TRUST:
michael@0 751 attr = sftkdb_getAttributeFromTemplate(CKA_ISSUER, ptemplate, len);
michael@0 752 if (attr == NULL) {
michael@0 753 return CKR_TEMPLATE_INCOMPLETE;
michael@0 754 }
michael@0 755 findTemplate[1] = *attr;
michael@0 756 attr = sftkdb_getAttributeFromTemplate(CKA_SERIAL_NUMBER,
michael@0 757 ptemplate, len);
michael@0 758 if (attr == NULL) {
michael@0 759 return CKR_TEMPLATE_INCOMPLETE;
michael@0 760 }
michael@0 761 findTemplate[2] = *attr;
michael@0 762 count = 3;
michael@0 763 break;
michael@0 764
michael@0 765 case CKO_PRIVATE_KEY:
michael@0 766 case CKO_PUBLIC_KEY:
michael@0 767 case CKO_SECRET_KEY:
michael@0 768 attr = sftkdb_getAttributeFromTemplate(CKA_ID, ptemplate, len);
michael@0 769 if (attr == NULL) {
michael@0 770 return CKR_TEMPLATE_INCOMPLETE;
michael@0 771 }
michael@0 772 if (attr->ulValueLen == 0) {
michael@0 773 /* key is too generic to determine that it's unique, usually
michael@0 774 * happens in the key gen case */
michael@0 775 return CKR_OBJECT_HANDLE_INVALID;
michael@0 776 }
michael@0 777
michael@0 778 findTemplate[1] = *attr;
michael@0 779 count = 2;
michael@0 780 break;
michael@0 781
michael@0 782 case CKO_NSS_CRL:
michael@0 783 attr = sftkdb_getAttributeFromTemplate(CKA_SUBJECT, ptemplate, len);
michael@0 784 if (attr == NULL) {
michael@0 785 return CKR_TEMPLATE_INCOMPLETE;
michael@0 786 }
michael@0 787 findTemplate[1] = *attr;
michael@0 788 count = 2;
michael@0 789 break;
michael@0 790
michael@0 791 case CKO_NSS_SMIME:
michael@0 792 attr = sftkdb_getAttributeFromTemplate(CKA_SUBJECT, ptemplate, len);
michael@0 793 if (attr == NULL) {
michael@0 794 return CKR_TEMPLATE_INCOMPLETE;
michael@0 795 }
michael@0 796 findTemplate[1] = *attr;
michael@0 797 attr = sftkdb_getAttributeFromTemplate(CKA_NSS_EMAIL, ptemplate, len);
michael@0 798 if (attr == NULL) {
michael@0 799 return CKR_TEMPLATE_INCOMPLETE;
michael@0 800 }
michael@0 801 findTemplate[2] = *attr;
michael@0 802 count = 3;
michael@0 803 break;
michael@0 804 default:
michael@0 805 attr = sftkdb_getAttributeFromTemplate(CKA_VALUE, ptemplate, len);
michael@0 806 if (attr == NULL) {
michael@0 807 return CKR_TEMPLATE_INCOMPLETE;
michael@0 808 }
michael@0 809 findTemplate[1] = *attr;
michael@0 810 count = 2;
michael@0 811 break;
michael@0 812 }
michael@0 813 *findCount = count;
michael@0 814
michael@0 815 return CKR_OK;
michael@0 816 }
michael@0 817
michael@0 818 /*
michael@0 819 * look to see if this object already exists and return its object ID if
michael@0 820 * it does.
michael@0 821 */
michael@0 822 static CK_RV
michael@0 823 sftkdb_lookupObject(SDB *db, CK_OBJECT_CLASS objectType,
michael@0 824 CK_OBJECT_HANDLE *id, CK_ATTRIBUTE *ptemplate, CK_ULONG len)
michael@0 825 {
michael@0 826 CK_ATTRIBUTE findTemplate[3];
michael@0 827 CK_ULONG count = 1;
michael@0 828 CK_ULONG objCount = 0;
michael@0 829 SDBFind *find = NULL;
michael@0 830 unsigned char objTypeData[SDB_ULONG_SIZE];
michael@0 831 CK_RV crv;
michael@0 832
michael@0 833 *id = CK_INVALID_HANDLE;
michael@0 834 if (objectType == CKO_NSS_CRL) {
michael@0 835 return CKR_OK;
michael@0 836 }
michael@0 837 crv = sftkdb_getFindTemplate(objectType, objTypeData,
michael@0 838 findTemplate, &count, ptemplate, len);
michael@0 839
michael@0 840 if (crv == CKR_OBJECT_HANDLE_INVALID) {
michael@0 841 /* key is too generic to determine that it's unique, usually
michael@0 842 * happens in the key gen case, tell the caller to go ahead
michael@0 843 * and just create it */
michael@0 844 return CKR_OK;
michael@0 845 }
michael@0 846 if (crv != CKR_OK) {
michael@0 847 return crv;
michael@0 848 }
michael@0 849
michael@0 850 /* use the raw find, so we get the correct database */
michael@0 851 crv = (*db->sdb_FindObjectsInit)(db, findTemplate, count, &find);
michael@0 852 if (crv != CKR_OK) {
michael@0 853 return crv;
michael@0 854 }
michael@0 855 (*db->sdb_FindObjects)(db, find, id, 1, &objCount);
michael@0 856 (*db->sdb_FindObjectsFinal)(db, find);
michael@0 857
michael@0 858 if (objCount == 0) {
michael@0 859 *id = CK_INVALID_HANDLE;
michael@0 860 }
michael@0 861 return CKR_OK;
michael@0 862 }
michael@0 863
michael@0 864
michael@0 865 /*
michael@0 866 * check to see if this template conflicts with others in our current database.
michael@0 867 */
michael@0 868 static CK_RV
michael@0 869 sftkdb_checkConflicts(SDB *db, CK_OBJECT_CLASS objectType,
michael@0 870 const CK_ATTRIBUTE *ptemplate, CK_ULONG len,
michael@0 871 CK_OBJECT_HANDLE sourceID)
michael@0 872 {
michael@0 873 CK_ATTRIBUTE findTemplate[2];
michael@0 874 unsigned char objTypeData[SDB_ULONG_SIZE];
michael@0 875 /* we may need to allocate some temporaries. Keep track of what was
michael@0 876 * allocated so we can free it in the end */
michael@0 877 unsigned char *temp1 = NULL;
michael@0 878 unsigned char *temp2 = NULL;
michael@0 879 CK_ULONG objCount = 0;
michael@0 880 SDBFind *find = NULL;
michael@0 881 CK_OBJECT_HANDLE id;
michael@0 882 const CK_ATTRIBUTE *attr, *attr2;
michael@0 883 CK_RV crv;
michael@0 884 CK_ATTRIBUTE subject;
michael@0 885
michael@0 886 /* Currently the only conflict is with nicknames pointing to the same
michael@0 887 * subject when creating or modifying a certificate. */
michael@0 888 /* If the object is not a cert, no problem. */
michael@0 889 if (objectType != CKO_CERTIFICATE) {
michael@0 890 return CKR_OK;
michael@0 891 }
michael@0 892 /* if not setting a nickname then there's still no problem */
michael@0 893 attr = sftkdb_getAttributeFromConstTemplate(CKA_LABEL, ptemplate, len);
michael@0 894 if ((attr == NULL) || (attr->ulValueLen == 0)) {
michael@0 895 return CKR_OK;
michael@0 896 }
michael@0 897 /* fetch the subject of the source. For creation and merge, this should
michael@0 898 * be found in the template */
michael@0 899 attr2 = sftkdb_getAttributeFromConstTemplate(CKA_SUBJECT, ptemplate, len);
michael@0 900 if (sourceID == CK_INVALID_HANDLE) {
michael@0 901 if ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen < 0)) {
michael@0 902 crv = CKR_TEMPLATE_INCOMPLETE;
michael@0 903 goto done;
michael@0 904 }
michael@0 905 } else if ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen <= 0)) {
michael@0 906 /* sourceID is set if we are trying to modify an existing entry instead
michael@0 907 * of creating a new one. In this case the subject may not be (probably
michael@0 908 * isn't) in the template, we have to read it from the database */
michael@0 909 subject.type = CKA_SUBJECT;
michael@0 910 subject.pValue = NULL;
michael@0 911 subject.ulValueLen = 0;
michael@0 912 crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1);
michael@0 913 if (crv != CKR_OK) {
michael@0 914 goto done;
michael@0 915 }
michael@0 916 if ((CK_LONG)subject.ulValueLen < 0) {
michael@0 917 crv = CKR_DEVICE_ERROR; /* closest pkcs11 error to corrupted DB */
michael@0 918 goto done;
michael@0 919 }
michael@0 920 temp1 = subject.pValue = PORT_Alloc(++subject.ulValueLen);
michael@0 921 if (temp1 == NULL) {
michael@0 922 crv = CKR_HOST_MEMORY;
michael@0 923 goto done;
michael@0 924 }
michael@0 925 crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1);
michael@0 926 if (crv != CKR_OK) {
michael@0 927 goto done;
michael@0 928 }
michael@0 929 attr2 = &subject;
michael@0 930 }
michael@0 931
michael@0 932 /* check for another cert in the database with the same nickname */
michael@0 933 sftk_ULong2SDBULong(objTypeData, objectType);
michael@0 934 findTemplate[0].type = CKA_CLASS;
michael@0 935 findTemplate[0].pValue = objTypeData;
michael@0 936 findTemplate[0].ulValueLen = SDB_ULONG_SIZE;
michael@0 937 findTemplate[1] = *attr;
michael@0 938
michael@0 939 crv = (*db->sdb_FindObjectsInit)(db, findTemplate, 2, &find);
michael@0 940 if (crv != CKR_OK) {
michael@0 941 goto done;
michael@0 942 }
michael@0 943 (*db->sdb_FindObjects)(db, find, &id, 1, &objCount);
michael@0 944 (*db->sdb_FindObjectsFinal)(db, find);
michael@0 945
michael@0 946 /* object count == 0 means no conflicting certs found,
michael@0 947 * go on with the operation */
michael@0 948 if (objCount == 0) {
michael@0 949 crv = CKR_OK;
michael@0 950 goto done;
michael@0 951 }
michael@0 952
michael@0 953 /* There is a least one cert that shares the nickname, make sure it also
michael@0 954 * matches the subject. */
michael@0 955 findTemplate[0] = *attr2;
michael@0 956 /* we know how big the source subject was. Use that length to create the
michael@0 957 * space for the target. If it's not enough space, then it means the
michael@0 958 * source subject is too big, and therefore not a match. GetAttributeValue
michael@0 959 * will return CKR_BUFFER_TOO_SMALL. Otherwise it should be exactly enough
michael@0 960 * space (or enough space to be able to compare the result. */
michael@0 961 temp2 = findTemplate[0].pValue = PORT_Alloc(++findTemplate[0].ulValueLen);
michael@0 962 if (temp2 == NULL) {
michael@0 963 crv = CKR_HOST_MEMORY;
michael@0 964 goto done;
michael@0 965 }
michael@0 966 crv = (*db->sdb_GetAttributeValue)(db, id, findTemplate, 1);
michael@0 967 if (crv != CKR_OK) {
michael@0 968 if (crv == CKR_BUFFER_TOO_SMALL) {
michael@0 969 /* if our buffer is too small, then the Subjects clearly do
michael@0 970 * not match */
michael@0 971 crv = CKR_ATTRIBUTE_VALUE_INVALID;
michael@0 972 goto loser;
michael@0 973 }
michael@0 974 /* otherwise we couldn't get the value, just fail */
michael@0 975 goto done;
michael@0 976 }
michael@0 977
michael@0 978 /* Ok, we have both subjects, make sure they are the same.
michael@0 979 * Compare the subjects */
michael@0 980 if ((findTemplate[0].ulValueLen != attr2->ulValueLen) ||
michael@0 981 (attr2->ulValueLen > 0 &&
michael@0 982 PORT_Memcmp(findTemplate[0].pValue, attr2->pValue, attr2->ulValueLen)
michael@0 983 != 0)) {
michael@0 984 crv = CKR_ATTRIBUTE_VALUE_INVALID;
michael@0 985 goto loser;
michael@0 986 }
michael@0 987 crv = CKR_OK;
michael@0 988
michael@0 989 done:
michael@0 990 /* If we've failed for some other reason than a conflict, make sure we
michael@0 991 * return an error code other than CKR_ATTRIBUTE_VALUE_INVALID.
michael@0 992 * (NOTE: neither sdb_FindObjectsInit nor sdb_GetAttributeValue should
michael@0 993 * return CKR_ATTRIBUTE_VALUE_INVALID, so the following is paranoia).
michael@0 994 */
michael@0 995 if (crv == CKR_ATTRIBUTE_VALUE_INVALID) {
michael@0 996 crv = CKR_GENERAL_ERROR; /* clearly a programming error */
michael@0 997 }
michael@0 998
michael@0 999 /* exit point if we found a conflict */
michael@0 1000 loser:
michael@0 1001 PORT_Free(temp1);
michael@0 1002 PORT_Free(temp2);
michael@0 1003 return crv;
michael@0 1004 }
michael@0 1005
michael@0 1006 /*
michael@0 1007 * try to update the template to fix any errors. This is only done
michael@0 1008 * during update.
michael@0 1009 *
michael@0 1010 * NOTE: we must update the template or return an error, or the update caller
michael@0 1011 * will loop forever!
michael@0 1012 *
michael@0 1013 * Two copies of the source code for this algorithm exist in NSS.
michael@0 1014 * Changes must be made in both copies.
michael@0 1015 * The other copy is in pk11_IncrementNickname() in pk11wrap/pk11merge.c.
michael@0 1016 *
michael@0 1017 */
michael@0 1018 static CK_RV
michael@0 1019 sftkdb_resolveConflicts(PLArenaPool *arena, CK_OBJECT_CLASS objectType,
michael@0 1020 CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
michael@0 1021 {
michael@0 1022 CK_ATTRIBUTE *attr;
michael@0 1023 char *nickname, *newNickname;
michael@0 1024 int end, digit;
michael@0 1025
michael@0 1026 /* sanity checks. We should never get here with these errors */
michael@0 1027 if (objectType != CKO_CERTIFICATE) {
michael@0 1028 return CKR_GENERAL_ERROR; /* shouldn't happen */
michael@0 1029 }
michael@0 1030 attr = sftkdb_getAttributeFromTemplate(CKA_LABEL, ptemplate, *plen);
michael@0 1031 if ((attr == NULL) || (attr->ulValueLen == 0)) {
michael@0 1032 return CKR_GENERAL_ERROR; /* shouldn't happen */
michael@0 1033 }
michael@0 1034
michael@0 1035 /* update the nickname */
michael@0 1036 /* is there a number at the end of the nickname already?
michael@0 1037 * if so just increment that number */
michael@0 1038 nickname = (char *)attr->pValue;
michael@0 1039
michael@0 1040 /* does nickname end with " #n*" ? */
michael@0 1041 for (end = attr->ulValueLen - 1;
michael@0 1042 end >= 2 && (digit = nickname[end]) <= '9' && digit >= '0';
michael@0 1043 end--) /* just scan */ ;
michael@0 1044 if (attr->ulValueLen >= 3 &&
michael@0 1045 end < (attr->ulValueLen - 1) /* at least one digit */ &&
michael@0 1046 nickname[end] == '#' &&
michael@0 1047 nickname[end - 1] == ' ') {
michael@0 1048 /* Already has a suitable suffix string */
michael@0 1049 } else {
michael@0 1050 /* ... append " #2" to the name */
michael@0 1051 static const char num2[] = " #2";
michael@0 1052 newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + sizeof(num2));
michael@0 1053 if (!newNickname) {
michael@0 1054 return CKR_HOST_MEMORY;
michael@0 1055 }
michael@0 1056 PORT_Memcpy(newNickname, nickname, attr->ulValueLen);
michael@0 1057 PORT_Memcpy(&newNickname[attr->ulValueLen], num2, sizeof(num2));
michael@0 1058 attr->pValue = newNickname; /* modifies ptemplate */
michael@0 1059 attr->ulValueLen += 3; /* 3 is strlen(num2) */
michael@0 1060 return CKR_OK;
michael@0 1061 }
michael@0 1062
michael@0 1063 for (end = attr->ulValueLen - 1;
michael@0 1064 end >= 0 && (digit = nickname[end]) <= '9' && digit >= '0';
michael@0 1065 end--) {
michael@0 1066 if (digit < '9') {
michael@0 1067 nickname[end]++;
michael@0 1068 return CKR_OK;
michael@0 1069 }
michael@0 1070 nickname[end] = '0';
michael@0 1071 }
michael@0 1072
michael@0 1073 /* we overflowed, insert a new '1' for a carry in front of the number */
michael@0 1074 newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + 1);
michael@0 1075 if (!newNickname) {
michael@0 1076 return CKR_HOST_MEMORY;
michael@0 1077 }
michael@0 1078 /* PORT_Memcpy should handle len of '0' */
michael@0 1079 PORT_Memcpy(newNickname, nickname, ++end);
michael@0 1080 newNickname[end] = '1';
michael@0 1081 PORT_Memset(&newNickname[end+1],'0',attr->ulValueLen - end);
michael@0 1082 attr->pValue = newNickname;
michael@0 1083 attr->ulValueLen++;
michael@0 1084 return CKR_OK;
michael@0 1085 }
michael@0 1086
michael@0 1087 /*
michael@0 1088 * set an attribute and sign it if necessary
michael@0 1089 */
michael@0 1090 static CK_RV
michael@0 1091 sftkdb_setAttributeValue(PLArenaPool *arena, SFTKDBHandle *handle,
michael@0 1092 SDB *db, CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template,
michael@0 1093 CK_ULONG count)
michael@0 1094 {
michael@0 1095 CK_RV crv;
michael@0 1096 crv = (*db->sdb_SetAttributeValue)(db, objectID, template, count);
michael@0 1097 if (crv != CKR_OK) {
michael@0 1098 return crv;
michael@0 1099 }
michael@0 1100 crv = sftk_signTemplate(arena, handle, db == handle->update,
michael@0 1101 objectID, template, count);
michael@0 1102 return crv;
michael@0 1103 }
michael@0 1104
michael@0 1105 /*
michael@0 1106 * write a softoken object out to the database.
michael@0 1107 */
michael@0 1108 CK_RV
michael@0 1109 sftkdb_write(SFTKDBHandle *handle, SFTKObject *object,
michael@0 1110 CK_OBJECT_HANDLE *objectID)
michael@0 1111 {
michael@0 1112 CK_ATTRIBUTE *template;
michael@0 1113 PLArenaPool *arena;
michael@0 1114 CK_ULONG count;
michael@0 1115 CK_RV crv;
michael@0 1116 SDB *db;
michael@0 1117 PRBool inTransaction = PR_FALSE;
michael@0 1118 CK_OBJECT_HANDLE id;
michael@0 1119
michael@0 1120 *objectID = CK_INVALID_HANDLE;
michael@0 1121
michael@0 1122 if (handle == NULL) {
michael@0 1123 return CKR_TOKEN_WRITE_PROTECTED;
michael@0 1124 }
michael@0 1125 db = SFTK_GET_SDB(handle);
michael@0 1126
michael@0 1127 /*
michael@0 1128 * we have opened a new database, but we have not yet updated it. We are
michael@0 1129 * still running pointing to the old database (so the application can
michael@0 1130 * still read). We don't want to write to the old database at this point,
michael@0 1131 * however, since it leads to user confusion. So at this point we simply
michael@0 1132 * require a user login. Let NSS know this so it can prompt the user.
michael@0 1133 */
michael@0 1134 if (db == handle->update) {
michael@0 1135 return CKR_USER_NOT_LOGGED_IN;
michael@0 1136 }
michael@0 1137
michael@0 1138 arena = PORT_NewArena(256);
michael@0 1139 if (arena == NULL) {
michael@0 1140 return CKR_HOST_MEMORY;
michael@0 1141 }
michael@0 1142
michael@0 1143 template = sftk_ExtractTemplate(arena, object, handle, &count, &crv);
michael@0 1144 if (!template) {
michael@0 1145 goto loser;
michael@0 1146 }
michael@0 1147
michael@0 1148 crv = (*db->sdb_Begin)(db);
michael@0 1149 if (crv != CKR_OK) {
michael@0 1150 goto loser;
michael@0 1151 }
michael@0 1152 inTransaction = PR_TRUE;
michael@0 1153
michael@0 1154 /*
michael@0 1155 * We want to make the base database as free from object specific knowledge
michael@0 1156 * as possible. To maintain compatibility, keep some of the desirable
michael@0 1157 * object specific semantics of the old database.
michael@0 1158 *
michael@0 1159 * These were 2 fold:
michael@0 1160 * 1) there were certain conflicts (like trying to set the same nickname
michael@0 1161 * on two different subjects) that would return an error.
michael@0 1162 * 2) Importing the 'same' object would silently update that object.
michael@0 1163 *
michael@0 1164 * The following 2 functions mimic the desirable effects of these two
michael@0 1165 * semantics without pushing any object knowledge to the underlying database
michael@0 1166 * code.
michael@0 1167 */
michael@0 1168
michael@0 1169 /* make sure we don't have attributes that conflict with the existing DB */
michael@0 1170 crv = sftkdb_checkConflicts(db, object->objclass, template, count,
michael@0 1171 CK_INVALID_HANDLE);
michael@0 1172 if (crv != CKR_OK) {
michael@0 1173 goto loser;
michael@0 1174 }
michael@0 1175 /* Find any copies that match this particular object */
michael@0 1176 crv = sftkdb_lookupObject(db, object->objclass, &id, template, count);
michael@0 1177 if (crv != CKR_OK) {
michael@0 1178 goto loser;
michael@0 1179 }
michael@0 1180 if (id == CK_INVALID_HANDLE) {
michael@0 1181 crv = sftkdb_CreateObject(arena, handle, db, objectID, template, count);
michael@0 1182 } else {
michael@0 1183 /* object already exists, modify it's attributes */
michael@0 1184 *objectID = id;
michael@0 1185 crv = sftkdb_setAttributeValue(arena, handle, db, id, template, count);
michael@0 1186 }
michael@0 1187 if (crv != CKR_OK) {
michael@0 1188 goto loser;
michael@0 1189 }
michael@0 1190
michael@0 1191 crv = (*db->sdb_Commit)(db);
michael@0 1192 inTransaction = PR_FALSE;
michael@0 1193
michael@0 1194 loser:
michael@0 1195 if (inTransaction) {
michael@0 1196 (*db->sdb_Abort)(db);
michael@0 1197 /* It is trivial to show the following code cannot
michael@0 1198 * happen unless something is horribly wrong with our compilier or
michael@0 1199 * hardware */
michael@0 1200 PORT_Assert(crv != CKR_OK);
michael@0 1201 if (crv == CKR_OK) crv = CKR_GENERAL_ERROR;
michael@0 1202 }
michael@0 1203
michael@0 1204 if (arena) {
michael@0 1205 PORT_FreeArena(arena,PR_FALSE);
michael@0 1206 }
michael@0 1207 if (crv == CKR_OK) {
michael@0 1208 *objectID |= (handle->type | SFTK_TOKEN_TYPE);
michael@0 1209 }
michael@0 1210 return crv;
michael@0 1211 }
michael@0 1212
michael@0 1213
michael@0 1214 CK_RV
michael@0 1215 sftkdb_FindObjectsInit(SFTKDBHandle *handle, const CK_ATTRIBUTE *template,
michael@0 1216 CK_ULONG count, SDBFind **find)
michael@0 1217 {
michael@0 1218 unsigned char *data = NULL;
michael@0 1219 CK_ATTRIBUTE *ntemplate = NULL;
michael@0 1220 CK_RV crv;
michael@0 1221 SDB *db;
michael@0 1222
michael@0 1223 if (handle == NULL) {
michael@0 1224 return CKR_OK;
michael@0 1225 }
michael@0 1226 db = SFTK_GET_SDB(handle);
michael@0 1227
michael@0 1228 if (count != 0) {
michael@0 1229 ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
michael@0 1230 if (ntemplate == NULL) {
michael@0 1231 return CKR_HOST_MEMORY;
michael@0 1232 }
michael@0 1233 }
michael@0 1234
michael@0 1235 crv = (*db->sdb_FindObjectsInit)(db, ntemplate,
michael@0 1236 count, find);
michael@0 1237 if (data) {
michael@0 1238 PORT_Free(ntemplate);
michael@0 1239 PORT_Free(data);
michael@0 1240 }
michael@0 1241 return crv;
michael@0 1242 }
michael@0 1243
michael@0 1244 CK_RV
michael@0 1245 sftkdb_FindObjects(SFTKDBHandle *handle, SDBFind *find,
michael@0 1246 CK_OBJECT_HANDLE *ids, int arraySize, CK_ULONG *count)
michael@0 1247 {
michael@0 1248 CK_RV crv;
michael@0 1249 SDB *db;
michael@0 1250
michael@0 1251 if (handle == NULL) {
michael@0 1252 *count = 0;
michael@0 1253 return CKR_OK;
michael@0 1254 }
michael@0 1255 db = SFTK_GET_SDB(handle);
michael@0 1256
michael@0 1257 crv = (*db->sdb_FindObjects)(db, find, ids,
michael@0 1258 arraySize, count);
michael@0 1259 if (crv == CKR_OK) {
michael@0 1260 int i;
michael@0 1261 for (i=0; i < *count; i++) {
michael@0 1262 ids[i] |= (handle->type | SFTK_TOKEN_TYPE);
michael@0 1263 }
michael@0 1264 }
michael@0 1265 return crv;
michael@0 1266 }
michael@0 1267
michael@0 1268 CK_RV sftkdb_FindObjectsFinal(SFTKDBHandle *handle, SDBFind *find)
michael@0 1269 {
michael@0 1270 SDB *db;
michael@0 1271 if (handle == NULL) {
michael@0 1272 return CKR_OK;
michael@0 1273 }
michael@0 1274 db = SFTK_GET_SDB(handle);
michael@0 1275 return (*db->sdb_FindObjectsFinal)(db, find);
michael@0 1276 }
michael@0 1277
michael@0 1278 CK_RV
michael@0 1279 sftkdb_GetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID,
michael@0 1280 CK_ATTRIBUTE *template, CK_ULONG count)
michael@0 1281 {
michael@0 1282 CK_RV crv,crv2;
michael@0 1283 CK_ATTRIBUTE *ntemplate;
michael@0 1284 unsigned char *data = NULL;
michael@0 1285 SDB *db;
michael@0 1286
michael@0 1287 if (handle == NULL) {
michael@0 1288 return CKR_GENERAL_ERROR;
michael@0 1289 }
michael@0 1290
michael@0 1291 /* short circuit common attributes */
michael@0 1292 if (count == 1 &&
michael@0 1293 (template[0].type == CKA_TOKEN ||
michael@0 1294 template[0].type == CKA_PRIVATE ||
michael@0 1295 template[0].type == CKA_SENSITIVE)) {
michael@0 1296 CK_BBOOL boolVal = CK_TRUE;
michael@0 1297
michael@0 1298 if (template[0].pValue == NULL) {
michael@0 1299 template[0].ulValueLen = sizeof(CK_BBOOL);
michael@0 1300 return CKR_OK;
michael@0 1301 }
michael@0 1302 if (template[0].ulValueLen < sizeof(CK_BBOOL)) {
michael@0 1303 template[0].ulValueLen = -1;
michael@0 1304 return CKR_BUFFER_TOO_SMALL;
michael@0 1305 }
michael@0 1306
michael@0 1307 if ((template[0].type == CKA_PRIVATE) &&
michael@0 1308 (handle->type != SFTK_KEYDB_TYPE)) {
michael@0 1309 boolVal = CK_FALSE;
michael@0 1310 }
michael@0 1311 if ((template[0].type == CKA_SENSITIVE) &&
michael@0 1312 (handle->type != SFTK_KEYDB_TYPE)) {
michael@0 1313 boolVal = CK_FALSE;
michael@0 1314 }
michael@0 1315 *(CK_BBOOL *)template[0].pValue = boolVal;
michael@0 1316 template[0].ulValueLen = sizeof(CK_BBOOL);
michael@0 1317 return CKR_OK;
michael@0 1318 }
michael@0 1319
michael@0 1320 db = SFTK_GET_SDB(handle);
michael@0 1321 /* nothing to do */
michael@0 1322 if (count == 0) {
michael@0 1323 return CKR_OK;
michael@0 1324 }
michael@0 1325 ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
michael@0 1326 if (ntemplate == NULL) {
michael@0 1327 return CKR_HOST_MEMORY;
michael@0 1328 }
michael@0 1329 objectID &= SFTK_OBJ_ID_MASK;
michael@0 1330 crv = (*db->sdb_GetAttributeValue)(db, objectID,
michael@0 1331 ntemplate, count);
michael@0 1332 crv2 = sftkdb_fixupTemplateOut(template, objectID, ntemplate,
michael@0 1333 count, handle);
michael@0 1334 if (crv == CKR_OK) crv = crv2;
michael@0 1335 if (data) {
michael@0 1336 PORT_Free(ntemplate);
michael@0 1337 PORT_Free(data);
michael@0 1338 }
michael@0 1339 return crv;
michael@0 1340
michael@0 1341 }
michael@0 1342
michael@0 1343 CK_RV
michael@0 1344 sftkdb_SetAttributeValue(SFTKDBHandle *handle, SFTKObject *object,
michael@0 1345 const CK_ATTRIBUTE *template, CK_ULONG count)
michael@0 1346 {
michael@0 1347 CK_ATTRIBUTE *ntemplate;
michael@0 1348 unsigned char *data = NULL;
michael@0 1349 PLArenaPool *arena = NULL;
michael@0 1350 SDB *db;
michael@0 1351 CK_RV crv = CKR_OK;
michael@0 1352 CK_OBJECT_HANDLE objectID = (object->handle & SFTK_OBJ_ID_MASK);
michael@0 1353 PRBool inTransaction = PR_FALSE;
michael@0 1354
michael@0 1355 if (handle == NULL) {
michael@0 1356 return CKR_TOKEN_WRITE_PROTECTED;
michael@0 1357 }
michael@0 1358
michael@0 1359 db = SFTK_GET_SDB(handle);
michael@0 1360 /* nothing to do */
michael@0 1361 if (count == 0) {
michael@0 1362 return CKR_OK;
michael@0 1363 }
michael@0 1364 /*
michael@0 1365 * we have opened a new database, but we have not yet updated it. We are
michael@0 1366 * still running pointing to the old database (so the application can
michael@0 1367 * still read). We don't want to write to the old database at this point,
michael@0 1368 * however, since it leads to user confusion. So at this point we simply
michael@0 1369 * require a user login. Let NSS know this so it can prompt the user.
michael@0 1370 */
michael@0 1371 if (db == handle->update) {
michael@0 1372 return CKR_USER_NOT_LOGGED_IN;
michael@0 1373 }
michael@0 1374
michael@0 1375 ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
michael@0 1376 if (ntemplate == NULL) {
michael@0 1377 return CKR_HOST_MEMORY;
michael@0 1378 }
michael@0 1379
michael@0 1380 /* make sure we don't have attributes that conflict with the existing DB */
michael@0 1381 crv = sftkdb_checkConflicts(db, object->objclass, template, count, objectID);
michael@0 1382 if (crv != CKR_OK) {
michael@0 1383 goto loser;
michael@0 1384 }
michael@0 1385
michael@0 1386 arena = PORT_NewArena(256);
michael@0 1387 if (arena == NULL) {
michael@0 1388 crv = CKR_HOST_MEMORY;
michael@0 1389 goto loser;
michael@0 1390 }
michael@0 1391
michael@0 1392 crv = (*db->sdb_Begin)(db);
michael@0 1393 if (crv != CKR_OK) {
michael@0 1394 goto loser;
michael@0 1395 }
michael@0 1396 inTransaction = PR_TRUE;
michael@0 1397 crv = sftkdb_setAttributeValue(arena, handle, db,
michael@0 1398 objectID, template, count);
michael@0 1399 if (crv != CKR_OK) {
michael@0 1400 goto loser;
michael@0 1401 }
michael@0 1402 crv = (*db->sdb_Commit)(db);
michael@0 1403 loser:
michael@0 1404 if (crv != CKR_OK && inTransaction) {
michael@0 1405 (*db->sdb_Abort)(db);
michael@0 1406 }
michael@0 1407 if (data) {
michael@0 1408 PORT_Free(ntemplate);
michael@0 1409 PORT_Free(data);
michael@0 1410 }
michael@0 1411 if (arena) {
michael@0 1412 PORT_FreeArena(arena, PR_FALSE);
michael@0 1413 }
michael@0 1414 return crv;
michael@0 1415 }
michael@0 1416
michael@0 1417 CK_RV
michael@0 1418 sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID)
michael@0 1419 {
michael@0 1420 CK_RV crv = CKR_OK;
michael@0 1421 SDB *db;
michael@0 1422
michael@0 1423 if (handle == NULL) {
michael@0 1424 return CKR_TOKEN_WRITE_PROTECTED;
michael@0 1425 }
michael@0 1426 db = SFTK_GET_SDB(handle);
michael@0 1427 objectID &= SFTK_OBJ_ID_MASK;
michael@0 1428 crv = (*db->sdb_Begin)(db);
michael@0 1429 if (crv != CKR_OK) {
michael@0 1430 goto loser;
michael@0 1431 }
michael@0 1432 crv = (*db->sdb_DestroyObject)(db, objectID);
michael@0 1433 if (crv != CKR_OK) {
michael@0 1434 goto loser;
michael@0 1435 }
michael@0 1436 crv = (*db->sdb_Commit)(db);
michael@0 1437 loser:
michael@0 1438 if (crv != CKR_OK) {
michael@0 1439 (*db->sdb_Abort)(db);
michael@0 1440 }
michael@0 1441 return crv;
michael@0 1442 }
michael@0 1443
michael@0 1444 CK_RV
michael@0 1445 sftkdb_CloseDB(SFTKDBHandle *handle)
michael@0 1446 {
michael@0 1447 #ifdef NO_FORK_CHECK
michael@0 1448 PRBool parentForkedAfterC_Initialize = PR_FALSE;
michael@0 1449 #endif
michael@0 1450 if (handle == NULL) {
michael@0 1451 return CKR_OK;
michael@0 1452 }
michael@0 1453 if (handle->update) {
michael@0 1454 if (handle->db->sdb_SetForkState) {
michael@0 1455 (*handle->db->sdb_SetForkState)(parentForkedAfterC_Initialize);
michael@0 1456 }
michael@0 1457 (*handle->update->sdb_Close)(handle->update);
michael@0 1458 }
michael@0 1459 if (handle->db) {
michael@0 1460 if (handle->db->sdb_SetForkState) {
michael@0 1461 (*handle->db->sdb_SetForkState)(parentForkedAfterC_Initialize);
michael@0 1462 }
michael@0 1463 (*handle->db->sdb_Close)(handle->db);
michael@0 1464 }
michael@0 1465 if (handle->passwordKey.data) {
michael@0 1466 PORT_ZFree(handle->passwordKey.data, handle->passwordKey.len);
michael@0 1467 }
michael@0 1468 if (handle->passwordLock) {
michael@0 1469 SKIP_AFTER_FORK(PZ_DestroyLock(handle->passwordLock));
michael@0 1470 }
michael@0 1471 if (handle->updatePasswordKey) {
michael@0 1472 SECITEM_FreeItem(handle->updatePasswordKey, PR_TRUE);
michael@0 1473 }
michael@0 1474 if (handle->updateID) {
michael@0 1475 PORT_Free(handle->updateID);
michael@0 1476 }
michael@0 1477 PORT_Free(handle);
michael@0 1478 return CKR_OK;
michael@0 1479 }
michael@0 1480
michael@0 1481 /*
michael@0 1482 * reset a database to it's uninitialized state.
michael@0 1483 */
michael@0 1484 static CK_RV
michael@0 1485 sftkdb_ResetDB(SFTKDBHandle *handle)
michael@0 1486 {
michael@0 1487 CK_RV crv = CKR_OK;
michael@0 1488 SDB *db;
michael@0 1489 if (handle == NULL) {
michael@0 1490 return CKR_TOKEN_WRITE_PROTECTED;
michael@0 1491 }
michael@0 1492 db = SFTK_GET_SDB(handle);
michael@0 1493 crv = (*db->sdb_Begin)(db);
michael@0 1494 if (crv != CKR_OK) {
michael@0 1495 goto loser;
michael@0 1496 }
michael@0 1497 crv = (*db->sdb_Reset)(db);
michael@0 1498 if (crv != CKR_OK) {
michael@0 1499 goto loser;
michael@0 1500 }
michael@0 1501 crv = (*db->sdb_Commit)(db);
michael@0 1502 loser:
michael@0 1503 if (crv != CKR_OK) {
michael@0 1504 (*db->sdb_Abort)(db);
michael@0 1505 }
michael@0 1506 return crv;
michael@0 1507 }
michael@0 1508
michael@0 1509
michael@0 1510 CK_RV
michael@0 1511 sftkdb_Begin(SFTKDBHandle *handle)
michael@0 1512 {
michael@0 1513 CK_RV crv = CKR_OK;
michael@0 1514 SDB *db;
michael@0 1515
michael@0 1516 if (handle == NULL) {
michael@0 1517 return CKR_OK;
michael@0 1518 }
michael@0 1519 db = SFTK_GET_SDB(handle);
michael@0 1520 if (db) {
michael@0 1521 crv = (*db->sdb_Begin)(db);
michael@0 1522 }
michael@0 1523 return crv;
michael@0 1524 }
michael@0 1525
michael@0 1526 CK_RV
michael@0 1527 sftkdb_Commit(SFTKDBHandle *handle)
michael@0 1528 {
michael@0 1529 CK_RV crv = CKR_OK;
michael@0 1530 SDB *db;
michael@0 1531
michael@0 1532 if (handle == NULL) {
michael@0 1533 return CKR_OK;
michael@0 1534 }
michael@0 1535 db = SFTK_GET_SDB(handle);
michael@0 1536 if (db) {
michael@0 1537 (*db->sdb_Commit)(db);
michael@0 1538 }
michael@0 1539 return crv;
michael@0 1540 }
michael@0 1541
michael@0 1542 CK_RV
michael@0 1543 sftkdb_Abort(SFTKDBHandle *handle)
michael@0 1544 {
michael@0 1545 CK_RV crv = CKR_OK;
michael@0 1546 SDB *db;
michael@0 1547
michael@0 1548 if (handle == NULL) {
michael@0 1549 return CKR_OK;
michael@0 1550 }
michael@0 1551 db = SFTK_GET_SDB(handle);
michael@0 1552 if (db) {
michael@0 1553 crv = (db->sdb_Abort)(db);
michael@0 1554 }
michael@0 1555 return crv;
michael@0 1556 }
michael@0 1557
michael@0 1558
michael@0 1559 /*
michael@0 1560 * functions to update the database from an old database
michael@0 1561 */
michael@0 1562
michael@0 1563 /*
michael@0 1564 * known attributes
michael@0 1565 */
michael@0 1566 static const CK_ATTRIBUTE_TYPE known_attributes[] = {
michael@0 1567 CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION,
michael@0 1568 CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER,
michael@0 1569 CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED,
michael@0 1570 CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL,
michael@0 1571 CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY,
michael@0 1572 CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE,
michael@0 1573 CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER,
michael@0 1574 CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE,
michael@0 1575 CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
michael@0 1576 CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT,
michael@0 1577 CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS,
michael@0 1578 CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE,
michael@0 1579 CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE,
michael@0 1580 CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS,
michael@0 1581 CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
michael@0 1582 CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_TEMPLATE,
michael@0 1583 CKA_UNWRAP_TEMPLATE, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT,
michael@0 1584 CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, CKA_RESOLUTION, CKA_CHAR_ROWS,
michael@0 1585 CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS,
michael@0 1586 CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE,
michael@0 1587 CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES,
michael@0 1588 CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NSS_URL, CKA_NSS_EMAIL,
michael@0 1589 CKA_NSS_SMIME_INFO, CKA_NSS_SMIME_TIMESTAMP,
michael@0 1590 CKA_NSS_PKCS8_SALT, CKA_NSS_PASSWORD_CHECK, CKA_NSS_EXPIRES,
michael@0 1591 CKA_NSS_KRL, CKA_NSS_PQG_COUNTER, CKA_NSS_PQG_SEED,
michael@0 1592 CKA_NSS_PQG_H, CKA_NSS_PQG_SEED_BITS, CKA_NSS_MODULE_SPEC,
michael@0 1593 CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION,
michael@0 1594 CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT,
michael@0 1595 CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN,
michael@0 1596 CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING,
michael@0 1597 CKA_TRUST_EMAIL_PROTECTION, CKA_TRUST_IPSEC_END_SYSTEM,
michael@0 1598 CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING,
michael@0 1599 CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
michael@0 1600 CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS
michael@0 1601 };
michael@0 1602
michael@0 1603 static int known_attributes_size= sizeof(known_attributes)/
michael@0 1604 sizeof(known_attributes[0]);
michael@0 1605
michael@0 1606 static CK_RV
michael@0 1607 sftkdb_GetObjectTemplate(SDB *source, CK_OBJECT_HANDLE id,
michael@0 1608 CK_ATTRIBUTE *ptemplate, CK_ULONG *max)
michael@0 1609 {
michael@0 1610 int i,j;
michael@0 1611 CK_RV crv;
michael@0 1612
michael@0 1613 if (*max < known_attributes_size) {
michael@0 1614 *max = known_attributes_size;
michael@0 1615 return CKR_BUFFER_TOO_SMALL;
michael@0 1616 }
michael@0 1617 for (i=0; i < known_attributes_size; i++) {
michael@0 1618 ptemplate[i].type = known_attributes[i];
michael@0 1619 ptemplate[i].pValue = NULL;
michael@0 1620 ptemplate[i].ulValueLen = 0;
michael@0 1621 }
michael@0 1622
michael@0 1623 crv = (*source->sdb_GetAttributeValue)(source, id,
michael@0 1624 ptemplate, known_attributes_size);
michael@0 1625
michael@0 1626 if ((crv != CKR_OK) && (crv != CKR_ATTRIBUTE_TYPE_INVALID)) {
michael@0 1627 return crv;
michael@0 1628 }
michael@0 1629
michael@0 1630 for (i=0, j=0; i < known_attributes_size; i++, j++) {
michael@0 1631 while (i < known_attributes_size && (ptemplate[i].ulValueLen == -1)) {
michael@0 1632 i++;
michael@0 1633 }
michael@0 1634 if (i >= known_attributes_size) {
michael@0 1635 break;
michael@0 1636 }
michael@0 1637 /* cheap optimization */
michael@0 1638 if (i == j) {
michael@0 1639 continue;
michael@0 1640 }
michael@0 1641 ptemplate[j] = ptemplate[i];
michael@0 1642 }
michael@0 1643 *max = j;
michael@0 1644 return CKR_OK;
michael@0 1645 }
michael@0 1646
michael@0 1647 static const char SFTKDB_META_UPDATE_TEMPLATE[] = "upd_%s_%s";
michael@0 1648
michael@0 1649 /*
michael@0 1650 * check to see if we have already updated this database.
michael@0 1651 * a NULL updateID means we are trying to do an in place
michael@0 1652 * single database update. In that case we have already
michael@0 1653 * determined that an update was necessary.
michael@0 1654 */
michael@0 1655 static PRBool
michael@0 1656 sftkdb_hasUpdate(const char *typeString, SDB *db, const char *updateID)
michael@0 1657 {
michael@0 1658 char *id;
michael@0 1659 CK_RV crv;
michael@0 1660 SECItem dummy = { 0, NULL, 0 };
michael@0 1661 unsigned char dummyData[SDB_MAX_META_DATA_LEN];
michael@0 1662
michael@0 1663 if (!updateID) {
michael@0 1664 return PR_FALSE;
michael@0 1665 }
michael@0 1666 id = PR_smprintf(SFTKDB_META_UPDATE_TEMPLATE, typeString, updateID);
michael@0 1667 if (id == NULL) {
michael@0 1668 return PR_FALSE;
michael@0 1669 }
michael@0 1670 dummy.data = dummyData;
michael@0 1671 dummy.len = sizeof(dummyData);
michael@0 1672
michael@0 1673 crv = (*db->sdb_GetMetaData)(db, id, &dummy, NULL);
michael@0 1674 PR_smprintf_free(id);
michael@0 1675 return crv == CKR_OK ? PR_TRUE : PR_FALSE;
michael@0 1676 }
michael@0 1677
michael@0 1678 /*
michael@0 1679 * we just completed an update, store the update id
michael@0 1680 * so we don't need to do it again. If non was given,
michael@0 1681 * there is nothing to do.
michael@0 1682 */
michael@0 1683 static CK_RV
michael@0 1684 sftkdb_putUpdate(const char *typeString, SDB *db, const char *updateID)
michael@0 1685 {
michael@0 1686 char *id;
michael@0 1687 CK_RV crv;
michael@0 1688 SECItem dummy = { 0, NULL, 0 };
michael@0 1689
michael@0 1690 /* if no id was given, nothing to do */
michael@0 1691 if (updateID == NULL) {
michael@0 1692 return CKR_OK;
michael@0 1693 }
michael@0 1694
michael@0 1695 dummy.data = (unsigned char *)updateID;
michael@0 1696 dummy.len = PORT_Strlen(updateID);
michael@0 1697
michael@0 1698 id = PR_smprintf(SFTKDB_META_UPDATE_TEMPLATE, typeString, updateID);
michael@0 1699 if (id == NULL) {
michael@0 1700 return PR_FALSE;
michael@0 1701 }
michael@0 1702
michael@0 1703 crv = (*db->sdb_PutMetaData)(db, id, &dummy, NULL);
michael@0 1704 PR_smprintf_free(id);
michael@0 1705 return crv;
michael@0 1706 }
michael@0 1707
michael@0 1708 /*
michael@0 1709 * get a ULong attribute from a template:
michael@0 1710 * NOTE: this is a raw templated stored in database order!
michael@0 1711 */
michael@0 1712 static CK_ULONG
michael@0 1713 sftkdb_getULongFromTemplate(CK_ATTRIBUTE_TYPE type,
michael@0 1714 CK_ATTRIBUTE *ptemplate, CK_ULONG len)
michael@0 1715 {
michael@0 1716 CK_ATTRIBUTE *attr = sftkdb_getAttributeFromTemplate(type,
michael@0 1717 ptemplate, len);
michael@0 1718
michael@0 1719 if (attr && attr->pValue && attr->ulValueLen == SDB_ULONG_SIZE) {
michael@0 1720 return sftk_SDBULong2ULong(attr->pValue);
michael@0 1721 }
michael@0 1722 return (CK_ULONG)-1;
michael@0 1723 }
michael@0 1724
michael@0 1725 /*
michael@0 1726 * we need to find a unique CKA_ID.
michael@0 1727 * The basic idea is to just increment the lowest byte.
michael@0 1728 * This code also handles the following corner cases:
michael@0 1729 * 1) the single byte overflows. On overflow we increment the next byte up
michael@0 1730 * and so forth until we have overflowed the entire CKA_ID.
michael@0 1731 * 2) If we overflow the entire CKA_ID we expand it by one byte.
michael@0 1732 * 3) the CKA_ID is non-existant, we create a new one with one byte.
michael@0 1733 * This means no matter what CKA_ID is passed, the result of this function
michael@0 1734 * is always a new CKA_ID, and this function will never return the same
michael@0 1735 * CKA_ID the it has returned in the passed.
michael@0 1736 */
michael@0 1737 static CK_RV
michael@0 1738 sftkdb_incrementCKAID(PLArenaPool *arena, CK_ATTRIBUTE *ptemplate)
michael@0 1739 {
michael@0 1740 unsigned char *buf = ptemplate->pValue;
michael@0 1741 CK_ULONG len = ptemplate->ulValueLen;
michael@0 1742
michael@0 1743 if (buf == NULL || len == (CK_ULONG)-1) {
michael@0 1744 /* we have no valid CKAID, we'll create a basic one byte CKA_ID below */
michael@0 1745 len = 0;
michael@0 1746 } else {
michael@0 1747 CK_ULONG i;
michael@0 1748
michael@0 1749 /* walk from the back to front, incrementing
michael@0 1750 * the CKA_ID until we no longer have a carry,
michael@0 1751 * or have hit the front of the id. */
michael@0 1752 for (i=len; i != 0; i--) {
michael@0 1753 buf[i-1]++;
michael@0 1754 if (buf[i-1] != 0) {
michael@0 1755 /* no more carries, the increment is complete */
michael@0 1756 return CKR_OK;
michael@0 1757 }
michael@0 1758 }
michael@0 1759 /* we've now overflowed, fall through and expand the CKA_ID by
michael@0 1760 * one byte */
michael@0 1761 }
michael@0 1762 buf = PORT_ArenaAlloc(arena, len+1);
michael@0 1763 if (!buf) {
michael@0 1764 return CKR_HOST_MEMORY;
michael@0 1765 }
michael@0 1766 if (len > 0) {
michael@0 1767 PORT_Memcpy(buf, ptemplate->pValue, len);
michael@0 1768 }
michael@0 1769 buf[len] = 0;
michael@0 1770 ptemplate->pValue = buf;
michael@0 1771 ptemplate->ulValueLen = len+1;
michael@0 1772 return CKR_OK;
michael@0 1773 }
michael@0 1774
michael@0 1775 /*
michael@0 1776 * drop an attribute from a template.
michael@0 1777 */
michael@0 1778 void
michael@0 1779 sftkdb_dropAttribute(CK_ATTRIBUTE *attr, CK_ATTRIBUTE *ptemplate,
michael@0 1780 CK_ULONG *plen)
michael@0 1781 {
michael@0 1782 CK_ULONG count = *plen;
michael@0 1783 CK_ULONG i;
michael@0 1784
michael@0 1785 for (i=0; i < count; i++) {
michael@0 1786 if (attr->type == ptemplate[i].type) {
michael@0 1787 break;
michael@0 1788 }
michael@0 1789 }
michael@0 1790
michael@0 1791 if (i == count) {
michael@0 1792 /* attribute not found */
michael@0 1793 return;
michael@0 1794 }
michael@0 1795
michael@0 1796 /* copy the remaining attributes up */
michael@0 1797 for ( i++; i < count; i++) {
michael@0 1798 ptemplate[i-1] = ptemplate[i];
michael@0 1799 }
michael@0 1800
michael@0 1801 /* decrement the template size */
michael@0 1802 *plen = count -1;
michael@0 1803 }
michael@0 1804
michael@0 1805 /*
michael@0 1806 * create some defines for the following functions to document the meaning
michael@0 1807 * of true/false. (make's it easier to remember what means what.
michael@0 1808 */
michael@0 1809 typedef enum {
michael@0 1810 SFTKDB_DO_NOTHING = 0,
michael@0 1811 SFTKDB_ADD_OBJECT,
michael@0 1812 SFTKDB_MODIFY_OBJECT,
michael@0 1813 SFTKDB_DROP_ATTRIBUTE
michael@0 1814 } sftkdbUpdateStatus;
michael@0 1815
michael@0 1816 /*
michael@0 1817 * helper function to reconcile a single trust entry.
michael@0 1818 * Identify which trust entry we want to keep.
michael@0 1819 * If we don't need to do anything (the records are already equal).
michael@0 1820 * return SFTKDB_DO_NOTHING.
michael@0 1821 * If we want to use the source version,
michael@0 1822 * return SFTKDB_MODIFY_OBJECT
michael@0 1823 * If we want to use the target version,
michael@0 1824 * return SFTKDB_DROP_ATTRIBUTE
michael@0 1825 *
michael@0 1826 * In the end the caller will remove any attributes in the source
michael@0 1827 * template when SFTKDB_DROP_ATTRIBUTE is specified, then use do a
michael@0 1828 * set attributes with that template on the target if we received
michael@0 1829 * any SFTKDB_MODIFY_OBJECT returns.
michael@0 1830 */
michael@0 1831 sftkdbUpdateStatus
michael@0 1832 sftkdb_reconcileTrustEntry(PLArenaPool *arena, CK_ATTRIBUTE *target,
michael@0 1833 CK_ATTRIBUTE *source)
michael@0 1834 {
michael@0 1835 CK_ULONG targetTrust = sftkdb_getULongFromTemplate(target->type,
michael@0 1836 target, 1);
michael@0 1837 CK_ULONG sourceTrust = sftkdb_getULongFromTemplate(target->type,
michael@0 1838 source, 1);
michael@0 1839
michael@0 1840 /*
michael@0 1841 * try to pick the best solution between the source and the
michael@0 1842 * target. Update the source template if we want the target value
michael@0 1843 * to win out. Prefer cases where we don't actually update the
michael@0 1844 * trust entry.
michael@0 1845 */
michael@0 1846
michael@0 1847 /* they are the same, everything is already kosher */
michael@0 1848 if (targetTrust == sourceTrust) {
michael@0 1849 return SFTKDB_DO_NOTHING;
michael@0 1850 }
michael@0 1851
michael@0 1852 /* handle the case where the source Trust attribute may be a bit
michael@0 1853 * flakey */
michael@0 1854 if (sourceTrust == (CK_ULONG)-1) {
michael@0 1855 /*
michael@0 1856 * The source Trust is invalid. We know that the target Trust
michael@0 1857 * must be valid here, otherwise the above
michael@0 1858 * targetTrust == sourceTrust check would have succeeded.
michael@0 1859 */
michael@0 1860 return SFTKDB_DROP_ATTRIBUTE;
michael@0 1861 }
michael@0 1862
michael@0 1863 /* target is invalid, use the source's idea of the trust value */
michael@0 1864 if (targetTrust == (CK_ULONG)-1) {
michael@0 1865 /* overwriting the target in this case is OK */
michael@0 1866 return SFTKDB_MODIFY_OBJECT;
michael@0 1867 }
michael@0 1868
michael@0 1869 /* at this point we know that both attributes exist and have the
michael@0 1870 * appropriate length (SDB_ULONG_SIZE). We no longer need to check
michael@0 1871 * ulValueLen for either attribute.
michael@0 1872 */
michael@0 1873 if (sourceTrust == CKT_NSS_TRUST_UNKNOWN) {
michael@0 1874 return SFTKDB_DROP_ATTRIBUTE;
michael@0 1875 }
michael@0 1876
michael@0 1877 /* target has no idea, use the source's idea of the trust value */
michael@0 1878 if (targetTrust == CKT_NSS_TRUST_UNKNOWN) {
michael@0 1879 /* overwriting the target in this case is OK */
michael@0 1880 return SFTKDB_MODIFY_OBJECT;
michael@0 1881 }
michael@0 1882
michael@0 1883 /* so both the target and the source have some idea of what this
michael@0 1884 * trust attribute should be, and neither agree exactly.
michael@0 1885 * At this point, we prefer 'hard' attributes over 'soft' ones.
michael@0 1886 * 'hard' ones are CKT_NSS_TRUSTED, CKT_NSS_TRUSTED_DELEGATOR, and
michael@0 1887 * CKT_NSS_NOT_TRUTED. Soft ones are ones which don't change the
michael@0 1888 * actual trust of the cert (CKT_MUST_VERIFY_TRUST,
michael@0 1889 * CKT_NSS_VALID_DELEGATOR).
michael@0 1890 */
michael@0 1891 if ((sourceTrust == CKT_NSS_MUST_VERIFY_TRUST)
michael@0 1892 || (sourceTrust == CKT_NSS_VALID_DELEGATOR)) {
michael@0 1893 return SFTKDB_DROP_ATTRIBUTE;
michael@0 1894 }
michael@0 1895 if ((targetTrust == CKT_NSS_MUST_VERIFY_TRUST)
michael@0 1896 || (targetTrust == CKT_NSS_VALID_DELEGATOR)) {
michael@0 1897 /* again, overwriting the target in this case is OK */
michael@0 1898 return SFTKDB_MODIFY_OBJECT;
michael@0 1899 }
michael@0 1900
michael@0 1901 /* both have hard attributes, we have a conflict, let the target win. */
michael@0 1902 return SFTKDB_DROP_ATTRIBUTE;
michael@0 1903 }
michael@0 1904
michael@0 1905 const CK_ATTRIBUTE_TYPE sftkdb_trustList[] =
michael@0 1906 { CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH,
michael@0 1907 CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION,
michael@0 1908 CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER,
michael@0 1909 CKA_TRUST_TIME_STAMPING };
michael@0 1910
michael@0 1911 #define SFTK_TRUST_TEMPLATE_COUNT \
michael@0 1912 (sizeof(sftkdb_trustList)/sizeof(sftkdb_trustList[0]))
michael@0 1913 /*
michael@0 1914 * Run through the list of known trust types, and reconcile each trust
michael@0 1915 * entry one by one. Keep track of we really need to write out the source
michael@0 1916 * trust object (overwriting the existing one).
michael@0 1917 */
michael@0 1918 static sftkdbUpdateStatus
michael@0 1919 sftkdb_reconcileTrust(PLArenaPool *arena, SDB *db, CK_OBJECT_HANDLE id,
michael@0 1920 CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
michael@0 1921 {
michael@0 1922 CK_ATTRIBUTE trustTemplate[SFTK_TRUST_TEMPLATE_COUNT];
michael@0 1923 unsigned char trustData[SFTK_TRUST_TEMPLATE_COUNT*SDB_ULONG_SIZE];
michael@0 1924 sftkdbUpdateStatus update = SFTKDB_DO_NOTHING;
michael@0 1925 CK_ULONG i;
michael@0 1926 CK_RV crv;
michael@0 1927
michael@0 1928
michael@0 1929 for (i=0; i < SFTK_TRUST_TEMPLATE_COUNT; i++) {
michael@0 1930 trustTemplate[i].type = sftkdb_trustList[i];
michael@0 1931 trustTemplate[i].pValue = &trustData[i*SDB_ULONG_SIZE];
michael@0 1932 trustTemplate[i].ulValueLen = SDB_ULONG_SIZE;
michael@0 1933 }
michael@0 1934 crv = (*db->sdb_GetAttributeValue)(db, id,
michael@0 1935 trustTemplate, SFTK_TRUST_TEMPLATE_COUNT);
michael@0 1936 if ((crv != CKR_OK) && (crv != CKR_ATTRIBUTE_TYPE_INVALID)) {
michael@0 1937 /* target trust has some problems, update it */
michael@0 1938 update = SFTKDB_MODIFY_OBJECT;
michael@0 1939 goto done;
michael@0 1940 }
michael@0 1941
michael@0 1942 for (i=0; i < SFTK_TRUST_TEMPLATE_COUNT; i++) {
michael@0 1943 CK_ATTRIBUTE *attr = sftkdb_getAttributeFromTemplate(
michael@0 1944 trustTemplate[i].type, ptemplate, *plen);
michael@0 1945 sftkdbUpdateStatus status;
michael@0 1946
michael@0 1947
michael@0 1948 /* if target trust value doesn't exist, nothing to merge */
michael@0 1949 if (trustTemplate[i].ulValueLen == (CK_ULONG)-1) {
michael@0 1950 /* if the source exists, then we want the source entry,
michael@0 1951 * go ahead and update */
michael@0 1952 if (attr && attr->ulValueLen != (CK_ULONG)-1) {
michael@0 1953 update = SFTKDB_MODIFY_OBJECT;
michael@0 1954 }
michael@0 1955 continue;
michael@0 1956 }
michael@0 1957
michael@0 1958 /*
michael@0 1959 * the source doesn't have the attribute, go to the next attribute
michael@0 1960 */
michael@0 1961 if (attr == NULL) {
michael@0 1962 continue;
michael@0 1963
michael@0 1964 }
michael@0 1965 status = sftkdb_reconcileTrustEntry(arena, &trustTemplate[i], attr);
michael@0 1966 if (status == SFTKDB_MODIFY_OBJECT) {
michael@0 1967 update = SFTKDB_MODIFY_OBJECT;
michael@0 1968 } else if (status == SFTKDB_DROP_ATTRIBUTE) {
michael@0 1969 /* drop the source copy of the attribute, we are going with
michael@0 1970 * the target's version */
michael@0 1971 sftkdb_dropAttribute(attr, ptemplate, plen);
michael@0 1972 }
michael@0 1973 }
michael@0 1974
michael@0 1975 /* finally manage stepup */
michael@0 1976 if (update == SFTKDB_MODIFY_OBJECT) {
michael@0 1977 CK_BBOOL stepUpBool = CK_FALSE;
michael@0 1978 /* if we are going to write from the source, make sure we don't
michael@0 1979 * overwrite the stepup bit if it's on*/
michael@0 1980 trustTemplate[0].type = CKA_TRUST_STEP_UP_APPROVED;
michael@0 1981 trustTemplate[0].pValue = &stepUpBool;
michael@0 1982 trustTemplate[0].ulValueLen = sizeof(stepUpBool);
michael@0 1983 crv = (*db->sdb_GetAttributeValue)(db, id, trustTemplate, 1);
michael@0 1984 if ((crv == CKR_OK) && (stepUpBool == CK_TRUE)) {
michael@0 1985 sftkdb_dropAttribute(trustTemplate, ptemplate, plen);
michael@0 1986 }
michael@0 1987 } else {
michael@0 1988 /* we currently aren't going to update. If the source stepup bit is
michael@0 1989 * on however, do an update so the target gets it as well */
michael@0 1990 CK_ATTRIBUTE *attr;
michael@0 1991
michael@0 1992 attr = sftkdb_getAttributeFromTemplate(CKA_TRUST_STEP_UP_APPROVED,
michael@0 1993 ptemplate, *plen);
michael@0 1994 if (attr && (attr->ulValueLen == sizeof(CK_BBOOL)) &&
michael@0 1995 (*(CK_BBOOL *)(attr->pValue) == CK_TRUE)) {
michael@0 1996 update = SFTKDB_MODIFY_OBJECT;
michael@0 1997 }
michael@0 1998 }
michael@0 1999
michael@0 2000 done:
michael@0 2001 return update;
michael@0 2002 }
michael@0 2003
michael@0 2004 static sftkdbUpdateStatus
michael@0 2005 sftkdb_handleIDAndName(PLArenaPool *arena, SDB *db, CK_OBJECT_HANDLE id,
michael@0 2006 CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
michael@0 2007 {
michael@0 2008 sftkdbUpdateStatus update = SFTKDB_DO_NOTHING;
michael@0 2009 CK_ATTRIBUTE *attr1, *attr2;
michael@0 2010 CK_ATTRIBUTE ttemplate[2] = {
michael@0 2011 {CKA_ID, NULL, 0},
michael@0 2012 {CKA_LABEL, NULL, 0}
michael@0 2013 };
michael@0 2014 CK_RV crv;
michael@0 2015
michael@0 2016 attr1 = sftkdb_getAttributeFromTemplate(CKA_LABEL, ptemplate, *plen);
michael@0 2017 attr2 = sftkdb_getAttributeFromTemplate(CKA_ID, ptemplate, *plen);
michael@0 2018
michael@0 2019 /* if the source has neither an id nor label, don't bother updating */
michael@0 2020 if ( (!attr1 || attr1->ulValueLen == 0) &&
michael@0 2021 (! attr2 || attr2->ulValueLen == 0) ) {
michael@0 2022 return SFTKDB_DO_NOTHING;
michael@0 2023 }
michael@0 2024
michael@0 2025 /* the source has either an id or a label, see what the target has */
michael@0 2026 crv = (*db->sdb_GetAttributeValue)(db, id, ttemplate, 2);
michael@0 2027
michael@0 2028 /* if the target has neither, update from the source */
michael@0 2029 if ( ((ttemplate[0].ulValueLen == 0) ||
michael@0 2030 (ttemplate[0].ulValueLen == (CK_ULONG)-1)) &&
michael@0 2031 ((ttemplate[1].ulValueLen == 0) ||
michael@0 2032 (ttemplate[1].ulValueLen == (CK_ULONG)-1)) ) {
michael@0 2033 return SFTKDB_MODIFY_OBJECT;
michael@0 2034 }
michael@0 2035
michael@0 2036 /* check the CKA_ID */
michael@0 2037 if ((ttemplate[0].ulValueLen != 0) &&
michael@0 2038 (ttemplate[0].ulValueLen != (CK_ULONG)-1)) {
michael@0 2039 /* we have a CKA_ID in the target, don't overwrite
michael@0 2040 * the target with an empty CKA_ID from the source*/
michael@0 2041 if (attr1 && attr1->ulValueLen == 0) {
michael@0 2042 sftkdb_dropAttribute(attr1, ptemplate, plen);
michael@0 2043 }
michael@0 2044 } else if (attr1 && attr1->ulValueLen != 0) {
michael@0 2045 /* source has a CKA_ID, but the target doesn't, update the target */
michael@0 2046 update = SFTKDB_MODIFY_OBJECT;
michael@0 2047 }
michael@0 2048
michael@0 2049
michael@0 2050 /* check the nickname */
michael@0 2051 if ((ttemplate[1].ulValueLen != 0) &&
michael@0 2052 (ttemplate[1].ulValueLen != (CK_ULONG)-1)) {
michael@0 2053
michael@0 2054 /* we have a nickname in the target, and we don't have to update
michael@0 2055 * the CKA_ID. We are done. NOTE: if we add addition attributes
michael@0 2056 * in this check, this shortcut can only go on the last of them. */
michael@0 2057 if (update == SFTKDB_DO_NOTHING) {
michael@0 2058 return update;
michael@0 2059 }
michael@0 2060 /* we have a nickname in the target, don't overwrite
michael@0 2061 * the target with an empty nickname from the source */
michael@0 2062 if (attr2 && attr2->ulValueLen == 0) {
michael@0 2063 sftkdb_dropAttribute(attr2, ptemplate, plen);
michael@0 2064 }
michael@0 2065 } else if (attr2 && attr2->ulValueLen != 0) {
michael@0 2066 /* source has a nickname, but the target doesn't, update the target */
michael@0 2067 update = SFTKDB_MODIFY_OBJECT;
michael@0 2068 }
michael@0 2069
michael@0 2070 return update;
michael@0 2071 }
michael@0 2072
michael@0 2073
michael@0 2074
michael@0 2075 /*
michael@0 2076 * This function updates the template before we write the object out.
michael@0 2077 *
michael@0 2078 * If we are going to skip updating this object, return PR_FALSE.
michael@0 2079 * If it should be updated we return PR_TRUE.
michael@0 2080 * To help readability, these have been defined
michael@0 2081 * as SFTK_DONT_UPDATE and SFTK_UPDATE respectively.
michael@0 2082 */
michael@0 2083 static PRBool
michael@0 2084 sftkdb_updateObjectTemplate(PLArenaPool *arena, SDB *db,
michael@0 2085 CK_OBJECT_CLASS objectType,
michael@0 2086 CK_ATTRIBUTE *ptemplate, CK_ULONG *plen,
michael@0 2087 CK_OBJECT_HANDLE *targetID)
michael@0 2088 {
michael@0 2089 PRBool done; /* should we repeat the loop? */
michael@0 2090 CK_OBJECT_HANDLE id;
michael@0 2091 CK_RV crv = CKR_OK;
michael@0 2092
michael@0 2093 do {
michael@0 2094 crv = sftkdb_checkConflicts(db, objectType, ptemplate,
michael@0 2095 *plen, CK_INVALID_HANDLE);
michael@0 2096 if (crv != CKR_ATTRIBUTE_VALUE_INVALID) {
michael@0 2097 break;
michael@0 2098 }
michael@0 2099 crv = sftkdb_resolveConflicts(arena, objectType, ptemplate, plen);
michael@0 2100 } while (crv == CKR_OK);
michael@0 2101
michael@0 2102 if (crv != CKR_OK) {
michael@0 2103 return SFTKDB_DO_NOTHING;
michael@0 2104 }
michael@0 2105
michael@0 2106 do {
michael@0 2107 done = PR_TRUE;
michael@0 2108 crv = sftkdb_lookupObject(db, objectType, &id, ptemplate, *plen);
michael@0 2109 if (crv != CKR_OK) {
michael@0 2110 return SFTKDB_DO_NOTHING;
michael@0 2111 }
michael@0 2112
michael@0 2113 /* This object already exists, merge it, don't update */
michael@0 2114 if (id != CK_INVALID_HANDLE) {
michael@0 2115 CK_ATTRIBUTE *attr = NULL;
michael@0 2116 /* special post processing for attributes */
michael@0 2117 switch (objectType) {
michael@0 2118 case CKO_CERTIFICATE:
michael@0 2119 case CKO_PUBLIC_KEY:
michael@0 2120 case CKO_PRIVATE_KEY:
michael@0 2121 /* update target's CKA_ID and labels if they don't already
michael@0 2122 * exist */
michael@0 2123 *targetID = id;
michael@0 2124 return sftkdb_handleIDAndName(arena, db, id, ptemplate, plen);
michael@0 2125 case CKO_NSS_TRUST:
michael@0 2126 /* if we have conflicting trust object types,
michael@0 2127 * we need to reconcile them */
michael@0 2128 *targetID = id;
michael@0 2129 return sftkdb_reconcileTrust(arena, db, id, ptemplate, plen);
michael@0 2130 case CKO_SECRET_KEY:
michael@0 2131 /* secret keys in the old database are all sdr keys,
michael@0 2132 * unfortunately they all appear to have the same CKA_ID,
michael@0 2133 * even though they are truly different keys, so we always
michael@0 2134 * want to update these keys, but we need to
michael@0 2135 * give them a new CKA_ID */
michael@0 2136 /* NOTE: this changes ptemplate */
michael@0 2137 attr = sftkdb_getAttributeFromTemplate(CKA_ID,ptemplate,*plen);
michael@0 2138 crv = attr ? sftkdb_incrementCKAID(arena, attr)
michael@0 2139 : CKR_HOST_MEMORY;
michael@0 2140 /* in the extremely rare event that we needed memory and
michael@0 2141 * couldn't get it, just drop the key */
michael@0 2142 if (crv != CKR_OK) {
michael@0 2143 return SFTKDB_DO_NOTHING;
michael@0 2144 }
michael@0 2145 done = PR_FALSE; /* repeat this find loop */
michael@0 2146 break;
michael@0 2147 default:
michael@0 2148 /* for all other objects, if we found the equivalent object,
michael@0 2149 * don't update it */
michael@0 2150 return SFTKDB_DO_NOTHING;
michael@0 2151 }
michael@0 2152 }
michael@0 2153 } while (!done);
michael@0 2154
michael@0 2155 /* this object doesn't exist, update it */
michael@0 2156 return SFTKDB_ADD_OBJECT;
michael@0 2157 }
michael@0 2158
michael@0 2159
michael@0 2160 #define MAX_ATTRIBUTES 500
michael@0 2161 static CK_RV
michael@0 2162 sftkdb_mergeObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id,
michael@0 2163 SECItem *key)
michael@0 2164 {
michael@0 2165 CK_ATTRIBUTE template[MAX_ATTRIBUTES];
michael@0 2166 CK_ATTRIBUTE *ptemplate;
michael@0 2167 CK_ULONG max_attributes = MAX_ATTRIBUTES;
michael@0 2168 CK_OBJECT_CLASS objectType;
michael@0 2169 SDB *source = handle->update;
michael@0 2170 SDB *target = handle->db;
michael@0 2171 int i;
michael@0 2172 CK_RV crv;
michael@0 2173 PLArenaPool *arena = NULL;
michael@0 2174
michael@0 2175 arena = PORT_NewArena(256);
michael@0 2176 if (arena == NULL) {
michael@0 2177 return CKR_HOST_MEMORY;
michael@0 2178 }
michael@0 2179
michael@0 2180 ptemplate = &template[0];
michael@0 2181 id &= SFTK_OBJ_ID_MASK;
michael@0 2182 crv = sftkdb_GetObjectTemplate(source, id, ptemplate, &max_attributes);
michael@0 2183 if (crv == CKR_BUFFER_TOO_SMALL) {
michael@0 2184 ptemplate = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, max_attributes);
michael@0 2185 if (ptemplate == NULL) {
michael@0 2186 crv = CKR_HOST_MEMORY;
michael@0 2187 } else {
michael@0 2188 crv = sftkdb_GetObjectTemplate(source, id,
michael@0 2189 ptemplate, &max_attributes);
michael@0 2190 }
michael@0 2191 }
michael@0 2192 if (crv != CKR_OK) {
michael@0 2193 goto loser;
michael@0 2194 }
michael@0 2195
michael@0 2196 for (i=0; i < max_attributes; i++) {
michael@0 2197 ptemplate[i].pValue = PORT_ArenaAlloc(arena,ptemplate[i].ulValueLen);
michael@0 2198 if (ptemplate[i].pValue == NULL) {
michael@0 2199 crv = CKR_HOST_MEMORY;
michael@0 2200 goto loser;
michael@0 2201 }
michael@0 2202 }
michael@0 2203 crv = (*source->sdb_GetAttributeValue)(source, id,
michael@0 2204 ptemplate, max_attributes);
michael@0 2205 if (crv != CKR_OK) {
michael@0 2206 goto loser;
michael@0 2207 }
michael@0 2208
michael@0 2209 objectType = sftkdb_getULongFromTemplate(CKA_CLASS, ptemplate,
michael@0 2210 max_attributes);
michael@0 2211
michael@0 2212 /*
michael@0 2213 * Update Object updates the object template if necessary then returns
michael@0 2214 * whether or not we need to actually write the object out to our target
michael@0 2215 * database.
michael@0 2216 */
michael@0 2217 if (!handle->updateID) {
michael@0 2218 crv = sftkdb_CreateObject(arena, handle, target, &id,
michael@0 2219 ptemplate, max_attributes);
michael@0 2220 } else {
michael@0 2221 sftkdbUpdateStatus update_status;
michael@0 2222 update_status = sftkdb_updateObjectTemplate(arena, target,
michael@0 2223 objectType, ptemplate, &max_attributes, &id);
michael@0 2224 switch (update_status) {
michael@0 2225 case SFTKDB_ADD_OBJECT:
michael@0 2226 crv = sftkdb_CreateObject(arena, handle, target, &id,
michael@0 2227 ptemplate, max_attributes);
michael@0 2228 break;
michael@0 2229 case SFTKDB_MODIFY_OBJECT:
michael@0 2230 crv = sftkdb_setAttributeValue(arena, handle, target,
michael@0 2231 id, ptemplate, max_attributes);
michael@0 2232 break;
michael@0 2233 case SFTKDB_DO_NOTHING:
michael@0 2234 case SFTKDB_DROP_ATTRIBUTE:
michael@0 2235 break;
michael@0 2236 }
michael@0 2237 }
michael@0 2238
michael@0 2239 loser:
michael@0 2240 if (arena) {
michael@0 2241 PORT_FreeArena(arena,PR_TRUE);
michael@0 2242 }
michael@0 2243 return crv;
michael@0 2244 }
michael@0 2245
michael@0 2246
michael@0 2247 #define MAX_IDS 10
michael@0 2248 /*
michael@0 2249 * update a new database from an old one, now that we have the key
michael@0 2250 */
michael@0 2251 CK_RV
michael@0 2252 sftkdb_Update(SFTKDBHandle *handle, SECItem *key)
michael@0 2253 {
michael@0 2254 SDBFind *find = NULL;
michael@0 2255 CK_ULONG idCount = MAX_IDS;
michael@0 2256 CK_OBJECT_HANDLE ids[MAX_IDS];
michael@0 2257 SECItem *updatePasswordKey = NULL;
michael@0 2258 CK_RV crv, crv2;
michael@0 2259 PRBool inTransaction = PR_FALSE;
michael@0 2260 int i;
michael@0 2261
michael@0 2262 if (handle == NULL) {
michael@0 2263 return CKR_OK;
michael@0 2264 }
michael@0 2265 if (handle->update == NULL) {
michael@0 2266 return CKR_OK;
michael@0 2267 }
michael@0 2268
michael@0 2269 /*
michael@0 2270 * put the whole update under a transaction. This allows us to handle
michael@0 2271 * any possible race conditions between with the updateID check.
michael@0 2272 */
michael@0 2273 crv = (*handle->db->sdb_Begin)(handle->db);
michael@0 2274 if (crv != CKR_OK) {
michael@0 2275 goto loser;
michael@0 2276 }
michael@0 2277 inTransaction = PR_TRUE;
michael@0 2278
michael@0 2279 /* some one else has already updated this db */
michael@0 2280 if (sftkdb_hasUpdate(sftkdb_TypeString(handle),
michael@0 2281 handle->db, handle->updateID)) {
michael@0 2282 crv = CKR_OK;
michael@0 2283 goto done;
michael@0 2284 }
michael@0 2285
michael@0 2286 updatePasswordKey = sftkdb_GetUpdatePasswordKey(handle);
michael@0 2287 if (updatePasswordKey) {
michael@0 2288 /* pass the source DB key to the legacy code,
michael@0 2289 * so it can decrypt things */
michael@0 2290 handle->oldKey = updatePasswordKey;
michael@0 2291 }
michael@0 2292
michael@0 2293 /* find all the objects */
michael@0 2294 crv = sftkdb_FindObjectsInit(handle, NULL, 0, &find);
michael@0 2295
michael@0 2296 if (crv != CKR_OK) {
michael@0 2297 goto loser;
michael@0 2298 }
michael@0 2299 while ((crv == CKR_OK) && (idCount == MAX_IDS)) {
michael@0 2300 crv = sftkdb_FindObjects(handle, find, ids, MAX_IDS, &idCount);
michael@0 2301 for (i=0; (crv == CKR_OK) && (i < idCount); i++) {
michael@0 2302 crv = sftkdb_mergeObject(handle, ids[i], key);
michael@0 2303 }
michael@0 2304 }
michael@0 2305 crv2 = sftkdb_FindObjectsFinal(handle, find);
michael@0 2306 if (crv == CKR_OK) crv = crv2;
michael@0 2307
michael@0 2308 loser:
michael@0 2309 /* no longer need the old key value */
michael@0 2310 handle->oldKey = NULL;
michael@0 2311
michael@0 2312 /* update the password - even if we didn't update objects */
michael@0 2313 if (handle->type == SFTK_KEYDB_TYPE) {
michael@0 2314 SECItem item1, item2;
michael@0 2315 unsigned char data1[SDB_MAX_META_DATA_LEN];
michael@0 2316 unsigned char data2[SDB_MAX_META_DATA_LEN];
michael@0 2317
michael@0 2318 item1.data = data1;
michael@0 2319 item1.len = sizeof(data1);
michael@0 2320 item2.data = data2;
michael@0 2321 item2.len = sizeof(data2);
michael@0 2322
michael@0 2323 /* if the target db already has a password, skip this. */
michael@0 2324 crv = (*handle->db->sdb_GetMetaData)(handle->db, "password",
michael@0 2325 &item1, &item2);
michael@0 2326 if (crv == CKR_OK) {
michael@0 2327 goto done;
michael@0 2328 }
michael@0 2329
michael@0 2330
michael@0 2331 /* nope, update it from the source */
michael@0 2332 crv = (*handle->update->sdb_GetMetaData)(handle->update, "password",
michael@0 2333 &item1, &item2);
michael@0 2334 if (crv != CKR_OK) {
michael@0 2335 goto done;
michael@0 2336 }
michael@0 2337 crv = (*handle->db->sdb_PutMetaData)(handle->db, "password", &item1,
michael@0 2338 &item2);
michael@0 2339 if (crv != CKR_OK) {
michael@0 2340 goto done;
michael@0 2341 }
michael@0 2342 }
michael@0 2343
michael@0 2344 done:
michael@0 2345 /* finally mark this up to date db up to date */
michael@0 2346 /* some one else has already updated this db */
michael@0 2347 if (crv == CKR_OK) {
michael@0 2348 crv = sftkdb_putUpdate(sftkdb_TypeString(handle),
michael@0 2349 handle->db, handle->updateID);
michael@0 2350 }
michael@0 2351
michael@0 2352 if (inTransaction) {
michael@0 2353 if (crv == CKR_OK) {
michael@0 2354 crv = (*handle->db->sdb_Commit)(handle->db);
michael@0 2355 } else {
michael@0 2356 (*handle->db->sdb_Abort)(handle->db);
michael@0 2357 }
michael@0 2358 }
michael@0 2359 if (handle->update) {
michael@0 2360 (*handle->update->sdb_Close)(handle->update);
michael@0 2361 handle->update = NULL;
michael@0 2362 }
michael@0 2363 if (handle->updateID) {
michael@0 2364 PORT_Free(handle->updateID);
michael@0 2365 handle->updateID = NULL;
michael@0 2366 }
michael@0 2367 sftkdb_FreeUpdatePasswordKey(handle);
michael@0 2368 if (updatePasswordKey) {
michael@0 2369 SECITEM_ZfreeItem(updatePasswordKey, PR_TRUE);
michael@0 2370 }
michael@0 2371 handle->updateDBIsInit = PR_FALSE;
michael@0 2372 return crv;
michael@0 2373 }
michael@0 2374
michael@0 2375 /******************************************************************
michael@0 2376 * DB handle managing functions.
michael@0 2377 *
michael@0 2378 * These functions are called by softoken to initialize, acquire,
michael@0 2379 * and release database handles.
michael@0 2380 */
michael@0 2381
michael@0 2382 const char *
michael@0 2383 sftkdb_GetUpdateID(SFTKDBHandle *handle)
michael@0 2384 {
michael@0 2385 return handle->updateID;
michael@0 2386 }
michael@0 2387
michael@0 2388 /* release a database handle */
michael@0 2389 void
michael@0 2390 sftk_freeDB(SFTKDBHandle *handle)
michael@0 2391 {
michael@0 2392 PRInt32 ref;
michael@0 2393
michael@0 2394 if (!handle) return;
michael@0 2395 ref = PR_ATOMIC_DECREMENT(&handle->ref);
michael@0 2396 if (ref == 0) {
michael@0 2397 sftkdb_CloseDB(handle);
michael@0 2398 }
michael@0 2399 return;
michael@0 2400 }
michael@0 2401
michael@0 2402
michael@0 2403 /*
michael@0 2404 * acquire a database handle for a certificate db
michael@0 2405 * (database for public objects)
michael@0 2406 */
michael@0 2407 SFTKDBHandle *
michael@0 2408 sftk_getCertDB(SFTKSlot *slot)
michael@0 2409 {
michael@0 2410 SFTKDBHandle *dbHandle;
michael@0 2411
michael@0 2412 PZ_Lock(slot->slotLock);
michael@0 2413 dbHandle = slot->certDB;
michael@0 2414 if (dbHandle) {
michael@0 2415 PR_ATOMIC_INCREMENT(&dbHandle->ref);
michael@0 2416 }
michael@0 2417 PZ_Unlock(slot->slotLock);
michael@0 2418 return dbHandle;
michael@0 2419 }
michael@0 2420
michael@0 2421 /*
michael@0 2422 * acquire a database handle for a key database
michael@0 2423 * (database for private objects)
michael@0 2424 */
michael@0 2425 SFTKDBHandle *
michael@0 2426 sftk_getKeyDB(SFTKSlot *slot)
michael@0 2427 {
michael@0 2428 SFTKDBHandle *dbHandle;
michael@0 2429
michael@0 2430 SKIP_AFTER_FORK(PZ_Lock(slot->slotLock));
michael@0 2431 dbHandle = slot->keyDB;
michael@0 2432 if (dbHandle) {
michael@0 2433 PR_ATOMIC_INCREMENT(&dbHandle->ref);
michael@0 2434 }
michael@0 2435 SKIP_AFTER_FORK(PZ_Unlock(slot->slotLock));
michael@0 2436 return dbHandle;
michael@0 2437 }
michael@0 2438
michael@0 2439 /*
michael@0 2440 * acquire the database for a specific object. NOTE: objectID must point
michael@0 2441 * to a Token object!
michael@0 2442 */
michael@0 2443 SFTKDBHandle *
michael@0 2444 sftk_getDBForTokenObject(SFTKSlot *slot, CK_OBJECT_HANDLE objectID)
michael@0 2445 {
michael@0 2446 SFTKDBHandle *dbHandle;
michael@0 2447
michael@0 2448 PZ_Lock(slot->slotLock);
michael@0 2449 dbHandle = objectID & SFTK_KEYDB_TYPE ? slot->keyDB : slot->certDB;
michael@0 2450 if (dbHandle) {
michael@0 2451 PR_ATOMIC_INCREMENT(&dbHandle->ref);
michael@0 2452 }
michael@0 2453 PZ_Unlock(slot->slotLock);
michael@0 2454 return dbHandle;
michael@0 2455 }
michael@0 2456
michael@0 2457 /*
michael@0 2458 * initialize a new database handle
michael@0 2459 */
michael@0 2460 static SFTKDBHandle *
michael@0 2461 sftk_NewDBHandle(SDB *sdb, int type)
michael@0 2462 {
michael@0 2463 SFTKDBHandle *handle = PORT_New(SFTKDBHandle);
michael@0 2464 handle->ref = 1;
michael@0 2465 handle->db = sdb;
michael@0 2466 handle->update = NULL;
michael@0 2467 handle->peerDB = NULL;
michael@0 2468 handle->newKey = NULL;
michael@0 2469 handle->oldKey = NULL;
michael@0 2470 handle->updatePasswordKey = NULL;
michael@0 2471 handle->updateID = NULL;
michael@0 2472 handle->type = type;
michael@0 2473 handle->passwordKey.data = NULL;
michael@0 2474 handle->passwordKey.len = 0;
michael@0 2475 handle->passwordLock = NULL;
michael@0 2476 if (type == SFTK_KEYDB_TYPE) {
michael@0 2477 handle->passwordLock = PZ_NewLock(nssILockAttribute);
michael@0 2478 }
michael@0 2479 sdb->app_private = handle;
michael@0 2480 return handle;
michael@0 2481 }
michael@0 2482
michael@0 2483 /*
michael@0 2484 * reset the key database to it's uninitialized state. This call
michael@0 2485 * will clear all the key entried.
michael@0 2486 */
michael@0 2487 SECStatus
michael@0 2488 sftkdb_ResetKeyDB(SFTKDBHandle *handle)
michael@0 2489 {
michael@0 2490 CK_RV crv;
michael@0 2491
michael@0 2492 /* only rest the key db */
michael@0 2493 if (handle->type != SFTK_KEYDB_TYPE) {
michael@0 2494 return SECFailure;
michael@0 2495 }
michael@0 2496 crv = sftkdb_ResetDB(handle);
michael@0 2497 if (crv != CKR_OK) {
michael@0 2498 /* set error */
michael@0 2499 return SECFailure;
michael@0 2500 }
michael@0 2501 return SECSuccess;
michael@0 2502 }
michael@0 2503
michael@0 2504 static PRBool
michael@0 2505 sftk_oldVersionExists(const char *dir, int version)
michael@0 2506 {
michael@0 2507 int i;
michael@0 2508 PRStatus exists = PR_FAILURE;
michael@0 2509 char *file = NULL;
michael@0 2510
michael@0 2511 for (i=version; i > 1 ; i--) {
michael@0 2512 file = PR_smprintf("%s%d.db",dir,i);
michael@0 2513 if (file == NULL) {
michael@0 2514 continue;
michael@0 2515 }
michael@0 2516 exists = PR_Access(file, PR_ACCESS_EXISTS);
michael@0 2517 PR_smprintf_free(file);
michael@0 2518 if (exists == PR_SUCCESS) {
michael@0 2519 return PR_TRUE;
michael@0 2520 }
michael@0 2521 }
michael@0 2522 return PR_FALSE;
michael@0 2523 }
michael@0 2524
michael@0 2525 static PRBool
michael@0 2526 sftk_hasLegacyDB(const char *confdir, const char *certPrefix,
michael@0 2527 const char *keyPrefix, int certVersion, int keyVersion)
michael@0 2528 {
michael@0 2529 char *dir;
michael@0 2530 PRBool exists;
michael@0 2531
michael@0 2532 if (certPrefix == NULL) {
michael@0 2533 certPrefix = "";
michael@0 2534 }
michael@0 2535
michael@0 2536 if (keyPrefix == NULL) {
michael@0 2537 keyPrefix = "";
michael@0 2538 }
michael@0 2539
michael@0 2540 dir= PR_smprintf("%s/%scert", confdir, certPrefix);
michael@0 2541 if (dir == NULL) {
michael@0 2542 return PR_FALSE;
michael@0 2543 }
michael@0 2544
michael@0 2545 exists = sftk_oldVersionExists(dir, certVersion);
michael@0 2546 PR_smprintf_free(dir);
michael@0 2547 if (exists) {
michael@0 2548 return PR_TRUE;
michael@0 2549 }
michael@0 2550
michael@0 2551 dir= PR_smprintf("%s/%skey", confdir, keyPrefix);
michael@0 2552 if (dir == NULL) {
michael@0 2553 return PR_FALSE;
michael@0 2554 }
michael@0 2555
michael@0 2556 exists = sftk_oldVersionExists(dir, keyVersion);
michael@0 2557 PR_smprintf_free(dir);
michael@0 2558 return exists;
michael@0 2559 }
michael@0 2560
michael@0 2561 /*
michael@0 2562 * initialize certificate and key database handles as a pair.
michael@0 2563 *
michael@0 2564 * This function figures out what type of database we are opening and
michael@0 2565 * calls the appropriate low level function to open the database.
michael@0 2566 * It also figures out whether or not to setup up automatic update.
michael@0 2567 */
michael@0 2568 CK_RV
michael@0 2569 sftk_DBInit(const char *configdir, const char *certPrefix,
michael@0 2570 const char *keyPrefix, const char *updatedir,
michael@0 2571 const char *updCertPrefix, const char *updKeyPrefix,
michael@0 2572 const char *updateID, PRBool readOnly, PRBool noCertDB,
michael@0 2573 PRBool noKeyDB, PRBool forceOpen, PRBool isFIPS,
michael@0 2574 SFTKDBHandle **certDB, SFTKDBHandle **keyDB)
michael@0 2575 {
michael@0 2576 const char *confdir;
michael@0 2577 NSSDBType dbType = NSS_DB_TYPE_NONE;
michael@0 2578 char *appName = NULL;
michael@0 2579 SDB *keySDB, *certSDB;
michael@0 2580 CK_RV crv = CKR_OK;
michael@0 2581 int flags = SDB_RDONLY;
michael@0 2582 PRBool newInit = PR_FALSE;
michael@0 2583 PRBool needUpdate = PR_FALSE;
michael@0 2584
michael@0 2585 if (!readOnly) {
michael@0 2586 flags = SDB_CREATE;
michael@0 2587 }
michael@0 2588
michael@0 2589 *certDB = NULL;
michael@0 2590 *keyDB = NULL;
michael@0 2591
michael@0 2592 if (noKeyDB && noCertDB) {
michael@0 2593 return CKR_OK;
michael@0 2594 }
michael@0 2595 confdir = _NSSUTIL_EvaluateConfigDir(configdir, &dbType, &appName);
michael@0 2596
michael@0 2597 /*
michael@0 2598 * now initialize the appropriate database
michael@0 2599 */
michael@0 2600 switch (dbType) {
michael@0 2601 case NSS_DB_TYPE_LEGACY:
michael@0 2602 crv = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags,
michael@0 2603 isFIPS, noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB);
michael@0 2604 break;
michael@0 2605 case NSS_DB_TYPE_MULTIACCESS:
michael@0 2606 crv = sftkdbCall_open(configdir, certPrefix, keyPrefix, 8, 3, flags,
michael@0 2607 isFIPS, noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB);
michael@0 2608 break;
michael@0 2609 case NSS_DB_TYPE_SQL:
michael@0 2610 case NSS_DB_TYPE_EXTERN: /* SHOULD open a loadable db */
michael@0 2611 crv = s_open(confdir, certPrefix, keyPrefix, 9, 4, flags,
michael@0 2612 noCertDB? NULL : &certSDB, noKeyDB ? NULL : &keySDB, &newInit);
michael@0 2613
michael@0 2614 /*
michael@0 2615 * if we failed to open the DB's read only, use the old ones if
michael@0 2616 * the exists.
michael@0 2617 */
michael@0 2618 if (crv != CKR_OK) {
michael@0 2619 if ((flags == SDB_RDONLY) &&
michael@0 2620 sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
michael@0 2621 /* we have legacy databases, if we failed to open the new format
michael@0 2622 * DB's read only, just use the legacy ones */
michael@0 2623 crv = sftkdbCall_open(confdir, certPrefix,
michael@0 2624 keyPrefix, 8, 3, flags, isFIPS,
michael@0 2625 noCertDB? NULL : &certSDB, noKeyDB ? NULL : &keySDB);
michael@0 2626 }
michael@0 2627 /* Handle the database merge case.
michael@0 2628 *
michael@0 2629 * For the merge case, we need help from the application. Only
michael@0 2630 * the application knows where the old database is, and what unique
michael@0 2631 * identifier it has associated with it.
michael@0 2632 *
michael@0 2633 * If the client supplies these values, we use them to determine
michael@0 2634 * if we need to update.
michael@0 2635 */
michael@0 2636 } else if (
michael@0 2637 /* both update params have been supplied */
michael@0 2638 updatedir && *updatedir && updateID && *updateID
michael@0 2639 /* old dbs exist? */
michael@0 2640 && sftk_hasLegacyDB(updatedir, updCertPrefix, updKeyPrefix, 8, 3)
michael@0 2641 /* and they have not yet been updated? */
michael@0 2642 && ((noKeyDB || !sftkdb_hasUpdate("key", keySDB, updateID))
michael@0 2643 || (noCertDB || !sftkdb_hasUpdate("cert", certSDB, updateID)))) {
michael@0 2644 /* we need to update */
michael@0 2645 confdir = updatedir;
michael@0 2646 certPrefix = updCertPrefix;
michael@0 2647 keyPrefix = updKeyPrefix;
michael@0 2648 needUpdate = PR_TRUE;
michael@0 2649 } else if (newInit) {
michael@0 2650 /* if the new format DB was also a newly created DB, and we
michael@0 2651 * succeeded, then need to update that new database with data
michael@0 2652 * from the existing legacy DB */
michael@0 2653 if (sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
michael@0 2654 needUpdate = PR_TRUE;
michael@0 2655 }
michael@0 2656 }
michael@0 2657 break;
michael@0 2658 default:
michael@0 2659 crv = CKR_GENERAL_ERROR; /* can't happen, EvaluationConfigDir MUST
michael@0 2660 * return one of the types we already
michael@0 2661 * specified. */
michael@0 2662 }
michael@0 2663 if (crv != CKR_OK) {
michael@0 2664 goto done;
michael@0 2665 }
michael@0 2666 if (!noCertDB) {
michael@0 2667 *certDB = sftk_NewDBHandle(certSDB, SFTK_CERTDB_TYPE);
michael@0 2668 } else {
michael@0 2669 *certDB = NULL;
michael@0 2670 }
michael@0 2671 if (!noKeyDB) {
michael@0 2672 *keyDB = sftk_NewDBHandle(keySDB, SFTK_KEYDB_TYPE);
michael@0 2673 } else {
michael@0 2674 *keyDB = NULL;
michael@0 2675 }
michael@0 2676
michael@0 2677 /* link them together */
michael@0 2678 if (*certDB) {
michael@0 2679 (*certDB)->peerDB = *keyDB;
michael@0 2680 }
michael@0 2681 if (*keyDB) {
michael@0 2682 (*keyDB)->peerDB = *certDB;
michael@0 2683 }
michael@0 2684
michael@0 2685 /*
michael@0 2686 * if we need to update, open the legacy database and
michael@0 2687 * mark the handle as needing update.
michael@0 2688 */
michael@0 2689 if (needUpdate) {
michael@0 2690 SDB *updateCert = NULL;
michael@0 2691 SDB *updateKey = NULL;
michael@0 2692 CK_RV crv2;
michael@0 2693
michael@0 2694 crv2 = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags,
michael@0 2695 isFIPS, noCertDB ? NULL : &updateCert,
michael@0 2696 noKeyDB ? NULL : &updateKey);
michael@0 2697 if (crv2 == CKR_OK) {
michael@0 2698 if (*certDB) {
michael@0 2699 (*certDB)->update = updateCert;
michael@0 2700 (*certDB)->updateID = updateID && *updateID
michael@0 2701 ? PORT_Strdup(updateID) : NULL;
michael@0 2702 updateCert->app_private = (*certDB);
michael@0 2703 }
michael@0 2704 if (*keyDB) {
michael@0 2705 PRBool tokenRemoved = PR_FALSE;
michael@0 2706 (*keyDB)->update = updateKey;
michael@0 2707 (*keyDB)->updateID = updateID && *updateID ?
michael@0 2708 PORT_Strdup(updateID) : NULL;
michael@0 2709 updateKey->app_private = (*keyDB);
michael@0 2710 (*keyDB)->updateDBIsInit = PR_TRUE;
michael@0 2711 (*keyDB)->updateDBIsInit =
michael@0 2712 (sftkdb_HasPasswordSet(*keyDB) == SECSuccess) ?
michael@0 2713 PR_TRUE : PR_FALSE;
michael@0 2714 /* if the password on the key db is NULL, kick off our update
michael@0 2715 * chain of events */
michael@0 2716 sftkdb_CheckPassword((*keyDB), "", &tokenRemoved);
michael@0 2717 } else {
michael@0 2718 /* we don't have a key DB, update the certificate DB now */
michael@0 2719 sftkdb_Update(*certDB, NULL);
michael@0 2720 }
michael@0 2721 }
michael@0 2722 }
michael@0 2723 done:
michael@0 2724 if (appName) {
michael@0 2725 PORT_Free(appName);
michael@0 2726 }
michael@0 2727 return forceOpen ? CKR_OK : crv;
michael@0 2728 }
michael@0 2729
michael@0 2730 CK_RV
michael@0 2731 sftkdb_Shutdown(void)
michael@0 2732 {
michael@0 2733 s_shutdown();
michael@0 2734 sftkdbCall_Shutdown();
michael@0 2735 return CKR_OK;
michael@0 2736 }
michael@0 2737

mercurial