Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch dmitry-security Excluding Merge-Ins
This is equivalent to a diff from 3fac77d7b0 to f4eb0f5afc
2011-10-04
| ||
15:15 | Merge protection against timing attacks into trunk. check-in: d4a341b49d user: dmitry tags: trunk | |
14:38 | Merge trunk into dmitry-security branch. Closed-Leaf check-in: f4eb0f5afc user: dmitry tags: dmitry-security | |
14:34 | Rename constant_time_eq to constant_time_cmp to better indicate that these functions return 0 when values are equal, like memcmp, strcmp, etc., not truth, to avoid possible mistakes. check-in: d244c484e7 user: dmitry tags: dmitry-security | |
2011-10-03
| ||
16:34 | Disabling Cache-control: no-store, as it made firefox forget about form field contents on back/forward in history. Resolution achieved by a minimal consensus at this thread on the mailing list. check-in: 3fac77d7b0 user: viriketo tags: trunk | |
2011-10-02
| ||
13:30 | minor hack to name_search() to make it stop searching after it determines there is an ambiguity. check-in: ae64088627 user: stephan tags: trunk | |
Changes to src/blob.c.
309 309 szA = blob_size(pA); 310 310 szB = blob_size(pB); 311 311 sz = szA<szB ? szA : szB; 312 312 rc = memcmp(blob_buffer(pA), blob_buffer(pB), sz); 313 313 if( rc==0 ){ 314 314 rc = szA - szB; 315 315 } 316 + return rc; 317 +} 318 + 319 +/* 320 +** Compare two blobs in constant time and return zero if they are equal. 321 +** Constant time comparison only applies for blobs of the same length. 322 +** If lengths are different, immediately returns 1. 323 +*/ 324 +int blob_constant_time_cmp(Blob *pA, Blob *pB){ 325 + int szA, szB, i; 326 + unsigned char *buf1, *buf2; 327 + unsigned char rc = 0; 328 + 329 + blob_is_init(pA); 330 + blob_is_init(pB); 331 + szA = blob_size(pA); 332 + szB = blob_size(pB); 333 + if( szA!=szB || szA==0 ) return 1; 334 + 335 + buf1 = blob_buffer(pA); 336 + buf2 = blob_buffer(pB); 337 + 338 + for( i=0; i<szA; i++ ){ 339 + rc = rc | (buf1[i] ^ buf2[i]); 340 + } 341 + 316 342 return rc; 317 343 } 318 344 319 345 /* 320 346 ** Compare a blob to a string. Return TRUE if they are equal. 321 347 */ 322 348 int blob_eq_str(Blob *pBlob, const char *z, int n){
Changes to src/login.c.
228 228 cgi_set_cookie(zCookieName, "", login_cookie_path(), -86400); 229 229 redirect_to_g(); 230 230 } 231 231 if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ 232 232 /* The user requests a password change */ 233 233 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); 234 234 if( db_int(1, "SELECT 0 FROM user" 235 - " WHERE uid=%d AND (pw=%Q OR pw=%Q)", 236 - g.userUid, zPasswd, zSha1Pw) ){ 235 + " WHERE uid=%d" 236 + " AND (constant_time_cmp(pw,%Q)=0" 237 + " OR constant_time_cmp(pw,%Q)=0)", 238 + g.userUid, zSha1Pw, zPasswd) ){ 237 239 sleep(1); 238 240 zErrMsg = 239 241 @ <p><span class="loginError"> 240 242 @ You entered an incorrect old password while attempting to change 241 243 @ your password. Your password is unchanged. 242 244 @ </span></p> 243 245 ; ................................................................................ 306 308 */ 307 309 zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0); 308 310 uid = db_int(0, 309 311 "SELECT uid FROM user" 310 312 " WHERE login=%Q" 311 313 " AND length(cap)>0 AND length(pw)>0" 312 314 " AND login NOT IN ('anonymous','nobody','developer','reader')" 313 - " AND (pw=%Q OR pw=%Q)", 314 - zUsername, zPasswd, zSha1Pw 315 + " AND (constant_time_cmp(pw,%Q)=0 OR constant_time_cmp(pw,%Q)=0)", 316 + zUsername, zSha1Pw, zPasswd 315 317 ); 316 318 if( uid<=0 ){ 317 319 sleep(1); 318 320 zErrMsg = 319 321 @ <p><span class="loginError"> 320 322 @ You entered an unknown user or an incorrect password. 321 323 @ </span></p> ................................................................................ 449 451 @ <tr><td></td> 450 452 @ <td><input type="submit" value="Change Password" /></td></tr> 451 453 @ </table> 452 454 @ </form> 453 455 } 454 456 style_footer(); 455 457 } 458 + 459 +/* 460 +** SQL function for constant time comparison of two values. 461 +** Sets result to 0 if two values are equal. 462 +*/ 463 +static void constant_time_cmp_function( 464 + sqlite3_context *context, 465 + int argc, 466 + sqlite3_value **argv 467 +){ 468 + const unsigned char *buf1, *buf2; 469 + int len, i; 470 + unsigned char rc = 0; 471 + 472 + assert( argc==2 ); 473 + len = sqlite3_value_bytes(argv[0]); 474 + if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){ 475 + rc = 1; 476 + }else{ 477 + buf1 = sqlite3_value_text(argv[0]); 478 + buf2 = sqlite3_value_text(argv[1]); 479 + for( i=0; i<len; i++ ){ 480 + rc = rc | (buf1[i] ^ buf2[i]); 481 + } 482 + } 483 + sqlite3_result_int(context, rc); 484 +} 456 485 457 486 /* 458 487 ** Attempt to find login credentials for user zLogin on a peer repository 459 488 ** with project code zCode. Transfer those credentials to the local 460 489 ** repository. 461 490 ** 462 491 ** Return true if a transfer was made and false if not. ................................................................................ 479 508 zCode 480 509 ); 481 510 if( zOtherRepo==0 ) return 0; /* No such peer repository */ 482 511 483 512 rc = sqlite3_open(zOtherRepo, &pOther); 484 513 if( rc==SQLITE_OK ){ 485 514 sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0); 515 + sqlite3_create_function(pOther, "constant_time_cmp", 2, SQLITE_UTF8, 0, 516 + constant_time_cmp_function, 0, 0); 486 517 sqlite3_busy_timeout(pOther, 5000); 487 518 zSQL = mprintf( 488 519 "SELECT cexpire FROM user" 489 - " WHERE cookie=%Q" 520 + " WHERE login=%Q" 490 521 " AND ipaddr=%Q" 491 - " AND login=%Q" 492 522 " AND length(cap)>0" 493 523 " AND length(pw)>0" 494 - " AND cexpire>julianday('now')", 495 - zHash, zRemoteAddr, zLogin 524 + " AND cexpire>julianday('now')" 525 + " AND constant_time_cmp(cookie,%Q)=0", 526 + zLogin, zRemoteAddr, zHash 496 527 ); 497 528 pStmt = 0; 498 529 rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); 499 530 if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ 500 531 db_multi_exec( 501 532 "UPDATE user SET cookie=%Q, ipaddr=%Q, cexpire=%.17g" 502 533 " WHERE login=%Q", ................................................................................ 525 556 if( fossil_strcmp(zLogin, "anonymous")==0 ) return 0; 526 557 if( fossil_strcmp(zLogin, "nobody")==0 ) return 0; 527 558 if( fossil_strcmp(zLogin, "developer")==0 ) return 0; 528 559 if( fossil_strcmp(zLogin, "reader")==0 ) return 0; 529 560 uid = db_int(0, 530 561 "SELECT uid FROM user" 531 562 " WHERE login=%Q" 532 - " AND cookie=%Q" 533 563 " AND ipaddr=%Q" 534 564 " AND cexpire>julianday('now')" 535 565 " AND length(cap)>0" 536 - " AND length(pw)>0", 537 - zLogin, zCookie, zRemoteAddr 566 + " AND length(pw)>0" 567 + " AND constant_time_cmp(cookie,%Q)=0", 568 + zLogin, zRemoteAddr, zCookie 538 569 ); 539 570 return uid; 540 571 } 541 572 542 573 /* 543 574 ** This routine examines the login cookie to see if it exists and 544 575 ** and is valid. If the login cookie checks out, it then sets ................................................................................ 552 583 const char *zCookie; /* Text of the login cookie */ 553 584 const char *zIpAddr; /* Raw IP address of the requestor */ 554 585 char *zRemoteAddr; /* Abbreviated IP address of the requestor */ 555 586 const char *zCap = 0; /* Capability string */ 556 587 557 588 /* Only run this check once. */ 558 589 if( g.userUid!=0 ) return; 590 + 591 + sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, 592 + constant_time_cmp_function, 0, 0); 559 593 560 594 /* If the HTTP connection is coming over 127.0.0.1 and if 561 595 ** local login is disabled and if we are using HTTP and not HTTPS, 562 596 ** then there is no need to check user credentials. 563 597 ** 564 598 ** This feature allows the "fossil ui" command to give the user 565 599 ** full access rights without having to log in.
Changes to src/xfer.c.
571 571 db_ephemeral_blob(&q, 0, &pw); 572 572 szPw = blob_size(&pw); 573 573 blob_zero(&combined); 574 574 blob_copy(&combined, pNonce); 575 575 blob_append(&combined, blob_buffer(&pw), szPw); 576 576 sha1sum_blob(&combined, &hash); 577 577 assert( blob_size(&hash)==40 ); 578 - rc = blob_compare(&hash, pSig); 578 + rc = blob_constant_time_cmp(&hash, pSig); 579 579 blob_reset(&hash); 580 580 blob_reset(&combined); 581 581 if( rc!=0 && szPw!=40 ){ 582 582 /* If this server stores cleartext passwords and the password did not 583 583 ** match, then perhaps the client is sending SHA1 passwords. Try 584 584 ** again with the SHA1 password. 585 585 */ ................................................................................ 586 586 const char *zPw = db_column_text(&q, 0); 587 587 char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin), 0); 588 588 blob_zero(&combined); 589 589 blob_copy(&combined, pNonce); 590 590 blob_append(&combined, zSecret, -1); 591 591 free(zSecret); 592 592 sha1sum_blob(&combined, &hash); 593 - rc = blob_compare(&hash, pSig); 593 + rc = blob_constant_time_cmp(&hash, pSig); 594 594 blob_reset(&hash); 595 595 blob_reset(&combined); 596 596 } 597 597 if( rc==0 ){ 598 598 const char *zCap; 599 599 zCap = db_column_text(&q, 1); 600 600 login_set_capabilities(zCap, 0);