Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 |