diff options
Diffstat (limited to 'security/nss/lib/softoken/sdb.c')
-rw-r--r-- | security/nss/lib/softoken/sdb.c | 411 |
1 files changed, 333 insertions, 78 deletions
diff --git a/security/nss/lib/softoken/sdb.c b/security/nss/lib/softoken/sdb.c index c84476100..50625ab94 100644 --- a/security/nss/lib/softoken/sdb.c +++ b/security/nss/lib/softoken/sdb.c @@ -90,6 +90,49 @@ typedef enum { #define SDB_MAX_BUSY_RETRIES 10 /* + * known attributes + */ +static const CK_ATTRIBUTE_TYPE known_attributes[] = { + CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION, + CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER, + CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED, + CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL, + CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY, + CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE, + CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER, + CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE, + CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT, + CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT, + CKA_PUBLIC_KEY_INFO, CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS, + CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE, + CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE, + CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS, + CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS, + CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_HW_FEATURE_TYPE, + CKA_RESET_ON_INIT, CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, + CKA_RESOLUTION, CKA_CHAR_ROWS, CKA_CHAR_COLUMNS, CKA_COLOR, + CKA_BITS_PER_PIXEL, CKA_CHAR_SETS, CKA_ENCODING_METHODS, CKA_MIME_TYPES, + CKA_MECHANISM_TYPE, CKA_REQUIRED_CMS_ATTRIBUTES, + CKA_DEFAULT_CMS_ATTRIBUTES, CKA_SUPPORTED_CMS_ATTRIBUTES, + CKA_WRAP_TEMPLATE, CKA_UNWRAP_TEMPLATE, CKA_NSS_TRUST, CKA_NSS_URL, + CKA_NSS_EMAIL, CKA_NSS_SMIME_INFO, CKA_NSS_SMIME_TIMESTAMP, + CKA_NSS_PKCS8_SALT, CKA_NSS_PASSWORD_CHECK, CKA_NSS_EXPIRES, + CKA_NSS_KRL, CKA_NSS_PQG_COUNTER, CKA_NSS_PQG_SEED, + CKA_NSS_PQG_H, CKA_NSS_PQG_SEED_BITS, CKA_NSS_MODULE_SPEC, + CKA_NSS_OVERRIDE_EXTENSIONS, CKA_NSS_SERVER_DISTRUST_AFTER, + CKA_NSS_EMAIL_DISTRUST_AFTER, CKA_TRUST_DIGITAL_SIGNATURE, + CKA_TRUST_NON_REPUDIATION, CKA_TRUST_KEY_ENCIPHERMENT, + CKA_TRUST_DATA_ENCIPHERMENT, CKA_TRUST_KEY_AGREEMENT, + CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN, CKA_TRUST_SERVER_AUTH, + CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION, + CKA_TRUST_IPSEC_END_SYSTEM, CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, + CKA_TRUST_TIME_STAMPING, CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, + CKA_CERT_MD5_HASH, CKA_NSS_DB +}; + +static const int known_attributes_size = PR_ARRAY_SIZE(known_attributes); + +/* * Note on use of sqlReadDB: Only one thread at a time may have an actual * operation going on given sqlite3 * database. An operation is defined as * the time from a sqlite3_prepare() until the sqlite3_finalize(). @@ -103,68 +146,26 @@ typedef enum { * other operations like NSC_GetAttributeValue */ struct SDBPrivateStr { - char *sqlDBName; /* invariant, path to this database */ - sqlite3 *sqlXactDB; /* access protected by dbMon, use protected + char *sqlDBName; /* invariant, path to this database */ + sqlite3 *sqlXactDB; /* access protected by dbMon, use protected * by the transaction. Current transaction db*/ - PRThread *sqlXactThread; /* protected by dbMon, + PRThread *sqlXactThread; /* protected by dbMon, * current transaction thread */ - sqlite3 *sqlReadDB; /* use protected by dbMon, value invariant */ - PRIntervalTime lastUpdateTime; /* last time the cache was updated */ - PRIntervalTime updateInterval; /* how long the cache can go before it + sqlite3 *sqlReadDB; /* use protected by dbMon, value invariant */ + PRIntervalTime lastUpdateTime; /* last time the cache was updated */ + PRIntervalTime updateInterval; /* how long the cache can go before it * must be updated again */ - sdbDataType type; /* invariant, database type */ - char *table; /* invariant, SQL table which contains the db */ - char *cacheTable; /* invariant, SQL table cache of db */ - PRMonitor *dbMon; /* invariant, monitor to protect + sdbDataType type; /* invariant, database type */ + char *table; /* invariant, SQL table which contains the db */ + char *cacheTable; /* invariant, SQL table cache of db */ + PRMonitor *dbMon; /* invariant, monitor to protect * sqlXact* fields, and use of the sqlReadDB */ + CK_ATTRIBUTE_TYPE *schemaAttrs; /* Attribute columns that exist in the table. */ + unsigned int numSchemaAttrs; }; typedef struct SDBPrivateStr SDBPrivate; -/* - * known attributes - */ -static const CK_ATTRIBUTE_TYPE known_attributes[] = { - CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION, - CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER, - CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED, - CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL, - CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY, - CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE, - CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER, - CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE, - CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT, - CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT, - CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS, - CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE, - CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE, - CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS, - CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS, - CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_TEMPLATE, - CKA_UNWRAP_TEMPLATE, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT, - CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, CKA_RESOLUTION, CKA_CHAR_ROWS, - CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS, - CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE, - CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES, - CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NETSCAPE_URL, CKA_NETSCAPE_EMAIL, - CKA_NETSCAPE_SMIME_INFO, CKA_NETSCAPE_SMIME_TIMESTAMP, - CKA_NETSCAPE_PKCS8_SALT, CKA_NETSCAPE_PASSWORD_CHECK, CKA_NETSCAPE_EXPIRES, - CKA_NETSCAPE_KRL, CKA_NETSCAPE_PQG_COUNTER, CKA_NETSCAPE_PQG_SEED, - CKA_NETSCAPE_PQG_H, CKA_NETSCAPE_PQG_SEED_BITS, CKA_NETSCAPE_MODULE_SPEC, - CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION, - CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT, - CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN, - CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING, - CKA_TRUST_EMAIL_PROTECTION, CKA_TRUST_IPSEC_END_SYSTEM, - CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING, - CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH, - CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS, - CKA_PUBLIC_KEY_INFO, CKA_NSS_SERVER_DISTRUST_AFTER, CKA_NSS_EMAIL_DISTRUST_AFTER -}; - -static int known_attributes_size = sizeof(known_attributes) / - sizeof(known_attributes[0]); - /* Magic for an explicit NULL. NOTE: ideally this should be * out of band data. Since it's not completely out of band, pick * a value that has no meaning to any existing PKCS #11 attributes. @@ -362,7 +363,7 @@ sdb_mapSQLError(sdbDataType type, int sqlerr) case SQLITE_CANTOPEN: case SQLITE_NOTFOUND: /* NSS distiguishes between failure to open the cert and the key db */ - return type == SDB_CERT ? CKR_NETSCAPE_CERTDB_FAILED : CKR_NETSCAPE_KEYDB_FAILED; + return type == SDB_CERT ? CKR_NSS_CERTDB_FAILED : CKR_NSS_KEYDB_FAILED; case SQLITE_IOERR: return CKR_DEVICE_ERROR; default: @@ -400,8 +401,20 @@ sdb_measureAccess(const char *directory) PRIntervalTime duration = PR_MillisecondsToInterval(33); const char *doesntExistName = "_dOeSnotExist_.db"; char *temp, *tempStartOfFilename; - size_t maxTempLen, maxFileNameLen, directoryLength; - + size_t maxTempLen, maxFileNameLen, directoryLength, tmpdirLength = 0; +#ifdef SDB_MEASURE_USE_TEMP_DIR + /* + * on some OS's and Filesystems, creating a bunch of files and deleting + * them messes up the systems's caching, but if we create the files in + * a temp directory which we later delete, then the cache gets cleared + * up. This code uses several OS dependent calls, and it's not clear + * that temp directory use won't mess up other filesystems and OS caching, + * so if you need this for your OS, you can turn on the + * 'SDB_MEASURE_USE_TEMP_DIR' define in coreconf + */ + const char template[] = "dbTemp.XXXXXX"; + tmpdirLength = sizeof(template); +#endif /* no directory, just return one */ if (directory == NULL) { return 1; @@ -412,24 +425,39 @@ sdb_measureAccess(const char *directory) directoryLength = strlen(directory); - maxTempLen = directoryLength + strlen(doesntExistName) + 1 /* potential additional separator char */ - + 11 /* max chars for 32 bit int plus potential sign */ - + 1; /* zero terminator */ + maxTempLen = directoryLength + 1 /* dirname + / */ + + tmpdirLength /* tmpdirname includes / */ + + strlen(doesntExistName) /* filename base */ + + 11 /* max chars for 32 bit int plus potential sign */ + + 1; /* zero terminator */ - temp = PORT_Alloc(maxTempLen); + temp = PORT_ZAlloc(maxTempLen); if (!temp) { return 1; } /* We'll copy directory into temp just once, then ensure it ends - * with the directory separator, then remember the position after - * the separator, and calculate the number of remaining bytes. */ + * with the directory separator. */ strcpy(temp, directory); if (directory[directoryLength - 1] != PR_GetDirectorySeparator()) { temp[directoryLength++] = PR_GetDirectorySeparator(); } - tempStartOfFilename = temp + directoryLength; + +#ifdef SDB_MEASURE_USE_TEMP_DIR + /* add the template for a temporary subdir, and create it */ + strcat(temp, template); + if (!mkdtemp(temp)) { + PORT_Free(temp); + return 1; + } + /* and terminate that tmp subdir with a / */ + strcat(temp, "/"); +#endif + + /* Remember the position after the last separator, and calculate the + * number of remaining bytes. */ + tempStartOfFilename = temp + directoryLength + tmpdirLength; maxFileNameLen = maxTempLen - directoryLength; /* measure number of Access operations that can be done in 33 milliseconds @@ -453,6 +481,12 @@ sdb_measureAccess(const char *directory) break; } +#ifdef SDB_MEASURE_USE_TEMP_DIR + /* turn temp back into our tmpdir path by removing doesntExistName, and + * remove the tmp dir */ + *tempStartOfFilename = '\0'; + (void)rmdir(temp); +#endif PORT_Free(temp); /* always return 1 or greater */ @@ -858,9 +892,9 @@ sdb_FindObjectsFinal(SDB *sdb, SDBFind *sdbFind) return sdb_mapSQLError(sdb_p->type, sqlerr); } -CK_RV -sdb_GetAttributeValueNoLock(SDB *sdb, CK_OBJECT_HANDLE object_id, - CK_ATTRIBUTE *template, CK_ULONG count) +static CK_RV +sdb_GetValidAttributeValueNoLock(SDB *sdb, CK_OBJECT_HANDLE object_id, + CK_ATTRIBUTE *template, CK_ULONG count) { SDBPrivate *sdb_p = sdb->private; sqlite3 *sqlDB = NULL; @@ -992,19 +1026,109 @@ loser: return error; } +/* NOTE: requires sdb_p->schemaAttrs to be sorted asc. */ +inline static PRBool +sdb_attributeExists(SDB *sdb, CK_ATTRIBUTE_TYPE attr) +{ + SDBPrivate *sdb_p = sdb->private; + int first = 0; + int last = (int)sdb_p->numSchemaAttrs - 1; + while (last >= first) { + int mid = first + (last - first) / 2; + if (sdb_p->schemaAttrs[mid] == attr) { + return PR_TRUE; + } + if (attr > sdb_p->schemaAttrs[mid]) { + first = mid + 1; + } else { + last = mid - 1; + } + } + + return PR_FALSE; +} + CK_RV sdb_GetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id, CK_ATTRIBUTE *template, CK_ULONG count) { - CK_RV crv; + CK_RV crv = CKR_OK; + unsigned int tmplIdx; + unsigned int resIdx = 0; + unsigned int validCount = 0; + unsigned int i; if (count == 0) { - return CKR_OK; + return crv; + } + + CK_ATTRIBUTE *validTemplate; + PRBool invalidExists = PR_FALSE; + for (tmplIdx = 0; tmplIdx < count; tmplIdx++) { + if (!sdb_attributeExists(sdb, template[tmplIdx].type)) { + template[tmplIdx].ulValueLen = -1; + crv = CKR_ATTRIBUTE_TYPE_INVALID; + invalidExists = PR_TRUE; + break; + } + } + + if (!invalidExists) { + validTemplate = template; + validCount = count; + } else { + /* Create a new template containing only the valid subset of + * input |template|, and query with that. */ + validCount = tmplIdx; + validTemplate = malloc(sizeof(CK_ATTRIBUTE) * count); + if (!validTemplate) { + return CKR_HOST_MEMORY; + } + /* Copy in what we already know is valid. */ + for (i = 0; i < validCount; i++) { + validTemplate[i] = template[i]; + } + + /* tmplIdx was left at the index of the first invalid + * attribute, which has been handled. We only need to + * deal with the remainder. */ + tmplIdx++; + for (; tmplIdx < count; tmplIdx++) { + if (sdb_attributeExists(sdb, template[tmplIdx].type)) { + validTemplate[validCount++] = template[tmplIdx]; + } else { + template[tmplIdx].ulValueLen = -1; + } + } + } + + if (validCount) { + LOCK_SQLITE() + CK_RV crv2 = sdb_GetValidAttributeValueNoLock(sdb, object_id, validTemplate, validCount); + UNLOCK_SQLITE() + + /* If an invalid attribute was removed above, let + * the caller know. Any other error from the actual + * query should propogate. */ + crv = (crv2 == CKR_OK) ? crv : crv2; + } + + if (invalidExists) { + /* Copy out valid lengths. */ + tmplIdx = 0; + for (resIdx = 0; resIdx < validCount; resIdx++) { + for (; tmplIdx < count; tmplIdx++) { + if (template[tmplIdx].type != validTemplate[resIdx].type) { + continue; + } + template[tmplIdx].ulValueLen = validTemplate[resIdx].ulValueLen; + tmplIdx++; + break; + } + } + free(validTemplate); } - LOCK_SQLITE() - crv = sdb_GetAttributeValueNoLock(sdb, object_id, template, count); - UNLOCK_SQLITE() return crv; } @@ -1115,7 +1239,7 @@ sdb_objectExists(SDB *sdb, CK_OBJECT_HANDLE candidate) CK_RV crv; CK_ATTRIBUTE template = { CKA_LABEL, NULL, 0 }; - crv = sdb_GetAttributeValueNoLock(sdb, candidate, &template, 1); + crv = sdb_GetValidAttributeValueNoLock(sdb, candidate, &template, 1); if (crv == CKR_OBJECT_HANDLE_INVALID) { return PR_FALSE; } @@ -1161,6 +1285,19 @@ sdb_getObjectId(SDB *sdb) return CK_INVALID_HANDLE; } +CK_RV +sdb_GetNewObjectID(SDB *sdb, CK_OBJECT_HANDLE *object) +{ + CK_OBJECT_HANDLE id; + + id = sdb_getObjectId(sdb); + if (id == CK_INVALID_HANDLE) { + return CKR_DEVICE_MEMORY; /* basically we ran out of resources */ + } + *object = id; + return CKR_OK; +} + static const char CREATE_CMD[] = "INSERT INTO %s (id%s) VALUES($ID%s);"; CK_RV sdb_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *object_id, @@ -1268,10 +1405,13 @@ loser: return error; } +/* + * Generic destroy that can destroy metadata or objects + */ static const char DESTROY_CMD[] = "DELETE FROM %s WHERE (id=$ID);"; - CK_RV -sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id) +sdb_destroyAnyObject(SDB *sdb, const char *table, + CK_OBJECT_HANDLE object_id, const char *string_id) { SDBPrivate *sdb_p = sdb->private; sqlite3 *sqlDB = NULL; @@ -1290,7 +1430,7 @@ sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id) if (error != CKR_OK) { goto loser; } - newStr = sqlite3_mprintf(DESTROY_CMD, sdb_p->table); + newStr = sqlite3_mprintf(DESTROY_CMD, table); if (newStr == NULL) { error = CKR_HOST_MEMORY; goto loser; @@ -1299,7 +1439,12 @@ sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id) sqlite3_free(newStr); if (sqlerr != SQLITE_OK) goto loser; - sqlerr = sqlite3_bind_int(stmt, 1, object_id); + if (string_id == NULL) { + sqlerr = sqlite3_bind_int(stmt, 1, object_id); + } else { + sqlerr = sqlite3_bind_text(stmt, 1, string_id, + PORT_Strlen(string_id), SQLITE_STATIC); + } if (sqlerr != SQLITE_OK) goto loser; @@ -1328,6 +1473,19 @@ loser: return error; } +CK_RV +sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id) +{ + SDBPrivate *sdb_p = sdb->private; + return sdb_destroyAnyObject(sdb, sdb_p->table, object_id, NULL); +} + +CK_RV +sdb_DestroyMetaData(SDB *sdb, const char *id) +{ + return sdb_destroyAnyObject(sdb, "metaData", 0, id); +} + static const char BEGIN_CMD[] = "BEGIN IMMEDIATE TRANSACTION;"; /* @@ -1725,6 +1883,7 @@ sdb_Close(SDB *sdb) if (sdb_p->dbMon) { PR_DestroyMonitor(sdb_p->dbMon); } + free(sdb_p->schemaAttrs); free(sdb_p); free(sdb); return sdb_mapSQLError(type, sqlerr); @@ -1762,6 +1921,18 @@ sdb_SetForkState(PRBool forked) * interface, we will need to set it and reset it from here */ } +static int +sdb_attributeComparator(const void *a, const void *b) +{ + if (*(CK_ATTRIBUTE_TYPE *)a < *(CK_ATTRIBUTE_TYPE *)b) { + return -1; + } + if (*(CK_ATTRIBUTE_TYPE *)a > *(CK_ATTRIBUTE_TYPE *)b) { + return 1; + } + return 0; +} + /* * initialize a single database */ @@ -1775,6 +1946,7 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate, int i; char *initStr = NULL; char *newStr; + char *queryStr = NULL; int inTransaction = 0; SDB *sdb = NULL; SDBPrivate *sdb_p = NULL; @@ -2028,7 +2200,85 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate, } sdb = (SDB *)malloc(sizeof(SDB)); + if (!sdb) { + error = CKR_HOST_MEMORY; + goto loser; + } sdb_p = (SDBPrivate *)malloc(sizeof(SDBPrivate)); + if (!sdb_p) { + error = CKR_HOST_MEMORY; + goto loser; + } + + /* Cache the attributes that are held in the table, so we can later check + * that queried attributes actually exist. We don't assume the schema + * to be exactly |known_attributes|, as it may change over time. */ + sdb_p->schemaAttrs = NULL; + if (!PORT_Strcmp("nssPublic", table) || + !PORT_Strcmp("nssPrivate", table)) { + sqlite3_stmt *stmt = NULL; + int retry = 0; + unsigned int backedAttrs = 0; + + /* Can't bind parameters to a PRAGMA. */ + queryStr = sqlite3_mprintf("PRAGMA table_info(%s);", table); + if (queryStr == NULL) { + error = CKR_HOST_MEMORY; + goto loser; + } + sqlerr = sqlite3_prepare_v2(sqlDB, queryStr, -1, &stmt, NULL); + sqlite3_free(queryStr); + queryStr = NULL; + if (sqlerr != SQLITE_OK) { + goto loser; + } + unsigned int schemaAttrsCapacity = known_attributes_size; + sdb_p->schemaAttrs = malloc(schemaAttrsCapacity * sizeof(CK_ATTRIBUTE_TYPE)); + if (!sdb_p->schemaAttrs) { + error = CKR_HOST_MEMORY; + goto loser; + } + do { + sqlerr = sqlite3_step(stmt); + if (sqlerr == SQLITE_BUSY) { + PR_Sleep(SDB_BUSY_RETRY_TIME); + } + if (sqlerr == SQLITE_ROW) { + if (backedAttrs == schemaAttrsCapacity) { + schemaAttrsCapacity += known_attributes_size; + sdb_p->schemaAttrs = realloc(sdb_p->schemaAttrs, + schemaAttrsCapacity * sizeof(CK_ATTRIBUTE_TYPE)); + if (!sdb_p->schemaAttrs) { + error = CKR_HOST_MEMORY; + goto loser; + } + } + /* Record the ULONG attribute value. */ + char *val = (char *)sqlite3_column_text(stmt, 1); + if (val && val[0] == 'a') { + CK_ATTRIBUTE_TYPE attr = strtoul(&val[1], NULL, 16); + sdb_p->schemaAttrs[backedAttrs++] = attr; + } + } + } while (!sdb_done(sqlerr, &retry)); + if (sqlerr != SQLITE_DONE) { + goto loser; + } + sqlerr = sqlite3_reset(stmt); + if (sqlerr != SQLITE_OK) { + goto loser; + } + sqlerr = sqlite3_finalize(stmt); + if (sqlerr != SQLITE_OK) { + goto loser; + } + + sdb_p->numSchemaAttrs = backedAttrs; + + /* Sort these once so we can shortcut invalid attribute searches. */ + qsort(sdb_p->schemaAttrs, sdb_p->numSchemaAttrs, + sizeof(CK_ATTRIBUTE_TYPE), sdb_attributeComparator); + } /* invariant fields */ sdb_p->sqlDBName = PORT_Strdup(dbname); @@ -2044,7 +2294,7 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate, sdb_p->sqlXactDB = NULL; sdb_p->sqlXactThread = NULL; sdb->private = sdb_p; - sdb->version = 0; + sdb->version = 1; sdb->sdb_flags = inFlags | SDB_HAS_META; sdb->app_private = NULL; sdb->sdb_FindObjectsInit = sdb_FindObjectsInit; @@ -2056,12 +2306,14 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate, sdb->sdb_DestroyObject = sdb_DestroyObject; sdb->sdb_GetMetaData = sdb_GetMetaData; sdb->sdb_PutMetaData = sdb_PutMetaData; + sdb->sdb_DestroyMetaData = sdb_DestroyMetaData; sdb->sdb_Begin = sdb_Begin; sdb->sdb_Commit = sdb_Commit; sdb->sdb_Abort = sdb_Abort; sdb->sdb_Reset = sdb_Reset; sdb->sdb_Close = sdb_Close; sdb->sdb_SetForkState = sdb_SetForkState; + sdb->sdb_GetNewObjectID = sdb_GetNewObjectID; if (inTransaction) { sqlerr = sqlite3_exec(sqlDB, COMMIT_CMD, NULL, 0, NULL); @@ -2087,6 +2339,9 @@ loser: free(sdb); } if (sdb_p) { + if (sdb_p->schemaAttrs) { + free(sdb_p->schemaAttrs); + } free(sdb_p); } if (sqlDB) { |