Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Store passwords in USER.PW as either cleartext (as is done in legacy) or as the SHA1 hash of the password. When changing a password or adding a new user, always use the SHA1 hash password. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | experimental |
| Files: | files | file ages | folders |
| SHA1: |
cfe33dcf9204b0ea2f6aebbdd2a66407 |
| User & Date: | drh 2010-01-09 22:03:21.000 |
| Original Comment: | Store passwords in USER.PW as either cleartext (as is done in legacy) or as the SHA1 hash of the password. When changing a password or adding a new user, always use the SHA1 hash password. |
References
|
2010-01-10
| ||
| 04:57 | • Ticket [e523287834] user passwords are stored in plain text status still Closed with 1 other change artifact: b553e1c6ba user: rwilson | |
| 04:54 | • Ticket [e523287834]: 1 change artifact: b6df22e756 user: anonymous | |
Context
|
2010-01-09
| ||
| 22:34 | Remove some debug/test printfs that were mistakenly left in the previous commit. check-in: e8b76a69ae user: drh tags: experimental | |
| 22:03 | Store passwords in USER.PW as either cleartext (as is done in legacy) or as the SHA1 hash of the password. When changing a password or adding a new user, always use the SHA1 hash password. check-in: cfe33dcf92 user: drh tags: experimental | |
| 18:43 | Call ticket_init() from the report view so users can have access to all the common ticket code check-in: 6d0b76d01f user: jeremy_c tags: trunk | |
Changes
Changes to src/cgi.c.
| ︙ | ︙ | |||
396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
if( nAllocQP<=nUsedQP ){
nAllocQP = nAllocQP*2 + 10;
aParamQP = realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) );
if( aParamQP==0 ) exit(1);
}
aParamQP[nUsedQP].zName = zName;
aParamQP[nUsedQP].zValue = zValue;
aParamQP[nUsedQP].seq = seqQP++;
nUsedQP++;
sortQP = 1;
}
/*
** Add another query parameter or cookie to the parameter set.
| > > > | 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
if( nAllocQP<=nUsedQP ){
nAllocQP = nAllocQP*2 + 10;
aParamQP = realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) );
if( aParamQP==0 ) exit(1);
}
aParamQP[nUsedQP].zName = zName;
aParamQP[nUsedQP].zValue = zValue;
if( g.fHttpTrace ){
fprintf(stderr, "# cgi: %s = [%s]\n", zName, zValue);
}
aParamQP[nUsedQP].seq = seqQP++;
nUsedQP++;
sortQP = 1;
}
/*
** Add another query parameter or cookie to the parameter set.
|
| ︙ | ︙ | |||
1251 1252 1253 1254 1255 1256 1257 |
if( child>0 ) nchildren++;
close(connection);
}else{
close(0);
dup(connection);
close(1);
dup(connection);
| | | 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 |
if( child>0 ) nchildren++;
close(connection);
}else{
close(0);
dup(connection);
close(1);
dup(connection);
if( !g.fHttpTrace && !g.fSqlTrace ){
close(2);
dup(connection);
}
close(connection);
return 0;
}
}
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
1177 1178 1179 1180 1181 1182 1183 |
}
/*
** This function registers auxiliary functions when the SQLite
** database connection is first established.
*/
LOCAL void db_connection_init(void){
| < < | | | | | | | | | | < < | 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 |
}
/*
** This function registers auxiliary functions when the SQLite
** database connection is first established.
*/
LOCAL void db_connection_init(void){
sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
sqlite3_create_function(
g.db, "file_is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
);
if( g.fSqlTrace ){
sqlite3_trace(g.db, db_sql_trace, 0);
}
}
/*
** Return true if the string zVal represents "true" (or "false").
*/
int is_truth(const char *zVal){
|
| ︙ | ︙ |
Changes to src/http.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 |
** of all payload that follows the login card. SIGNATURE is the sha1
** checksum of the nonce followed by the user password.
**
** Write the constructed login card into pLogin. pLogin is initialized
** by this routine.
*/
static void http_build_login_card(Blob *pPayload, Blob *pLogin){
| | > | > | | < | > > > > > > > > > > > > > > > > > > > > | | | < | | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
** of all payload that follows the login card. SIGNATURE is the sha1
** checksum of the nonce followed by the user password.
**
** Write the constructed login card into pLogin. pLogin is initialized
** by this routine.
*/
static void http_build_login_card(Blob *pPayload, Blob *pLogin){
Blob nonce; /* The nonce */
const char *zLogin; /* The user login name */
const char *zPw; /* The user password */
Blob pw; /* The nonce with user password appended */
Blob sig; /* The signature field */
blob_zero(&nonce);
blob_zero(&pw);
sha1sum_blob(pPayload, &nonce);
blob_copy(&pw, &nonce);
blob_zero(pLogin);
if( g.urlUser==0 ){
user_select();
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", g.userUid);
zLogin = g.zLogin;
}else{
if( g.urlPasswd==0 ){
if( strcmp(g.urlUser,"anonymous")!=0 ){
char *zPrompt = mprintf("password for %s: ", g.urlUser);
Blob x;
prompt_for_password(zPrompt, &x, 0);
free(zPrompt);
g.urlPasswd = blob_str(&x);
}else{
g.urlPasswd = "";
}
}
zPw = g.urlPasswd;
zLogin = g.urlUser;
}
/* The login card wants the SHA1 hash of the password, so convert the
** password to its SHA1 hash it it isn't already a SHA1 hash.
**
** Except, if the password begins with "*" then use the characters
** after the "*" as a cleartext password. Put an "*" at the beginning
** of the password to trick a newer client to use the cleartext password
** protocol required by legacy servers.
*/
if( zPw && zPw[0] ){
if( zPw[0]=='*' ){
zPw++;
}else{
zPw = sha1sum(zPw);
}
}
blob_append(&pw, zPw, -1);
sha1sum_blob(&pw, &sig);
blob_appendf(pLogin, "login %F %b %b\n", zLogin, &nonce, &sig);
blob_reset(&pw);
blob_reset(&sig);
blob_reset(&nonce);
}
/*
** Construct an appropriate HTTP request header. Write the header
** into pHdr. This routine initializes the pHdr blob. pPayload is
** the complete payload (including the login card) already compressed.
*/
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
void login_page(void){
const char *zUsername, *zPasswd;
const char *zNew1, *zNew2;
const char *zAnonPw = 0;
int anonFlag;
char *zErrMsg = "";
int uid; /* User id loged in user */
login_check_credentials();
zUsername = P("u");
zPasswd = P("p");
anonFlag = P("anon")!=0;
if( P("out")!=0 ){
const char *zCookieName = login_cookie_name();
cgi_set_cookie(zCookieName, "", 0, -86400);
redirect_to_g();
}
if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
if( db_int(1, "SELECT 0 FROM user"
| > > | > | | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
void login_page(void){
const char *zUsername, *zPasswd;
const char *zNew1, *zNew2;
const char *zAnonPw = 0;
int anonFlag;
char *zErrMsg = "";
int uid; /* User id loged in user */
char *zSha1Pw;
login_check_credentials();
zUsername = P("u");
zPasswd = P("p");
zSha1Pw = (zPasswd && zPasswd[0]) ? sha1sum(zPasswd) : "";
anonFlag = P("anon")!=0;
if( P("out")!=0 ){
const char *zCookieName = login_cookie_name();
cgi_set_cookie(zCookieName, "", 0, -86400);
redirect_to_g();
}
if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
if( db_int(1, "SELECT 0 FROM user"
" WHERE uid=%d AND (pw=%Q OR pw=%Q)",
g.userUid, zPasswd, zSha1Pw) ){
sleep(1);
zErrMsg =
@ <p><font color="red">
@ You entered an incorrect old password while attempting to change
@ your password. Your password is unchanged.
@ </font></p>
;
}else if( strcmp(zNew1,zNew2)!=0 ){
zErrMsg =
@ <p><font color="red">
@ The two copies of your new passwords do not match.
@ Your password is unchanged.
@ </font></p>
;
}else{
db_multi_exec(
"UPDATE user SET pw=%Q WHERE uid=%d", sha1sum(zNew1), g.userUid
);
redirect_to_g();
return;
}
}
uid = isValidAnonymousLogin(zUsername, zPasswd);
if( uid>0 ){
|
| ︙ | ︙ | |||
198 199 200 201 202 203 204 |
redirect_to_g();
}
if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
uid = db_int(0,
"SELECT uid FROM user"
" WHERE login=%Q"
" AND login NOT IN ('anonymous','nobody','developer','reader')"
| | | | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
redirect_to_g();
}
if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
uid = db_int(0,
"SELECT uid FROM user"
" WHERE login=%Q"
" AND login NOT IN ('anonymous','nobody','developer','reader')"
" AND (pw=%Q OR pw=%Q)",
zUsername, zPasswd, zSha1Pw
);
if( uid<=0 ){
sleep(1);
zErrMsg =
@ <p><font color="red">
@ You entered an unknown user or an incorrect password.
@ </font></p>
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
824 825 826 827 828 829 830 |
}
db_close();
if( cgi_http_server(iPort, mxPort, zBrowserCmd) ){
fossil_fatal("unable to listen on TCP socket %d", iPort);
}
g.httpIn = stdin;
g.httpOut = stdout;
| | | 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 |
}
db_close();
if( cgi_http_server(iPort, mxPort, zBrowserCmd) ){
fossil_fatal("unable to listen on TCP socket %d", iPort);
}
g.httpIn = stdin;
g.httpOut = stdout;
if( g.fHttpTrace || g.fSqlTrace ){
fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
}
g.cgiPanic = 1;
if( g.argc==2 ){
db_must_be_within_tree();
}else{
db_open_repository(g.argv[2]);
|
| ︙ | ︙ |
Changes to src/schema.c.
| ︙ | ︙ | |||
93 94 95 96 97 98 99 100 101 102 103 104 105 106 | @ uid INTEGER REFERENCES user, -- User login @ mtime DATETIME, -- Time or receipt @ nonce TEXT UNIQUE, -- Nonce used for login @ ipaddr TEXT -- Remote IP address. NULL for direct. @ ); @ @ -- Information about users @ -- @ CREATE TABLE user( @ uid INTEGER PRIMARY KEY, -- User ID @ login TEXT, -- login name of the user @ pw TEXT, -- password @ cap TEXT, -- Capabilities of this user @ cookie TEXT, -- WWW login cookie | > > > > > | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | @ uid INTEGER REFERENCES user, -- User login @ mtime DATETIME, -- Time or receipt @ nonce TEXT UNIQUE, -- Nonce used for login @ ipaddr TEXT -- Remote IP address. NULL for direct. @ ); @ @ -- Information about users @ -- @ -- The user.pw field can be either cleartext of the password, or @ -- a SHA1 hash of the password. If the user.pw field is exactly 40 @ -- characters long we assume it is a SHA1 hash. Otherwise, it is @ -- cleartext. @ -- @ CREATE TABLE user( @ uid INTEGER PRIMARY KEY, -- User ID @ login TEXT, -- login name of the user @ pw TEXT, -- password @ cap TEXT, -- Capabilities of this user @ cookie TEXT, -- WWW login cookie |
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
316 317 318 319 320 321 322 |
if( au ){ zCap[i++] = 'u'; }
if( av ){ zCap[i++] = 'v'; }
if( aw ){ zCap[i++] = 'w'; }
if( az ){ zCap[i++] = 'z'; }
zCap[i] = 0;
zPw = P("pw");
| | > > | 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
if( au ){ zCap[i++] = 'u'; }
if( av ){ zCap[i++] = 'v'; }
if( aw ){ zCap[i++] = 'w'; }
if( az ){ zCap[i++] = 'z'; }
zCap[i] = 0;
zPw = P("pw");
if( isValidPwString(zPw) ){
zPw = sha1sum(zPw);
}else{
zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
}
zLogin = P("login");
if( uid>0 &&
db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
){
style_header("User Creation Error");
|
| ︙ | ︙ | |||
471 472 473 474 475 476 477 |
@ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Tkt<br>
@ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Tkt Report<br>
@ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
@ </td>
@ </tr>
@ <tr>
@ <td align="right">Password:</td>
| < < | | | 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 |
@ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Tkt<br>
@ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Tkt Report<br>
@ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
@ </td>
@ </tr>
@ <tr>
@ <td align="right">Password:</td>
if( zPw[0] ){
/* Obscure the password for all users */
@ <td><input type="password" name="pw" value="**********"></td>
}else{
/* Show an empty password as an empty input field */
@ <td><input type="password" name="pw" value=""></td>
}
@ </tr>
if( !higherUser ){
|
| ︙ | ︙ |
Changes to src/sha1.c.
| ︙ | ︙ | |||
531 532 533 534 535 536 537 538 539 540 541 542 543 544 |
blob_zero(pCksum);
}
blob_resize(pCksum, 40);
SHA1Result(&ctx, zResult);
DigestToBase16(zResult, blob_buffer(pCksum));
return 0;
}
/*
** COMMAND: sha1sum
** %fossil sha1sum FILE...
**
** Compute an SHA1 checksum of all files named on the command-line.
| > > > > > > > > > > > > > > > > | 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 |
blob_zero(pCksum);
}
blob_resize(pCksum, 40);
SHA1Result(&ctx, zResult);
DigestToBase16(zResult, blob_buffer(pCksum));
return 0;
}
/*
** Compute the SHA1 checksum of a zero-terminated string. The
** result is held in memory obtained from mprintf().
*/
char *sha1sum(const char *zIn){
SHA1Context ctx;
unsigned char zResult[20];
char zDigest[41];
SHA1Reset(&ctx);
SHA1Input(&ctx, (unsigned const char*)zIn, strlen(zIn));
SHA1Result(&ctx, zResult);
DigestToBase16(zResult, zDigest);
return mprintf("%s", zDigest);
}
/*
** COMMAND: sha1sum
** %fossil sha1sum FILE...
**
** Compute an SHA1 checksum of all files named on the command-line.
|
| ︙ | ︙ |
Changes to src/user.c.
| ︙ | ︙ | |||
245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
}else{
zPrompt = mprintf("new passwd for %s: ", g.argv[3]);
prompt_for_password(zPrompt, &pw, 1);
}
if( blob_size(&pw)==0 ){
printf("password unchanged\n");
}else{
db_multi_exec("UPDATE user SET pw=%B WHERE uid=%d", &pw, uid);
}
}else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
int uid;
if( g.argc!=4 && g.argc!=5 ){
usage("user capabilities USERNAME ?PERMISSIONS?");
}
| > | 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
}else{
zPrompt = mprintf("new passwd for %s: ", g.argv[3]);
prompt_for_password(zPrompt, &pw, 1);
}
if( blob_size(&pw)==0 ){
printf("password unchanged\n");
}else{
sha1sum_blob(&pw, &pw);
db_multi_exec("UPDATE user SET pw=%B WHERE uid=%d", &pw, uid);
}
}else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
int uid;
if( g.argc!=4 && g.argc!=5 ){
usage("user capabilities USERNAME ?PERMISSIONS?");
}
|
| ︙ | ︙ | |||
342 343 344 345 346 347 348 |
"INSERT INTO user(login, pw, cap, info)"
"VALUES('anonymous', '', 'cfghjkmnoqw', '')"
);
g.userUid = db_last_insert_rowid();
g.zLogin = "anonymous";
}
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
"INSERT INTO user(login, pw, cap, info)"
"VALUES('anonymous', '', 'cfghjkmnoqw', '')"
);
g.userUid = db_last_insert_rowid();
g.zLogin = "anonymous";
}
}
/*
** SQL command to compute a SHA1 hash.
*/
static void user_sha1_func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
char *zIn;
assert( argc==1 );
zIn = (char*)sqlite3_value_text(argv[0]);
if( zIn ){
sqlite3_result_text(context, sha1sum(zIn), -1, free);
}
}
/*
** COMMAND: test-hash-passwords
**
** Usage: %fossil test-hash-passwords REPOSITORY
**
** Convert all local password storage to use a SHA1 hash of the password
** rather than cleartext. Passwords that are already stored as the SHA1
** has are unchanged.
*/
void user_hash_passwords_cmd(void){
if( g.argc!=3 ) usage("REPOSITORY");
db_open_repository(g.argv[2]);
sqlite3_create_function(g.db, "sha1sum", 1, SQLITE_UTF8, 0,
user_sha1_func, 0, 0);
db_multi_exec(
"UPDATE user SET pw=sha1sum(pw)"
" WHERE length(pw)>0 AND length(pw)!=40"
);
}
|
Changes to src/xfer.c.
| ︙ | ︙ | |||
355 356 357 358 359 360 361 | sha1sum_blob(&tail, &h2); rc = blob_compare(pHash, &h2); blob_reset(&h2); blob_reset(&tail); return rc==0; } | < | 355 356 357 358 359 360 361 362 363 364 365 366 367 368 | sha1sum_blob(&tail, &h2); rc = blob_compare(pHash, &h2); blob_reset(&h2); blob_reset(&tail); return rc==0; } /* ** Check the signature on an application/x-fossil payload received by ** the HTTP server. The signature is a line of the following form: ** ** login LOGIN NONCE SIGNATURE ** ** The NONCE is the SHA1 hash of the remainder of the input. |
| ︙ | ︙ | |||
392 393 394 395 396 397 398 399 400 401 402 403 |
"SELECT pw, cap, uid FROM user"
" WHERE login=%Q"
" AND login NOT IN ('anonymous','nobody','developer','reader')"
" AND length(pw)>0",
zLogin
);
if( db_step(&q)==SQLITE_ROW ){
Blob pw, combined, hash;
blob_zero(&pw);
db_ephemeral_blob(&q, 0, &pw);
blob_zero(&combined);
blob_copy(&combined, pNonce);
| > > | > > > > > | > > > > > > > > > > > > | | > | | > | 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
"SELECT pw, cap, uid FROM user"
" WHERE login=%Q"
" AND login NOT IN ('anonymous','nobody','developer','reader')"
" AND length(pw)>0",
zLogin
);
if( db_step(&q)==SQLITE_ROW ){
int szPw;
Blob pw, combined, hash;
blob_zero(&pw);
db_ephemeral_blob(&q, 0, &pw);
szPw = blob_size(&pw);
blob_zero(&combined);
blob_copy(&combined, pNonce);
blob_append(&combined, blob_buffer(&pw), szPw);
sha1sum_blob(&combined, &hash);
assert( blob_size(&hash)==40 );
rc = blob_compare(&hash, pSig);
fprintf(stderr,"login card %s for %s with szpw=%d\n",rc?"miss":"hit",zLogin,szPw);
blob_reset(&hash);
blob_reset(&combined);
if( rc!=0 && szPw!=40 ){
/* If this server stores cleartext passwords and the password did not
** match, then perhaps the client is sending SHA1 passwords. Try
** again with the SHA1 password.
*/
blob_zero(&pw);
db_ephemeral_blob(&q, 0, &pw);
sha1sum_blob(&pw, &pw);
blob_zero(&combined);
blob_copy(&combined, pNonce);
blob_append(&combined, blob_buffer(&pw), blob_size(&pw));
blob_reset(&pw);
sha1sum_blob(&combined, &hash);
rc = blob_compare(&hash, pSig);
fprintf(stderr,"login card %s for %s after pw hashing\n",rc?"miss":"hit",zLogin);
blob_reset(&hash);
blob_reset(&combined);
}
if( rc==0 ){
const char *zCap;
zCap = db_column_text(&q, 1);
login_set_capabilities(zCap);
g.userUid = db_column_int(&q, 2);
g.zLogin = mprintf("%b", pLogin);
g.zNonce = mprintf("%b", pNonce);
|
| ︙ | ︙ |