diff --git a/ext/userauth/user-auth.txt b/ext/userauth/user-auth.txt new file mode 100644 index 0000000000..dd49dfcfb7 --- /dev/null +++ b/ext/userauth/user-auth.txt @@ -0,0 +1,151 @@ +Activate the user authentication logic by compiling SQLite with +the -DSQLITE_USER_AUTHENTICATION compile-time option. + +The following new APIs are available when user authentication is +activated: + + int sqlite3_user_authenticate( + sqlite3 *db, /* The database connection */ + const char *zUsername, /* Username */ + int nPW, /* Number of bytes in aPW[] */ + const void *aPW /* Password or credentials */ + ); + + int sqlite3_user_add( + sqlite3 *db, /* Database connection */ + const char *zUsername, /* Username to be added */ + int isAdmin, /* True to give new user admin privilege */ + int nPW, /* Number of bytes in aPW[] */ + const void *aPW /* Password or credentials */ + ); + + int sqlite3_user_change( + sqlite3 *db, /* Database connection */ + const char *zUsername, /* Username to change */ + int isAdmin, /* Modified admin privilege for the user */ + int nPW, /* Number of bytes in aPW[] */ + const void *aPW /* Modified password or credentials */ + ); + + int sqlite3_user_delete( + sqlite3 *db, /* Database connection */ + const char *zUsername /* Username to remove */ + ); + +The sqlite3_open(), sqlite3_open16(), and sqlite3_open_v2() interfaces +work as before: they open a new database connection. However, if the +database being opened requires authentication, then the database +connection will be unusable until after sqlite3_user_authenticate() +has been called successfully [1c]. The sqlite3_user_authenticate() call +will return SQLITE_OK if the authentication credentials are accepted +and SQLITE_ERROR if not. + +Calling sqlite3_user_authenticate() on a no-authentication-required +database connection is a harmless no-op. + +If the database is encrypted, then sqlite3_key_v2() must be called first, +with the correct decryption key, prior to invoking sqlite3_user_authenticate(). + +To recapitulate: When opening an existing unencrypted authentication- +required database, the call sequence is: + + sqlite3_open_v2() + sqlite3_user_authenticate(); + /* Database is now usable */ + +To open an existing, encrypted, authentication-required database, the +call sequence is: + + sqlite3_open_v2(); + sqlite3_key_v2(); + sqlite3_user_authenticate(); + /* Database is now usable */ + +When opening a no-authentication-required database, the database +connection is treated as if it was authenticated as an admin user. + +When ATTACH-ing new database files to a connection, each newly attached +database that is an authentication-required database is checked using +the same username and password as supplied to the main database. If that +check fails, then the ATTACH-ed database is unreadable [1g]. + +The sqlite3_user_add() interface can be used (by an admin user only) +to create a new user. When called on a no-authentication-required +database, this routine converts the database into an authentication- +required database [3], automatically makes the added user an +administrator [1b], and logs in the current connection as that user [1a]. +The sqlite3_user_add() interface only works for the "main" database, not +for any ATTACH-ed databases. Any call to sqlite3_user_add() by a +non-admin user results in an error. + +Hence, to create a new, unencrypted, authentication-required database, +the call sequence is: + + sqlite3_open_v2(); + sqlite3_user_add(); + +And to create a new, encrypted, authentication-required database, the call +sequence is: + + sqlite3_open_v2(); + sqlite3_key_v2(); + sqlite3_user_add(); + +The sqlite3_user_delete() interface can be used (by an admin user only) +to delete a user. The currently logged-in user cannot be deleted, +which guarantees that there is always an admin user and hence that +the database cannot be converted into a no-authentication-required +database [3]. + +The sqlite3_user_change() interface can be used to change a users +login credentials or admin privilege. Any user can change their own +login credentials [1b]. Only an admin user can change another users login +credentials or admin privilege setting. No user may change their own +admin privilege setting. + +The sqlite3_set_authorizer() callback is modified to take a 7th parameter +which is the username of the currently logged in user, or NULL for a +no-authentication-required database [1d]. + +----------------------------------------------------------------------------- +Implementation notes: + +An authentication-required database is identified by the presence of a +new table: + + CREATE TABLE sqlite_user( + uname TEXT PRIMARY KEY, + isAdmin BOOLEAN, + pw BLOB + ) WITHOUT ROWID; + +This table is inaccessible (unreadable and unwriteable) to non-admin users +and is read-only for admin users. However, if the same database file is +opened by a version of SQLite that omits the -DSQLITE_USER_AUTHENTICATION +compile-time option, then the sqlite_user table will be readable by +anybody and writeable by anybody if the "PRAGMA writable_schema=ON" +statement is run first. + +The sqlite_user.pw field is encoded by a built-in SQL function +"sqlite_crypt(X,Y)". The two arguments are both BLOBs. The first argument +is the plaintext password supplied to the sqlite3_user_authenticate() +interface. The second argument is the sqlite_user.pw value and is supplied +so that the function can extra the "salt" used by the password encoder. +the result of sqlite_crypt(X,Y) is another blob which is the value that +ends up being stored in sqlite_user.pw. To verify credentials X supplied +by the sqlite3_user_authenticate() routine, SQLite runs: + + sqlite_user.pw == sqlite_crypt(X, sqlite_user.pw) + +To compute an appropriate sqlite_user.pw value from a new or modified +password X, sqlite_crypt(X,NULL) is run. A new random salt is selected +when the second argument is NULL. + +The built-in version of of sqlite_crypt() uses a simple Ceasar-cypher +which prevents passwords from being revealed by search the raw database +for ASCII text, but is otherwise trivally broken. To truly secure the +passwords, the database should be encrypted using the SQLite Encryption +Extension or similar technology. Or, the application can use the +sqlite3_create_function() interface to provide an alternative +implementation of sqlite_crypt() that computes a stronger password hash, +perhaps using a cryptographic hash function like SHA1. diff --git a/ext/userauth/userauth.c b/ext/userauth/userauth.c new file mode 100644 index 0000000000..305ae43a11 --- /dev/null +++ b/ext/userauth/userauth.c @@ -0,0 +1,197 @@ +/* +** 2014-09-08 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains the bulk of the implementation of the +** user-authentication extension feature. Some parts of the user- +** authentication code are contained within the SQLite core (in the +** src/ subdirectory of the main source code tree) but those parts +** that could reasonable be separated out are moved into this file. +** +** To compile with the user-authentication feature, append this file to +** end of an SQLite amalgamation, then add the SQLITE_USER_AUTHENTICATION +** compile-time option. See the user-auth.txt file in the same source +** directory as this file for additional information. +*/ +#ifdef SQLITE_USER_AUTHENTICATION + +/* +** Prepare an SQL statement for use by the user authentication logic. +** Return a pointer to the prepared statement on success. Return a +** NULL pointer if there is an error of any kind. +*/ +static sqlite3_stmt *sqlite3UserAuthPrepare( + sqlite3 *db, + const char *zFormat, + ... +){ + sqlite3_stmt *pStmt; + char *zSql; + int rc; + va_list ap; + + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( zSql==0 ) return 0; + savedFlags = db->auth.authFlags; + db->auth.authFlags |= UAUTH_Ovrd; + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + db->auth.authFlags = savedFlags; + sqlite3_free(zSql); + if( rc ){ + sqlite3_finalize(pStmt); + pStmt = 0; + } + return pStmt; +} + +/* +** Check to see if database zDb has a "sqlite_user" table and if it does +** whether that table can authenticate zUser with nPw,zPw. +*/ +static int sqlite3UserAuthCheckLogin( + sqlite3 *db, /* The database connection to check */ + const char *zDb, /* Name of specific database to check */ + const char *zUser, /* User name */ + int nPw, /* Size of password in bytes */ + const char *zPw, /* Password */ + int *pbOk /* OUT: write boolean result here */ +){ + sqlite3_stmt *pStmt; + char *zSql; + int rc; + int iResult; + + *pbOk = 0; + iResult = 0; + pStmt = sqlite3UserAuthPrepare(db, + "SELECT 1 FROM \"%w\".sqlite_master " + " WHERE name='sqlite_user' AND type='table'", zDb); + if( pStmt==0 ) return SQLITE_NOMEM; + rc = sqlite3_step(pStmt): + sqlite3_finalize(pStmt); + if( rc==SQLITE_DONE ){ + *pbOk = 1; + return SQLITE_OK; + } + if( rc!=SQLITE_OK ){ + return rc; + } + pStmt = sqlite3UserAuthPrepare(db, + "SELECT pw=sqlite_crypt(?1,pw), isAdmin FROM \"%w\".sqlite_user" + " WHERE uname=?2", zDb); + if( pStmt==0 ) return SQLITE_NOMEM; + sqlite3_bind_blob(pStmt, 1, zPw, nPw, SQLITE_STATIC); + sqlite3_bind_text(pStmt, 2, zUser, -1, SQLITE_STATIC); + rc = sqlite_step(pStmt); + if( rc==SQLITE_ROW && sqlite3_column_int(pStmt,0) ){ + *pbOk = sqlite3_column_int(pStmt, 1); + } + sqlite3_finalize(pStmt); + return rc; +} + +/* +** If a database contains the SQLITE_USER table, then the +** sqlite3_user_authenticate() interface must be invoked with an +** appropriate username and password prior to enable read and write +** access to the database. +** +** Return SQLITE_OK on success or SQLITE_ERROR if the username/password +** combination is incorrect or unknown. +** +** If the SQLITE_USER table is not present in the database file, then +** this interface is a harmless no-op returnning SQLITE_OK. +*/ +int sqlite3_user_authenticate( + sqlite3 *db, /* The database connection */ + const char *zUsername, /* Username */ + int nPW, /* Number of bytes in aPW[] */ + const void *aPW /* Password or credentials */ +){ + int bOk = 0; + int rc; + + rc = sqlite3UserAuthCheckLogin(db, zUsername, nPw, zPw, &bOk); + if( bOk ){ + db->auth.authFlags = bOk==2 ? UAUTH_Auth|UAUTH_Admin : UAUTH_Auth; + sqlite3_free(db->auth.zAuthUser); + db->auth.zAuthUser = sqlite3_malloc("%s", zUsername); + sqlite3_free(db->auth.zPw); + db->auth.zPw = sqlite3_malloc( nPw+1 ); + if( db->auth.zPw ){ + memcpy(db->auth.zPw,zPw,nPw); + db->auth.nPw = nPw; + rc = SQLITE_OK; + }else{ + rc = SQLITE_NOMEM; + } + }else{ + db->auth.authFlags = 0; + } + return rc; +} + +/* +** The sqlite3_user_add() interface can be used (by an admin user only) +** to create a new user. When called on a no-authentication-required +** database, this routine converts the database into an authentication- +** required database, automatically makes the added user an +** administrator, and logs in the current connection as that user. +** The sqlite3_user_add() interface only works for the "main" database, not +** for any ATTACH-ed databases. Any call to sqlite3_user_add() by a +** non-admin user results in an error. +*/ +int sqlite3_user_add( + sqlite3 *db, /* Database connection */ + const char *zUsername, /* Username to be added */ + int isAdmin, /* True to give new user admin privilege */ + int nPW, /* Number of bytes in aPW[] */ + const void *aPW /* Password or credentials */ +){ + if( !DbIsAdmin(db) ) return SQLITE_ERROR; + + return SQLITE_OK; +} + +/* +** The sqlite3_user_change() interface can be used to change a users +** login credentials or admin privilege. Any user can change their own +** login credentials. Only an admin user can change another users login +** credentials or admin privilege setting. No user may change their own +** admin privilege setting. +*/ +int sqlite3_user_change( + sqlite3 *db, /* Database connection */ + const char *zUsername, /* Username to change */ + int isAdmin, /* Modified admin privilege for the user */ + int nPW, /* Number of bytes in aPW[] */ + const void *aPW /* Modified password or credentials */ +){ + return SQLITE_OK; +} + +/* +** The sqlite3_user_delete() interface can be used (by an admin user only) +** to delete a user. The currently logged-in user cannot be deleted, +** which guarantees that there is always an admin user and hence that +** the database cannot be converted into a no-authentication-required +** database. +*/ +int sqlite3_user_delete( + sqlite3 *db, /* Database connection */ + const char *zUsername /* Username to remove */ +){ + return SQLITE_OK; +} + +#endif /* SQLITE_USER_AUTHENTICATION */ diff --git a/ext/userauth/userauth.h b/ext/userauth/userauth.h new file mode 100644 index 0000000000..279675f72c --- /dev/null +++ b/ext/userauth/userauth.h @@ -0,0 +1,88 @@ +/* +** 2014-09-08 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains the application interface definitions for the +** user-authentication extension feature. +** +** To compile with the user-authentication feature, append this file to +** end of an SQLite amalgamation header file ("sqlite3.h"), then add +** the SQLITE_USER_AUTHENTICATION compile-time option. See the +** user-auth.txt file in the same source directory as this file for +** additional information. +*/ +#ifdef SQLITE_USER_AUTHENTICATION + +/* +** If a database contains the SQLITE_USER table, then the +** sqlite3_user_authenticate() interface must be invoked with an +** appropriate username and password prior to enable read and write +** access to the database. +** +** Return SQLITE_OK on success or SQLITE_ERROR if the username/password +** combination is incorrect or unknown. +** +** If the SQLITE_USER table is not present in the database file, then +** this interface is a harmless no-op returnning SQLITE_OK. +*/ +int sqlite3_user_authenticate( + sqlite3 *db, /* The database connection */ + const char *zUsername, /* Username */ + int nPW, /* Number of bytes in aPW[] */ + const void *aPW /* Password or credentials */ +); + +/* +** The sqlite3_user_add() interface can be used (by an admin user only) +** to create a new user. When called on a no-authentication-required +** database, this routine converts the database into an authentication- +** required database, automatically makes the added user an +** administrator, and logs in the current connection as that user. +** The sqlite3_user_add() interface only works for the "main" database, not +** for any ATTACH-ed databases. Any call to sqlite3_user_add() by a +** non-admin user results in an error. +*/ +int sqlite3_user_add( + sqlite3 *db, /* Database connection */ + const char *zUsername, /* Username to be added */ + int isAdmin, /* True to give new user admin privilege */ + int nPW, /* Number of bytes in aPW[] */ + const void *aPW /* Password or credentials */ +); + +/* +** The sqlite3_user_change() interface can be used to change a users +** login credentials or admin privilege. Any user can change their own +** login credentials. Only an admin user can change another users login +** credentials or admin privilege setting. No user may change their own +** admin privilege setting. +*/ +int sqlite3_user_change( + sqlite3 *db, /* Database connection */ + const char *zUsername, /* Username to change */ + int isAdmin, /* Modified admin privilege for the user */ + int nPW, /* Number of bytes in aPW[] */ + const void *aPW /* Modified password or credentials */ +); + +/* +** The sqlite3_user_delete() interface can be used (by an admin user only) +** to delete a user. The currently logged-in user cannot be deleted, +** which guarantees that there is always an admin user and hence that +** the database cannot be converted into a no-authentication-required +** database. +*/ +int sqlite3_user_delete( + sqlite3 *db, /* Database connection */ + const char *zUsername /* Username to remove */ +); + +#endif /* SQLITE_USER_AUTHENTICATION */ diff --git a/manifest b/manifest index ccd849754b..0b16bdd037 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fixes\sto\sos_unix.c\sto\ssupport\sdatabase\s(and\sother)\sfiles\slarger\sthan\s2GiB\son\sAndroid. -D 2014-09-06T17:06:13.410 +C Non-working\spreliminary\simplementation\sattempts\son\suser\sauthentication. +D 2014-09-09T14:47:53.726 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -144,6 +144,9 @@ F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea F ext/rtree/sqlite3rtree.h 83349d519fe5f518b3ea025d18dd1fe51b1684bd F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 +F ext/userauth/user-auth.txt f471c5a363ab0682b109d85982ea857f9a144ccc +F ext/userauth/userauth.c cea064a9b66d7497c74cc504b3a01d7f390cbe6e +F ext/userauth/userauth.h efbfb68ff083749ad63b12dcb5877b936c3458d6 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -171,7 +174,7 @@ F src/btmutex.c 49ca66250c7dfa844a4d4cb8272b87420d27d3a5 F src/btree.c b1c1cd1cc3ae2e433a23b9a6c9ab53805707d8cd F src/btree.h a79aa6a71e7f1055f01052b7f821bd1c2dce95c8 F src/btreeInt.h e0ecb5dba292722039a7540beb3fc448103273cc -F src/build.c 8cb237719c185eec7bd8449b2e747491ded11932 +F src/build.c 8b4c67c9fb638fb2e64b2bcd24677e71a5d61bfc F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0 F src/complete.c 535183afb3c75628b78ce82612931ac7cdf26f14 F src/ctime.c 0231df905e2c4abba4483ee18ffc05adc321df2a @@ -190,7 +193,7 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c 87c92f4a08e2f70220e3b22a9c3b2482d36a134a F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b F src/loadext.c 31c2122b7dd05a179049bbf163fd4839f181cbab -F src/main.c e48517e3da289d93ad86e8b7b4f68078df5e6e51 +F src/main.c 26299e9d9a72239f0652ac9a23e04ced3f536e70 F src/malloc.c 954de5f998c23237e04474a3f2159bf483bba65a F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c faf615aafd8be74a71494dfa027c113ea5c6615f @@ -217,8 +220,8 @@ F src/parse.y 22d6a074e5f5a7258947a1dc55a9bf946b765dd0 F src/pcache.c 2048affdb09a04478b5fc6e64cb1083078d369be F src/pcache.h 9b559127b83f84ff76d735c8262f04853be0c59a F src/pcache1.c dab8ab930d4a73b99768d881185994f34b80ecaa -F src/pragma.c 14bcdb504128a476cce5bbc086d5226c5e46c225 -F src/prepare.c 3842c1dfc0b053458e3adcf9f6efc48e03e3fe3d +F src/pragma.c e4f6421d7a1f23cd21bf281f2d8c56874879e1d0 +F src/prepare.c 9548c8efb80dbc14b0d54d58574c8a7414cde4db F src/printf.c e74925089a85e3c9f0e315595f41c139d3d118c2 F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece F src/resolve.c 0d1621e45fffe4b4396477cf46e41a84b0145ffb @@ -228,7 +231,7 @@ F src/shell.c 713cef4d73c05fc8e12f4960072329d767a05d50 F src/sqlite.h.in 64a77f2822f1325b12050972003184f99b655a0f F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3ext.h 1f40357fb9b12a80c5a3b2b109fd249b009213d4 -F src/sqliteInt.h 4d1b0488f097aa7be346176c4d5e3cc1e25d99da +F src/sqliteInt.h 883f905f17a78bb09061a8efa4f2708faa6ffacc F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 4e28a53e66bad8d014a510ef0205f5497c712b08 @@ -1193,7 +1196,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P e62aab5e9290503869e1f4d5e0fefd2b4dee0a69 9dca7ce55797b3eb617859f6189c1a2ec6f66566 -R 4eacabb789e62917f709201f03eadd53 -U dan -Z f60ed4f30cad7188918c7dd18fc1b985 +P ad7063aa1a0db32cdbe71815545b2edca57d3bcc +R 8d9410cfb98bb3b10637c181b910724f +T *branch * user-auth +T *sym-user-auth * +T -sym-trunk * +U drh +Z 7af3a6ca660957a95167232843af8c9e diff --git a/manifest.uuid b/manifest.uuid index fc5888a5d9..c31b773fe5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ad7063aa1a0db32cdbe71815545b2edca57d3bcc \ No newline at end of file +8440f093bac19a41d44ee352744354eab897fe4e \ No newline at end of file diff --git a/src/build.c b/src/build.c index 6d54befbcc..c30645cdc6 100644 --- a/src/build.c +++ b/src/build.c @@ -271,6 +271,16 @@ void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){ pParse->nested--; } +#if SQLITE_USER_AUTHENTICATION +/* +** Return TRUE if zTable is the name of the system table that stores the +** list of users and their access credentials. +*/ +int sqlite3UserAuthTable(const char *zTable){ + return sqlite3_stricmp(zTable, "sqlite_user")==0; +} +#endif + /* ** Locate the in-memory structure that describes a particular database ** table given the name of that table and (optionally) the name of the @@ -289,6 +299,11 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){ assert( zName!=0 ); /* All mutexes are required for schema access. Make sure we hold them. */ assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); +#if SQLITE_USER_AUTHENTICATION + /* Only the admin user is allowed to know that the sqlite_user table + ** exists */ + if( DbIsAdmin(db)==0 && sqlite3UserAuthTable(zName)!=0 ) return 0; +#endif for(i=OMIT_TEMPDB; inDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue; @@ -2867,6 +2882,9 @@ Index *sqlite3CreateIndex( assert( pTab!=0 ); assert( pParse->nErr==0 ); if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 +#if SQLITE_USER_AUTHENTICATION + && sqlite3UserAuthTable(pTab->zName)==0 +#endif && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0 ){ sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); goto exit_create_index; diff --git a/src/main.c b/src/main.c index 977c786f38..f5114b7924 100644 --- a/src/main.c +++ b/src/main.c @@ -985,6 +985,10 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ sqlite3ValueFree(db->pErr); sqlite3CloseExtensions(db); +#if SQLITE_USER_AUTHENTICATION + sqlite3_free(db->auth.zAuthUser); + sqlite3_free(db->auth.zAuthPW); +#endif db->magic = SQLITE_MAGIC_ERROR; @@ -2566,6 +2570,9 @@ static int openDatabase( db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt); db->aDb[1].pSchema = sqlite3SchemaGet(db, 0); +#if SQLITE_USER_AUTHENTICATION + db->auth.authFlags = UAUTH_Auth|UAUTH_Admin; +#endif /* The default safety_level for the main database is 'full'; for the temp ** database it is 'NONE'. This matches the pager layer defaults. diff --git a/src/pragma.c b/src/pragma.c index 12446125fb..8ae79bda33 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1397,6 +1397,12 @@ void sqlite3Pragma( ** in auto-commit mode. */ mask &= ~(SQLITE_ForeignKeys); } +#if SQLITE_USER_AUTHENTICATION + if( !DbIsAdmin(db) ){ + /* Do not allow non-admin users to modify the schema arbitrarily */ + mask &= ~(SQLITE_WriteSchema); + } +#endif if( sqlite3GetBoolean(zRight, 0) ){ db->flags |= mask; diff --git a/src/prepare.c b/src/prepare.c index 5b92e88513..3703b35176 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -206,6 +206,9 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ if( ALWAYS(pTab) ){ pTab->tabFlags |= TF_Readonly; } +#if SQLITE_USER_AUTHENTICATION + db->auth.authFlags = UAUTH_Auth|UAUTH_Admin; +#endif /* Create a cursor to hold the database open */ @@ -361,6 +364,14 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ DbSetProperty(db, iDb, DB_SchemaLoaded); rc = SQLITE_OK; } +#if SQLITE_USER_AUTHENTICATION + if( rc==SQLITE_OK && iDb!=1 ){ + if( sqlite3FindTable(db, "sqlite_user", db->aDb[iDb].zName)!=0 ){ + db->auth.authFlags = UAUTH_AuthReqd; + } + } +#endif + /* Jump here for an error that occurs after successfully allocating ** curMain and calling sqlite3BtreeEnter(). For an error that occurs @@ -720,6 +731,17 @@ static int sqlite3LockAndPrepare( sqlite3_finalize(*ppStmt); rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, pOld, ppStmt, pzTail); } +#if SQLITE_USER_AUTHENTICATION + assert( rc==SQLITE_OK || *ppStmt==0 ); +printf("rc=%d init=%d auth=%d sql=[%.50s]\n", rc, db->init.busy, db->auth.authFlags, zSql); +fflush(stdout); + if( rc==SQLITE_OK && !DbIsAuth(db) && db->init.busy==0 ){ + sqlite3_finalize(*ppStmt); + *ppStmt = 0; + sqlite3ErrorWithMsg(db, SQLITE_ERROR, "user not authenticated"); + rc = SQLITE_ERROR; + } +#endif sqlite3BtreeLeaveAll(db); sqlite3_mutex_leave(db->mutex); assert( rc==SQLITE_OK || *ppStmt==0 ); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index ceacdbb335..ae43014c4c 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -988,6 +988,35 @@ struct FuncDefHash { FuncDef *a[23]; /* Hash table for functions */ }; +#ifdef SQLITE_USER_AUTHENTICATION +/* +** Information held in the "sqlite3" database connection object and used +** to manage user authentication. +*/ +typedef struct sqlite3_userauth sqlite3_userauth; +struct sqlite3_userauth { + u8 authFlags; /* Status flags for user authentication */ + int nAuthPW; /* Size of the zAuthPW in bytes */ + char *zAuthPW; /* Password used to authenticate */ + char *zAuthUser; /* User name used to authenticate */ +}; + +/* Allowed values for sqlite3_userauth.authFlags */ +#define UAUTH_Ovrd 0x01 /* Do not enforce access restrictions */ +#define UAUTH_Auth 0x02 /* True if the user has authenticated */ +#define UAUTH_Admin 0x04 /* True if the user is an administrator */ +#define UAUTH_AuthReqd 0x08 /* True if main has an sqlite_user table */ + +/* Macros for accessing sqlite3.auth.authFlags */ +#define DbIsAuth(D) (((D)->auth.authFlags&UAUTH_Auth)!=0) +#define DbIsAdmin(D) (((D)->auth.authFlags&UAUTH_Admin)!=0) + +/* Functions used only by user authorization logic */ +int sqlite3UserAuthTable(const char*); + +#endif /* SQLITE_USER_AUTHENTICATION */ + + /* ** Each database connection is an instance of the following structure. */ @@ -1082,7 +1111,6 @@ struct sqlite3 { i64 nDeferredCons; /* Net deferred constraints this transaction. */ i64 nDeferredImmCons; /* Net deferred immediate constraints */ int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ - #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY /* The following variables are all protected by the STATIC_MASTER ** mutex, not by sqlite3.mutex. They are used by code in notify.c. @@ -1100,6 +1128,9 @@ struct sqlite3 { void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ #endif +#ifdef SQLITE_USER_AUTHENTICATION + sqlite3_userauth auth; /* User authentication information */ +#endif }; /*