Fossil SCM
merged and conflict-resolved trunk [c0274f996266aef].
Commit
4fbf77d4f371c1960fb4524be3098834a421103c
Parent
cee8bc672779570…
9 files changed
+26
+26
+1
-1
+1
-1
+43
-9
+43
-9
+60
+60
+2
-2
+26
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -311,10 +311,36 @@ | ||
| 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 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -311,10 +311,36 @@ | |
| 311 | sz = szA<szB ? szA : szB; |
| 312 | rc = memcmp(blob_buffer(pA), blob_buffer(pB), sz); |
| 313 | if( rc==0 ){ |
| 314 | rc = szA - szB; |
| 315 | } |
| 316 | return rc; |
| 317 | } |
| 318 | |
| 319 | /* |
| 320 | ** Compare a blob to a string. Return TRUE if they are equal. |
| 321 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -311,10 +311,36 @@ | |
| 311 | sz = szA<szB ? szA : szB; |
| 312 | rc = memcmp(blob_buffer(pA), blob_buffer(pB), sz); |
| 313 | if( rc==0 ){ |
| 314 | rc = szA - szB; |
| 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 | |
| 342 | return rc; |
| 343 | } |
| 344 | |
| 345 | /* |
| 346 | ** Compare a blob to a string. Return TRUE if they are equal. |
| 347 |
+26
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -311,10 +311,36 @@ | ||
| 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 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -311,10 +311,36 @@ | |
| 311 | sz = szA<szB ? szA : szB; |
| 312 | rc = memcmp(blob_buffer(pA), blob_buffer(pB), sz); |
| 313 | if( rc==0 ){ |
| 314 | rc = szA - szB; |
| 315 | } |
| 316 | return rc; |
| 317 | } |
| 318 | |
| 319 | /* |
| 320 | ** Compare a blob to a string. Return TRUE if they are equal. |
| 321 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -311,10 +311,36 @@ | |
| 311 | sz = szA<szB ? szA : szB; |
| 312 | rc = memcmp(blob_buffer(pA), blob_buffer(pB), sz); |
| 313 | if( rc==0 ){ |
| 314 | rc = szA - szB; |
| 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 | |
| 342 | return rc; |
| 343 | } |
| 344 | |
| 345 | /* |
| 346 | ** Compare a blob to a string. Return TRUE if they are equal. |
| 347 |
+1
-1
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -325,11 +325,11 @@ | ||
| 325 | 325 | */ |
| 326 | 326 | /*time_t expires = time(0) + atoi(db_config("constant_expires","604800"));*/ |
| 327 | 327 | time_t expires = time(0) + 604800; |
| 328 | 328 | fprintf(g.httpOut, "Expires: %s\r\n", cgi_rfc822_datestamp(expires)); |
| 329 | 329 | }else{ |
| 330 | - fprintf(g.httpOut, "Cache-control: no-cache, no-store\r\n"); | |
| 330 | + fprintf(g.httpOut, "Cache-control: no-cache\r\n"); | |
| 331 | 331 | } |
| 332 | 332 | |
| 333 | 333 | /* Content intended for logged in users should only be cached in |
| 334 | 334 | ** the browser, not some shared location. |
| 335 | 335 | */ |
| 336 | 336 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -325,11 +325,11 @@ | |
| 325 | */ |
| 326 | /*time_t expires = time(0) + atoi(db_config("constant_expires","604800"));*/ |
| 327 | time_t expires = time(0) + 604800; |
| 328 | fprintf(g.httpOut, "Expires: %s\r\n", cgi_rfc822_datestamp(expires)); |
| 329 | }else{ |
| 330 | fprintf(g.httpOut, "Cache-control: no-cache, no-store\r\n"); |
| 331 | } |
| 332 | |
| 333 | /* Content intended for logged in users should only be cached in |
| 334 | ** the browser, not some shared location. |
| 335 | */ |
| 336 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -325,11 +325,11 @@ | |
| 325 | */ |
| 326 | /*time_t expires = time(0) + atoi(db_config("constant_expires","604800"));*/ |
| 327 | time_t expires = time(0) + 604800; |
| 328 | fprintf(g.httpOut, "Expires: %s\r\n", cgi_rfc822_datestamp(expires)); |
| 329 | }else{ |
| 330 | fprintf(g.httpOut, "Cache-control: no-cache\r\n"); |
| 331 | } |
| 332 | |
| 333 | /* Content intended for logged in users should only be cached in |
| 334 | ** the browser, not some shared location. |
| 335 | */ |
| 336 |
+1
-1
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -325,11 +325,11 @@ | ||
| 325 | 325 | */ |
| 326 | 326 | /*time_t expires = time(0) + atoi(db_config("constant_expires","604800"));*/ |
| 327 | 327 | time_t expires = time(0) + 604800; |
| 328 | 328 | fprintf(g.httpOut, "Expires: %s\r\n", cgi_rfc822_datestamp(expires)); |
| 329 | 329 | }else{ |
| 330 | - fprintf(g.httpOut, "Cache-control: no-cache, no-store\r\n"); | |
| 330 | + fprintf(g.httpOut, "Cache-control: no-cache\r\n"); | |
| 331 | 331 | } |
| 332 | 332 | |
| 333 | 333 | /* Content intended for logged in users should only be cached in |
| 334 | 334 | ** the browser, not some shared location. |
| 335 | 335 | */ |
| 336 | 336 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -325,11 +325,11 @@ | |
| 325 | */ |
| 326 | /*time_t expires = time(0) + atoi(db_config("constant_expires","604800"));*/ |
| 327 | time_t expires = time(0) + 604800; |
| 328 | fprintf(g.httpOut, "Expires: %s\r\n", cgi_rfc822_datestamp(expires)); |
| 329 | }else{ |
| 330 | fprintf(g.httpOut, "Cache-control: no-cache, no-store\r\n"); |
| 331 | } |
| 332 | |
| 333 | /* Content intended for logged in users should only be cached in |
| 334 | ** the browser, not some shared location. |
| 335 | */ |
| 336 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -325,11 +325,11 @@ | |
| 325 | */ |
| 326 | /*time_t expires = time(0) + atoi(db_config("constant_expires","604800"));*/ |
| 327 | time_t expires = time(0) + 604800; |
| 328 | fprintf(g.httpOut, "Expires: %s\r\n", cgi_rfc822_datestamp(expires)); |
| 329 | }else{ |
| 330 | fprintf(g.httpOut, "Cache-control: no-cache\r\n"); |
| 331 | } |
| 332 | |
| 333 | /* Content intended for logged in users should only be cached in |
| 334 | ** the browser, not some shared location. |
| 335 | */ |
| 336 |
+43
-9
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -380,12 +380,14 @@ | ||
| 380 | 380 | } |
| 381 | 381 | if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ |
| 382 | 382 | /* The user requests a password change */ |
| 383 | 383 | zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); |
| 384 | 384 | if( db_int(1, "SELECT 0 FROM user" |
| 385 | - " WHERE uid=%d AND (pw=%Q OR pw=%Q)", | |
| 386 | - g.userUid, zPasswd, zSha1Pw) ){ | |
| 385 | + " WHERE uid=%d" | |
| 386 | + " AND (constant_time_cmp(pw,%Q)=0" | |
| 387 | + " OR constant_time_cmp(pw,%Q)=0)", | |
| 388 | + g.userUid, zSha1Pw, zPasswd) ){ | |
| 387 | 389 | sleep(1); |
| 388 | 390 | zErrMsg = |
| 389 | 391 | @ <p><span class="loginError"> |
| 390 | 392 | @ You entered an incorrect old password while attempting to change |
| 391 | 393 | @ your password. Your password is unchanged. |
| @@ -556,10 +558,37 @@ | ||
| 556 | 558 | @ </table> |
| 557 | 559 | @ </form> |
| 558 | 560 | } |
| 559 | 561 | style_footer(); |
| 560 | 562 | } |
| 563 | + | |
| 564 | +/* | |
| 565 | +** SQL function for constant time comparison of two values. | |
| 566 | +** Sets result to 0 if two values are equal. | |
| 567 | +*/ | |
| 568 | +static void constant_time_cmp_function( | |
| 569 | + sqlite3_context *context, | |
| 570 | + int argc, | |
| 571 | + sqlite3_value **argv | |
| 572 | +){ | |
| 573 | + const unsigned char *buf1, *buf2; | |
| 574 | + int len, i; | |
| 575 | + unsigned char rc = 0; | |
| 576 | + | |
| 577 | + assert( argc==2 ); | |
| 578 | + len = sqlite3_value_bytes(argv[0]); | |
| 579 | + if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){ | |
| 580 | + rc = 1; | |
| 581 | + }else{ | |
| 582 | + buf1 = sqlite3_value_text(argv[0]); | |
| 583 | + buf2 = sqlite3_value_text(argv[1]); | |
| 584 | + for( i=0; i<len; i++ ){ | |
| 585 | + rc = rc | (buf1[i] ^ buf2[i]); | |
| 586 | + } | |
| 587 | + } | |
| 588 | + sqlite3_result_int(context, rc); | |
| 589 | +} | |
| 561 | 590 | |
| 562 | 591 | /* |
| 563 | 592 | ** Attempt to find login credentials for user zLogin on a peer repository |
| 564 | 593 | ** with project code zCode. Transfer those credentials to the local |
| 565 | 594 | ** repository. |
| @@ -586,20 +615,22 @@ | ||
| 586 | 615 | if( zOtherRepo==0 ) return 0; /* No such peer repository */ |
| 587 | 616 | |
| 588 | 617 | rc = sqlite3_open(zOtherRepo, &pOther); |
| 589 | 618 | if( rc==SQLITE_OK ){ |
| 590 | 619 | sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0); |
| 620 | + sqlite3_create_function(pOther, "constant_time_cmp", 2, SQLITE_UTF8, 0, | |
| 621 | + constant_time_cmp_function, 0, 0); | |
| 591 | 622 | sqlite3_busy_timeout(pOther, 5000); |
| 592 | 623 | zSQL = mprintf( |
| 593 | 624 | "SELECT cexpire FROM user" |
| 594 | - " WHERE cookie=%Q" | |
| 625 | + " WHERE login=%Q" | |
| 595 | 626 | " AND ipaddr=%Q" |
| 596 | - " AND login=%Q" | |
| 597 | 627 | " AND length(cap)>0" |
| 598 | 628 | " AND length(pw)>0" |
| 599 | - " AND cexpire>julianday('now')", | |
| 600 | - zHash, zRemoteAddr, zLogin | |
| 629 | + " AND cexpire>julianday('now')" | |
| 630 | + " AND constant_time_cmp(cookie,%Q)=0", | |
| 631 | + zLogin, zRemoteAddr, zHash | |
| 601 | 632 | ); |
| 602 | 633 | pStmt = 0; |
| 603 | 634 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 604 | 635 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 605 | 636 | db_multi_exec( |
| @@ -636,16 +667,16 @@ | ||
| 636 | 667 | if( fossil_strcmp(zLogin, "developer")==0 ) return 0; |
| 637 | 668 | if( fossil_strcmp(zLogin, "reader")==0 ) return 0; |
| 638 | 669 | uid = db_int(0, |
| 639 | 670 | "SELECT uid FROM user" |
| 640 | 671 | " WHERE login=%Q" |
| 641 | - " AND cookie=%Q" | |
| 642 | 672 | " AND ipaddr=%Q" |
| 643 | 673 | " AND cexpire>julianday('now')" |
| 644 | 674 | " AND length(cap)>0" |
| 645 | - " AND length(pw)>0", | |
| 646 | - zLogin, zCookie, zRemoteAddr | |
| 675 | + " AND length(pw)>0" | |
| 676 | + " AND constant_time_cmp(cookie,%Q)=0", | |
| 677 | + zLogin, zRemoteAddr, zCookie | |
| 647 | 678 | ); |
| 648 | 679 | return uid; |
| 649 | 680 | } |
| 650 | 681 | |
| 651 | 682 | /* |
| @@ -661,10 +692,13 @@ | ||
| 661 | 692 | char *zRemoteAddr; /* Abbreviated IP address of the requestor */ |
| 662 | 693 | const char *zCap = 0; /* Capability string */ |
| 663 | 694 | |
| 664 | 695 | /* Only run this check once. */ |
| 665 | 696 | if( g.userUid!=0 ) return; |
| 697 | + | |
| 698 | + sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, | |
| 699 | + constant_time_cmp_function, 0, 0); | |
| 666 | 700 | |
| 667 | 701 | /* If the HTTP connection is coming over 127.0.0.1 and if |
| 668 | 702 | ** local login is disabled and if we are using HTTP and not HTTPS, |
| 669 | 703 | ** then there is no need to check user credentials. |
| 670 | 704 | ** |
| 671 | 705 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -380,12 +380,14 @@ | |
| 380 | } |
| 381 | if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ |
| 382 | /* The user requests a password change */ |
| 383 | zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); |
| 384 | if( db_int(1, "SELECT 0 FROM user" |
| 385 | " WHERE uid=%d AND (pw=%Q OR pw=%Q)", |
| 386 | g.userUid, zPasswd, zSha1Pw) ){ |
| 387 | sleep(1); |
| 388 | zErrMsg = |
| 389 | @ <p><span class="loginError"> |
| 390 | @ You entered an incorrect old password while attempting to change |
| 391 | @ your password. Your password is unchanged. |
| @@ -556,10 +558,37 @@ | |
| 556 | @ </table> |
| 557 | @ </form> |
| 558 | } |
| 559 | style_footer(); |
| 560 | } |
| 561 | |
| 562 | /* |
| 563 | ** Attempt to find login credentials for user zLogin on a peer repository |
| 564 | ** with project code zCode. Transfer those credentials to the local |
| 565 | ** repository. |
| @@ -586,20 +615,22 @@ | |
| 586 | if( zOtherRepo==0 ) return 0; /* No such peer repository */ |
| 587 | |
| 588 | rc = sqlite3_open(zOtherRepo, &pOther); |
| 589 | if( rc==SQLITE_OK ){ |
| 590 | sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0); |
| 591 | sqlite3_busy_timeout(pOther, 5000); |
| 592 | zSQL = mprintf( |
| 593 | "SELECT cexpire FROM user" |
| 594 | " WHERE cookie=%Q" |
| 595 | " AND ipaddr=%Q" |
| 596 | " AND login=%Q" |
| 597 | " AND length(cap)>0" |
| 598 | " AND length(pw)>0" |
| 599 | " AND cexpire>julianday('now')", |
| 600 | zHash, zRemoteAddr, zLogin |
| 601 | ); |
| 602 | pStmt = 0; |
| 603 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 604 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 605 | db_multi_exec( |
| @@ -636,16 +667,16 @@ | |
| 636 | if( fossil_strcmp(zLogin, "developer")==0 ) return 0; |
| 637 | if( fossil_strcmp(zLogin, "reader")==0 ) return 0; |
| 638 | uid = db_int(0, |
| 639 | "SELECT uid FROM user" |
| 640 | " WHERE login=%Q" |
| 641 | " AND cookie=%Q" |
| 642 | " AND ipaddr=%Q" |
| 643 | " AND cexpire>julianday('now')" |
| 644 | " AND length(cap)>0" |
| 645 | " AND length(pw)>0", |
| 646 | zLogin, zCookie, zRemoteAddr |
| 647 | ); |
| 648 | return uid; |
| 649 | } |
| 650 | |
| 651 | /* |
| @@ -661,10 +692,13 @@ | |
| 661 | char *zRemoteAddr; /* Abbreviated IP address of the requestor */ |
| 662 | const char *zCap = 0; /* Capability string */ |
| 663 | |
| 664 | /* Only run this check once. */ |
| 665 | if( g.userUid!=0 ) return; |
| 666 | |
| 667 | /* If the HTTP connection is coming over 127.0.0.1 and if |
| 668 | ** local login is disabled and if we are using HTTP and not HTTPS, |
| 669 | ** then there is no need to check user credentials. |
| 670 | ** |
| 671 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -380,12 +380,14 @@ | |
| 380 | } |
| 381 | if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ |
| 382 | /* The user requests a password change */ |
| 383 | zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); |
| 384 | if( db_int(1, "SELECT 0 FROM user" |
| 385 | " WHERE uid=%d" |
| 386 | " AND (constant_time_cmp(pw,%Q)=0" |
| 387 | " OR constant_time_cmp(pw,%Q)=0)", |
| 388 | g.userUid, zSha1Pw, zPasswd) ){ |
| 389 | sleep(1); |
| 390 | zErrMsg = |
| 391 | @ <p><span class="loginError"> |
| 392 | @ You entered an incorrect old password while attempting to change |
| 393 | @ your password. Your password is unchanged. |
| @@ -556,10 +558,37 @@ | |
| 558 | @ </table> |
| 559 | @ </form> |
| 560 | } |
| 561 | style_footer(); |
| 562 | } |
| 563 | |
| 564 | /* |
| 565 | ** SQL function for constant time comparison of two values. |
| 566 | ** Sets result to 0 if two values are equal. |
| 567 | */ |
| 568 | static void constant_time_cmp_function( |
| 569 | sqlite3_context *context, |
| 570 | int argc, |
| 571 | sqlite3_value **argv |
| 572 | ){ |
| 573 | const unsigned char *buf1, *buf2; |
| 574 | int len, i; |
| 575 | unsigned char rc = 0; |
| 576 | |
| 577 | assert( argc==2 ); |
| 578 | len = sqlite3_value_bytes(argv[0]); |
| 579 | if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){ |
| 580 | rc = 1; |
| 581 | }else{ |
| 582 | buf1 = sqlite3_value_text(argv[0]); |
| 583 | buf2 = sqlite3_value_text(argv[1]); |
| 584 | for( i=0; i<len; i++ ){ |
| 585 | rc = rc | (buf1[i] ^ buf2[i]); |
| 586 | } |
| 587 | } |
| 588 | sqlite3_result_int(context, rc); |
| 589 | } |
| 590 | |
| 591 | /* |
| 592 | ** Attempt to find login credentials for user zLogin on a peer repository |
| 593 | ** with project code zCode. Transfer those credentials to the local |
| 594 | ** repository. |
| @@ -586,20 +615,22 @@ | |
| 615 | if( zOtherRepo==0 ) return 0; /* No such peer repository */ |
| 616 | |
| 617 | rc = sqlite3_open(zOtherRepo, &pOther); |
| 618 | if( rc==SQLITE_OK ){ |
| 619 | sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0); |
| 620 | sqlite3_create_function(pOther, "constant_time_cmp", 2, SQLITE_UTF8, 0, |
| 621 | constant_time_cmp_function, 0, 0); |
| 622 | sqlite3_busy_timeout(pOther, 5000); |
| 623 | zSQL = mprintf( |
| 624 | "SELECT cexpire FROM user" |
| 625 | " WHERE login=%Q" |
| 626 | " AND ipaddr=%Q" |
| 627 | " AND length(cap)>0" |
| 628 | " AND length(pw)>0" |
| 629 | " AND cexpire>julianday('now')" |
| 630 | " AND constant_time_cmp(cookie,%Q)=0", |
| 631 | zLogin, zRemoteAddr, zHash |
| 632 | ); |
| 633 | pStmt = 0; |
| 634 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 635 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 636 | db_multi_exec( |
| @@ -636,16 +667,16 @@ | |
| 667 | if( fossil_strcmp(zLogin, "developer")==0 ) return 0; |
| 668 | if( fossil_strcmp(zLogin, "reader")==0 ) return 0; |
| 669 | uid = db_int(0, |
| 670 | "SELECT uid FROM user" |
| 671 | " WHERE login=%Q" |
| 672 | " AND ipaddr=%Q" |
| 673 | " AND cexpire>julianday('now')" |
| 674 | " AND length(cap)>0" |
| 675 | " AND length(pw)>0" |
| 676 | " AND constant_time_cmp(cookie,%Q)=0", |
| 677 | zLogin, zRemoteAddr, zCookie |
| 678 | ); |
| 679 | return uid; |
| 680 | } |
| 681 | |
| 682 | /* |
| @@ -661,10 +692,13 @@ | |
| 692 | char *zRemoteAddr; /* Abbreviated IP address of the requestor */ |
| 693 | const char *zCap = 0; /* Capability string */ |
| 694 | |
| 695 | /* Only run this check once. */ |
| 696 | if( g.userUid!=0 ) return; |
| 697 | |
| 698 | sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, |
| 699 | constant_time_cmp_function, 0, 0); |
| 700 | |
| 701 | /* If the HTTP connection is coming over 127.0.0.1 and if |
| 702 | ** local login is disabled and if we are using HTTP and not HTTPS, |
| 703 | ** then there is no need to check user credentials. |
| 704 | ** |
| 705 |
+43
-9
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -380,12 +380,14 @@ | ||
| 380 | 380 | } |
| 381 | 381 | if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ |
| 382 | 382 | /* The user requests a password change */ |
| 383 | 383 | zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); |
| 384 | 384 | if( db_int(1, "SELECT 0 FROM user" |
| 385 | - " WHERE uid=%d AND (pw=%Q OR pw=%Q)", | |
| 386 | - g.userUid, zPasswd, zSha1Pw) ){ | |
| 385 | + " WHERE uid=%d" | |
| 386 | + " AND (constant_time_cmp(pw,%Q)=0" | |
| 387 | + " OR constant_time_cmp(pw,%Q)=0)", | |
| 388 | + g.userUid, zSha1Pw, zPasswd) ){ | |
| 387 | 389 | sleep(1); |
| 388 | 390 | zErrMsg = |
| 389 | 391 | @ <p><span class="loginError"> |
| 390 | 392 | @ You entered an incorrect old password while attempting to change |
| 391 | 393 | @ your password. Your password is unchanged. |
| @@ -556,10 +558,37 @@ | ||
| 556 | 558 | @ </table> |
| 557 | 559 | @ </form> |
| 558 | 560 | } |
| 559 | 561 | style_footer(); |
| 560 | 562 | } |
| 563 | + | |
| 564 | +/* | |
| 565 | +** SQL function for constant time comparison of two values. | |
| 566 | +** Sets result to 0 if two values are equal. | |
| 567 | +*/ | |
| 568 | +static void constant_time_cmp_function( | |
| 569 | + sqlite3_context *context, | |
| 570 | + int argc, | |
| 571 | + sqlite3_value **argv | |
| 572 | +){ | |
| 573 | + const unsigned char *buf1, *buf2; | |
| 574 | + int len, i; | |
| 575 | + unsigned char rc = 0; | |
| 576 | + | |
| 577 | + assert( argc==2 ); | |
| 578 | + len = sqlite3_value_bytes(argv[0]); | |
| 579 | + if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){ | |
| 580 | + rc = 1; | |
| 581 | + }else{ | |
| 582 | + buf1 = sqlite3_value_text(argv[0]); | |
| 583 | + buf2 = sqlite3_value_text(argv[1]); | |
| 584 | + for( i=0; i<len; i++ ){ | |
| 585 | + rc = rc | (buf1[i] ^ buf2[i]); | |
| 586 | + } | |
| 587 | + } | |
| 588 | + sqlite3_result_int(context, rc); | |
| 589 | +} | |
| 561 | 590 | |
| 562 | 591 | /* |
| 563 | 592 | ** Attempt to find login credentials for user zLogin on a peer repository |
| 564 | 593 | ** with project code zCode. Transfer those credentials to the local |
| 565 | 594 | ** repository. |
| @@ -586,20 +615,22 @@ | ||
| 586 | 615 | if( zOtherRepo==0 ) return 0; /* No such peer repository */ |
| 587 | 616 | |
| 588 | 617 | rc = sqlite3_open(zOtherRepo, &pOther); |
| 589 | 618 | if( rc==SQLITE_OK ){ |
| 590 | 619 | sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0); |
| 620 | + sqlite3_create_function(pOther, "constant_time_cmp", 2, SQLITE_UTF8, 0, | |
| 621 | + constant_time_cmp_function, 0, 0); | |
| 591 | 622 | sqlite3_busy_timeout(pOther, 5000); |
| 592 | 623 | zSQL = mprintf( |
| 593 | 624 | "SELECT cexpire FROM user" |
| 594 | - " WHERE cookie=%Q" | |
| 625 | + " WHERE login=%Q" | |
| 595 | 626 | " AND ipaddr=%Q" |
| 596 | - " AND login=%Q" | |
| 597 | 627 | " AND length(cap)>0" |
| 598 | 628 | " AND length(pw)>0" |
| 599 | - " AND cexpire>julianday('now')", | |
| 600 | - zHash, zRemoteAddr, zLogin | |
| 629 | + " AND cexpire>julianday('now')" | |
| 630 | + " AND constant_time_cmp(cookie,%Q)=0", | |
| 631 | + zLogin, zRemoteAddr, zHash | |
| 601 | 632 | ); |
| 602 | 633 | pStmt = 0; |
| 603 | 634 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 604 | 635 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 605 | 636 | db_multi_exec( |
| @@ -636,16 +667,16 @@ | ||
| 636 | 667 | if( fossil_strcmp(zLogin, "developer")==0 ) return 0; |
| 637 | 668 | if( fossil_strcmp(zLogin, "reader")==0 ) return 0; |
| 638 | 669 | uid = db_int(0, |
| 639 | 670 | "SELECT uid FROM user" |
| 640 | 671 | " WHERE login=%Q" |
| 641 | - " AND cookie=%Q" | |
| 642 | 672 | " AND ipaddr=%Q" |
| 643 | 673 | " AND cexpire>julianday('now')" |
| 644 | 674 | " AND length(cap)>0" |
| 645 | - " AND length(pw)>0", | |
| 646 | - zLogin, zCookie, zRemoteAddr | |
| 675 | + " AND length(pw)>0" | |
| 676 | + " AND constant_time_cmp(cookie,%Q)=0", | |
| 677 | + zLogin, zRemoteAddr, zCookie | |
| 647 | 678 | ); |
| 648 | 679 | return uid; |
| 649 | 680 | } |
| 650 | 681 | |
| 651 | 682 | /* |
| @@ -661,10 +692,13 @@ | ||
| 661 | 692 | char *zRemoteAddr; /* Abbreviated IP address of the requestor */ |
| 662 | 693 | const char *zCap = 0; /* Capability string */ |
| 663 | 694 | |
| 664 | 695 | /* Only run this check once. */ |
| 665 | 696 | if( g.userUid!=0 ) return; |
| 697 | + | |
| 698 | + sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, | |
| 699 | + constant_time_cmp_function, 0, 0); | |
| 666 | 700 | |
| 667 | 701 | /* If the HTTP connection is coming over 127.0.0.1 and if |
| 668 | 702 | ** local login is disabled and if we are using HTTP and not HTTPS, |
| 669 | 703 | ** then there is no need to check user credentials. |
| 670 | 704 | ** |
| 671 | 705 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -380,12 +380,14 @@ | |
| 380 | } |
| 381 | if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ |
| 382 | /* The user requests a password change */ |
| 383 | zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); |
| 384 | if( db_int(1, "SELECT 0 FROM user" |
| 385 | " WHERE uid=%d AND (pw=%Q OR pw=%Q)", |
| 386 | g.userUid, zPasswd, zSha1Pw) ){ |
| 387 | sleep(1); |
| 388 | zErrMsg = |
| 389 | @ <p><span class="loginError"> |
| 390 | @ You entered an incorrect old password while attempting to change |
| 391 | @ your password. Your password is unchanged. |
| @@ -556,10 +558,37 @@ | |
| 556 | @ </table> |
| 557 | @ </form> |
| 558 | } |
| 559 | style_footer(); |
| 560 | } |
| 561 | |
| 562 | /* |
| 563 | ** Attempt to find login credentials for user zLogin on a peer repository |
| 564 | ** with project code zCode. Transfer those credentials to the local |
| 565 | ** repository. |
| @@ -586,20 +615,22 @@ | |
| 586 | if( zOtherRepo==0 ) return 0; /* No such peer repository */ |
| 587 | |
| 588 | rc = sqlite3_open(zOtherRepo, &pOther); |
| 589 | if( rc==SQLITE_OK ){ |
| 590 | sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0); |
| 591 | sqlite3_busy_timeout(pOther, 5000); |
| 592 | zSQL = mprintf( |
| 593 | "SELECT cexpire FROM user" |
| 594 | " WHERE cookie=%Q" |
| 595 | " AND ipaddr=%Q" |
| 596 | " AND login=%Q" |
| 597 | " AND length(cap)>0" |
| 598 | " AND length(pw)>0" |
| 599 | " AND cexpire>julianday('now')", |
| 600 | zHash, zRemoteAddr, zLogin |
| 601 | ); |
| 602 | pStmt = 0; |
| 603 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 604 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 605 | db_multi_exec( |
| @@ -636,16 +667,16 @@ | |
| 636 | if( fossil_strcmp(zLogin, "developer")==0 ) return 0; |
| 637 | if( fossil_strcmp(zLogin, "reader")==0 ) return 0; |
| 638 | uid = db_int(0, |
| 639 | "SELECT uid FROM user" |
| 640 | " WHERE login=%Q" |
| 641 | " AND cookie=%Q" |
| 642 | " AND ipaddr=%Q" |
| 643 | " AND cexpire>julianday('now')" |
| 644 | " AND length(cap)>0" |
| 645 | " AND length(pw)>0", |
| 646 | zLogin, zCookie, zRemoteAddr |
| 647 | ); |
| 648 | return uid; |
| 649 | } |
| 650 | |
| 651 | /* |
| @@ -661,10 +692,13 @@ | |
| 661 | char *zRemoteAddr; /* Abbreviated IP address of the requestor */ |
| 662 | const char *zCap = 0; /* Capability string */ |
| 663 | |
| 664 | /* Only run this check once. */ |
| 665 | if( g.userUid!=0 ) return; |
| 666 | |
| 667 | /* If the HTTP connection is coming over 127.0.0.1 and if |
| 668 | ** local login is disabled and if we are using HTTP and not HTTPS, |
| 669 | ** then there is no need to check user credentials. |
| 670 | ** |
| 671 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -380,12 +380,14 @@ | |
| 380 | } |
| 381 | if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ |
| 382 | /* The user requests a password change */ |
| 383 | zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); |
| 384 | if( db_int(1, "SELECT 0 FROM user" |
| 385 | " WHERE uid=%d" |
| 386 | " AND (constant_time_cmp(pw,%Q)=0" |
| 387 | " OR constant_time_cmp(pw,%Q)=0)", |
| 388 | g.userUid, zSha1Pw, zPasswd) ){ |
| 389 | sleep(1); |
| 390 | zErrMsg = |
| 391 | @ <p><span class="loginError"> |
| 392 | @ You entered an incorrect old password while attempting to change |
| 393 | @ your password. Your password is unchanged. |
| @@ -556,10 +558,37 @@ | |
| 558 | @ </table> |
| 559 | @ </form> |
| 560 | } |
| 561 | style_footer(); |
| 562 | } |
| 563 | |
| 564 | /* |
| 565 | ** SQL function for constant time comparison of two values. |
| 566 | ** Sets result to 0 if two values are equal. |
| 567 | */ |
| 568 | static void constant_time_cmp_function( |
| 569 | sqlite3_context *context, |
| 570 | int argc, |
| 571 | sqlite3_value **argv |
| 572 | ){ |
| 573 | const unsigned char *buf1, *buf2; |
| 574 | int len, i; |
| 575 | unsigned char rc = 0; |
| 576 | |
| 577 | assert( argc==2 ); |
| 578 | len = sqlite3_value_bytes(argv[0]); |
| 579 | if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){ |
| 580 | rc = 1; |
| 581 | }else{ |
| 582 | buf1 = sqlite3_value_text(argv[0]); |
| 583 | buf2 = sqlite3_value_text(argv[1]); |
| 584 | for( i=0; i<len; i++ ){ |
| 585 | rc = rc | (buf1[i] ^ buf2[i]); |
| 586 | } |
| 587 | } |
| 588 | sqlite3_result_int(context, rc); |
| 589 | } |
| 590 | |
| 591 | /* |
| 592 | ** Attempt to find login credentials for user zLogin on a peer repository |
| 593 | ** with project code zCode. Transfer those credentials to the local |
| 594 | ** repository. |
| @@ -586,20 +615,22 @@ | |
| 615 | if( zOtherRepo==0 ) return 0; /* No such peer repository */ |
| 616 | |
| 617 | rc = sqlite3_open(zOtherRepo, &pOther); |
| 618 | if( rc==SQLITE_OK ){ |
| 619 | sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0); |
| 620 | sqlite3_create_function(pOther, "constant_time_cmp", 2, SQLITE_UTF8, 0, |
| 621 | constant_time_cmp_function, 0, 0); |
| 622 | sqlite3_busy_timeout(pOther, 5000); |
| 623 | zSQL = mprintf( |
| 624 | "SELECT cexpire FROM user" |
| 625 | " WHERE login=%Q" |
| 626 | " AND ipaddr=%Q" |
| 627 | " AND length(cap)>0" |
| 628 | " AND length(pw)>0" |
| 629 | " AND cexpire>julianday('now')" |
| 630 | " AND constant_time_cmp(cookie,%Q)=0", |
| 631 | zLogin, zRemoteAddr, zHash |
| 632 | ); |
| 633 | pStmt = 0; |
| 634 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 635 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 636 | db_multi_exec( |
| @@ -636,16 +667,16 @@ | |
| 667 | if( fossil_strcmp(zLogin, "developer")==0 ) return 0; |
| 668 | if( fossil_strcmp(zLogin, "reader")==0 ) return 0; |
| 669 | uid = db_int(0, |
| 670 | "SELECT uid FROM user" |
| 671 | " WHERE login=%Q" |
| 672 | " AND ipaddr=%Q" |
| 673 | " AND cexpire>julianday('now')" |
| 674 | " AND length(cap)>0" |
| 675 | " AND length(pw)>0" |
| 676 | " AND constant_time_cmp(cookie,%Q)=0", |
| 677 | zLogin, zRemoteAddr, zCookie |
| 678 | ); |
| 679 | return uid; |
| 680 | } |
| 681 | |
| 682 | /* |
| @@ -661,10 +692,13 @@ | |
| 692 | char *zRemoteAddr; /* Abbreviated IP address of the requestor */ |
| 693 | const char *zCap = 0; /* Capability string */ |
| 694 | |
| 695 | /* Only run this check once. */ |
| 696 | if( g.userUid!=0 ) return; |
| 697 | |
| 698 | sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, |
| 699 | constant_time_cmp_function, 0, 0); |
| 700 | |
| 701 | /* If the HTTP connection is coming over 127.0.0.1 and if |
| 702 | ** local login is disabled and if we are using HTTP and not HTTPS, |
| 703 | ** then there is no need to check user credentials. |
| 704 | ** |
| 705 |
+60
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -298,10 +298,67 @@ | ||
| 298 | 298 | memset(&g.json, 0, sizeof(g.json)); |
| 299 | 299 | if(g.db){ |
| 300 | 300 | db_close(0); |
| 301 | 301 | } |
| 302 | 302 | } |
| 303 | + | |
| 304 | +/* | |
| 305 | +** Search g.argv for arguments "--args FILENAME". If found, then | |
| 306 | +** (1) remove the two arguments from g.argv | |
| 307 | +** (2) Read the file FILENAME | |
| 308 | +** (3) Use the contents of FILE to replace the two removed arguments: | |
| 309 | +** (a) Ignore blank lines in the file | |
| 310 | +** (b) Each non-empty line of the file is an argument, except | |
| 311 | +** (c) If the line begins with "-" and contains a space, it is broken | |
| 312 | +** into two arguments at the space. | |
| 313 | +*/ | |
| 314 | +static void expand_args_option(void){ | |
| 315 | + Blob file = empty_blob; /* Content of the file */ | |
| 316 | + Blob line = empty_blob; /* One line of the file */ | |
| 317 | + unsigned int nLine; /* Number of lines in the file*/ | |
| 318 | + unsigned int i, j, k; /* Loop counters */ | |
| 319 | + int n; /* Number of bytes in one line */ | |
| 320 | + char *z; /* General use string pointer */ | |
| 321 | + char **newArgv; /* New expanded g.argv under construction */ | |
| 322 | + | |
| 323 | + for(i=1; i<g.argc-1; i++){ | |
| 324 | + z = g.argv[i]; | |
| 325 | + if( z[0]!='-' ) continue; | |
| 326 | + z++; | |
| 327 | + if( z[0]=='-' ) z++; | |
| 328 | + if( z[0]==0 ) return; /* Stop searching at "--" */ | |
| 329 | + if( fossil_strcmp(z, "args")==0 ) break; | |
| 330 | + } | |
| 331 | + if( i>=g.argc-1 ) return; | |
| 332 | + | |
| 333 | + blob_read_from_file(&file, g.argv[i+1]); | |
| 334 | + z = blob_str(&file); | |
| 335 | + for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++; | |
| 336 | + newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) ); | |
| 337 | + for(j=0; j<i; j++) newArgv[j] = g.argv[j]; | |
| 338 | + | |
| 339 | + blob_rewind(&file); | |
| 340 | + while( (n = blob_line(&file, &line))>0 ){ | |
| 341 | + if( n<=1 ) continue; | |
| 342 | + z = blob_buffer(&line); | |
| 343 | + z[n-1] = 0; | |
| 344 | + newArgv[j++] = z; | |
| 345 | + if( z[0]=='-' ){ | |
| 346 | + for(k=1; z[k] && !fossil_isspace(z[k]); k++){} | |
| 347 | + if( z[k] ){ | |
| 348 | + z[k] = 0; | |
| 349 | + k++; | |
| 350 | + if( z[k] ) newArgv[j++] = &z[k]; | |
| 351 | + } | |
| 352 | + } | |
| 353 | + } | |
| 354 | + i += 2; | |
| 355 | + while( i<g.argc ) newArgv[j++] = g.argv[i++]; | |
| 356 | + newArgv[j] = 0; | |
| 357 | + g.argc = j; | |
| 358 | + g.argv = newArgv; | |
| 359 | +} | |
| 303 | 360 | |
| 304 | 361 | /* |
| 305 | 362 | ** This procedure runs first. |
| 306 | 363 | */ |
| 307 | 364 | int main(int argc, char **argv){ |
| @@ -324,10 +381,13 @@ | ||
| 324 | 381 | g.json.errorDetailParanoia = 0; |
| 325 | 382 | #endif |
| 326 | 383 | g.json.outOpt = cson_output_opt_empty; |
| 327 | 384 | g.json.outOpt.addNewline = 1; |
| 328 | 385 | g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */; |
| 386 | + expand_args_option(); | |
| 387 | + argc = g.argc; | |
| 388 | + argv = g.argv; | |
| 329 | 389 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 330 | 390 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 331 | 391 | zCmdName = "cgi"; |
| 332 | 392 | g.isHTTP = 1; |
| 333 | 393 | }else if( argc<2 ){ |
| 334 | 394 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -298,10 +298,67 @@ | |
| 298 | memset(&g.json, 0, sizeof(g.json)); |
| 299 | if(g.db){ |
| 300 | db_close(0); |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | /* |
| 305 | ** This procedure runs first. |
| 306 | */ |
| 307 | int main(int argc, char **argv){ |
| @@ -324,10 +381,13 @@ | |
| 324 | g.json.errorDetailParanoia = 0; |
| 325 | #endif |
| 326 | g.json.outOpt = cson_output_opt_empty; |
| 327 | g.json.outOpt.addNewline = 1; |
| 328 | g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */; |
| 329 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 330 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 331 | zCmdName = "cgi"; |
| 332 | g.isHTTP = 1; |
| 333 | }else if( argc<2 ){ |
| 334 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -298,10 +298,67 @@ | |
| 298 | memset(&g.json, 0, sizeof(g.json)); |
| 299 | if(g.db){ |
| 300 | db_close(0); |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | /* |
| 305 | ** Search g.argv for arguments "--args FILENAME". If found, then |
| 306 | ** (1) remove the two arguments from g.argv |
| 307 | ** (2) Read the file FILENAME |
| 308 | ** (3) Use the contents of FILE to replace the two removed arguments: |
| 309 | ** (a) Ignore blank lines in the file |
| 310 | ** (b) Each non-empty line of the file is an argument, except |
| 311 | ** (c) If the line begins with "-" and contains a space, it is broken |
| 312 | ** into two arguments at the space. |
| 313 | */ |
| 314 | static void expand_args_option(void){ |
| 315 | Blob file = empty_blob; /* Content of the file */ |
| 316 | Blob line = empty_blob; /* One line of the file */ |
| 317 | unsigned int nLine; /* Number of lines in the file*/ |
| 318 | unsigned int i, j, k; /* Loop counters */ |
| 319 | int n; /* Number of bytes in one line */ |
| 320 | char *z; /* General use string pointer */ |
| 321 | char **newArgv; /* New expanded g.argv under construction */ |
| 322 | |
| 323 | for(i=1; i<g.argc-1; i++){ |
| 324 | z = g.argv[i]; |
| 325 | if( z[0]!='-' ) continue; |
| 326 | z++; |
| 327 | if( z[0]=='-' ) z++; |
| 328 | if( z[0]==0 ) return; /* Stop searching at "--" */ |
| 329 | if( fossil_strcmp(z, "args")==0 ) break; |
| 330 | } |
| 331 | if( i>=g.argc-1 ) return; |
| 332 | |
| 333 | blob_read_from_file(&file, g.argv[i+1]); |
| 334 | z = blob_str(&file); |
| 335 | for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++; |
| 336 | newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) ); |
| 337 | for(j=0; j<i; j++) newArgv[j] = g.argv[j]; |
| 338 | |
| 339 | blob_rewind(&file); |
| 340 | while( (n = blob_line(&file, &line))>0 ){ |
| 341 | if( n<=1 ) continue; |
| 342 | z = blob_buffer(&line); |
| 343 | z[n-1] = 0; |
| 344 | newArgv[j++] = z; |
| 345 | if( z[0]=='-' ){ |
| 346 | for(k=1; z[k] && !fossil_isspace(z[k]); k++){} |
| 347 | if( z[k] ){ |
| 348 | z[k] = 0; |
| 349 | k++; |
| 350 | if( z[k] ) newArgv[j++] = &z[k]; |
| 351 | } |
| 352 | } |
| 353 | } |
| 354 | i += 2; |
| 355 | while( i<g.argc ) newArgv[j++] = g.argv[i++]; |
| 356 | newArgv[j] = 0; |
| 357 | g.argc = j; |
| 358 | g.argv = newArgv; |
| 359 | } |
| 360 | |
| 361 | /* |
| 362 | ** This procedure runs first. |
| 363 | */ |
| 364 | int main(int argc, char **argv){ |
| @@ -324,10 +381,13 @@ | |
| 381 | g.json.errorDetailParanoia = 0; |
| 382 | #endif |
| 383 | g.json.outOpt = cson_output_opt_empty; |
| 384 | g.json.outOpt.addNewline = 1; |
| 385 | g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */; |
| 386 | expand_args_option(); |
| 387 | argc = g.argc; |
| 388 | argv = g.argv; |
| 389 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 390 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 391 | zCmdName = "cgi"; |
| 392 | g.isHTTP = 1; |
| 393 | }else if( argc<2 ){ |
| 394 |
+60
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -298,10 +298,67 @@ | ||
| 298 | 298 | memset(&g.json, 0, sizeof(g.json)); |
| 299 | 299 | if(g.db){ |
| 300 | 300 | db_close(0); |
| 301 | 301 | } |
| 302 | 302 | } |
| 303 | + | |
| 304 | +/* | |
| 305 | +** Search g.argv for arguments "--args FILENAME". If found, then | |
| 306 | +** (1) remove the two arguments from g.argv | |
| 307 | +** (2) Read the file FILENAME | |
| 308 | +** (3) Use the contents of FILE to replace the two removed arguments: | |
| 309 | +** (a) Ignore blank lines in the file | |
| 310 | +** (b) Each non-empty line of the file is an argument, except | |
| 311 | +** (c) If the line begins with "-" and contains a space, it is broken | |
| 312 | +** into two arguments at the space. | |
| 313 | +*/ | |
| 314 | +static void expand_args_option(void){ | |
| 315 | + Blob file = empty_blob; /* Content of the file */ | |
| 316 | + Blob line = empty_blob; /* One line of the file */ | |
| 317 | + unsigned int nLine; /* Number of lines in the file*/ | |
| 318 | + unsigned int i, j, k; /* Loop counters */ | |
| 319 | + int n; /* Number of bytes in one line */ | |
| 320 | + char *z; /* General use string pointer */ | |
| 321 | + char **newArgv; /* New expanded g.argv under construction */ | |
| 322 | + | |
| 323 | + for(i=1; i<g.argc-1; i++){ | |
| 324 | + z = g.argv[i]; | |
| 325 | + if( z[0]!='-' ) continue; | |
| 326 | + z++; | |
| 327 | + if( z[0]=='-' ) z++; | |
| 328 | + if( z[0]==0 ) return; /* Stop searching at "--" */ | |
| 329 | + if( fossil_strcmp(z, "args")==0 ) break; | |
| 330 | + } | |
| 331 | + if( i>=g.argc-1 ) return; | |
| 332 | + | |
| 333 | + blob_read_from_file(&file, g.argv[i+1]); | |
| 334 | + z = blob_str(&file); | |
| 335 | + for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++; | |
| 336 | + newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) ); | |
| 337 | + for(j=0; j<i; j++) newArgv[j] = g.argv[j]; | |
| 338 | + | |
| 339 | + blob_rewind(&file); | |
| 340 | + while( (n = blob_line(&file, &line))>0 ){ | |
| 341 | + if( n<=1 ) continue; | |
| 342 | + z = blob_buffer(&line); | |
| 343 | + z[n-1] = 0; | |
| 344 | + newArgv[j++] = z; | |
| 345 | + if( z[0]=='-' ){ | |
| 346 | + for(k=1; z[k] && !fossil_isspace(z[k]); k++){} | |
| 347 | + if( z[k] ){ | |
| 348 | + z[k] = 0; | |
| 349 | + k++; | |
| 350 | + if( z[k] ) newArgv[j++] = &z[k]; | |
| 351 | + } | |
| 352 | + } | |
| 353 | + } | |
| 354 | + i += 2; | |
| 355 | + while( i<g.argc ) newArgv[j++] = g.argv[i++]; | |
| 356 | + newArgv[j] = 0; | |
| 357 | + g.argc = j; | |
| 358 | + g.argv = newArgv; | |
| 359 | +} | |
| 303 | 360 | |
| 304 | 361 | /* |
| 305 | 362 | ** This procedure runs first. |
| 306 | 363 | */ |
| 307 | 364 | int main(int argc, char **argv){ |
| @@ -324,10 +381,13 @@ | ||
| 324 | 381 | g.json.errorDetailParanoia = 0; |
| 325 | 382 | #endif |
| 326 | 383 | g.json.outOpt = cson_output_opt_empty; |
| 327 | 384 | g.json.outOpt.addNewline = 1; |
| 328 | 385 | g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */; |
| 386 | + expand_args_option(); | |
| 387 | + argc = g.argc; | |
| 388 | + argv = g.argv; | |
| 329 | 389 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 330 | 390 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 331 | 391 | zCmdName = "cgi"; |
| 332 | 392 | g.isHTTP = 1; |
| 333 | 393 | }else if( argc<2 ){ |
| 334 | 394 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -298,10 +298,67 @@ | |
| 298 | memset(&g.json, 0, sizeof(g.json)); |
| 299 | if(g.db){ |
| 300 | db_close(0); |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | /* |
| 305 | ** This procedure runs first. |
| 306 | */ |
| 307 | int main(int argc, char **argv){ |
| @@ -324,10 +381,13 @@ | |
| 324 | g.json.errorDetailParanoia = 0; |
| 325 | #endif |
| 326 | g.json.outOpt = cson_output_opt_empty; |
| 327 | g.json.outOpt.addNewline = 1; |
| 328 | g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */; |
| 329 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 330 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 331 | zCmdName = "cgi"; |
| 332 | g.isHTTP = 1; |
| 333 | }else if( argc<2 ){ |
| 334 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -298,10 +298,67 @@ | |
| 298 | memset(&g.json, 0, sizeof(g.json)); |
| 299 | if(g.db){ |
| 300 | db_close(0); |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | /* |
| 305 | ** Search g.argv for arguments "--args FILENAME". If found, then |
| 306 | ** (1) remove the two arguments from g.argv |
| 307 | ** (2) Read the file FILENAME |
| 308 | ** (3) Use the contents of FILE to replace the two removed arguments: |
| 309 | ** (a) Ignore blank lines in the file |
| 310 | ** (b) Each non-empty line of the file is an argument, except |
| 311 | ** (c) If the line begins with "-" and contains a space, it is broken |
| 312 | ** into two arguments at the space. |
| 313 | */ |
| 314 | static void expand_args_option(void){ |
| 315 | Blob file = empty_blob; /* Content of the file */ |
| 316 | Blob line = empty_blob; /* One line of the file */ |
| 317 | unsigned int nLine; /* Number of lines in the file*/ |
| 318 | unsigned int i, j, k; /* Loop counters */ |
| 319 | int n; /* Number of bytes in one line */ |
| 320 | char *z; /* General use string pointer */ |
| 321 | char **newArgv; /* New expanded g.argv under construction */ |
| 322 | |
| 323 | for(i=1; i<g.argc-1; i++){ |
| 324 | z = g.argv[i]; |
| 325 | if( z[0]!='-' ) continue; |
| 326 | z++; |
| 327 | if( z[0]=='-' ) z++; |
| 328 | if( z[0]==0 ) return; /* Stop searching at "--" */ |
| 329 | if( fossil_strcmp(z, "args")==0 ) break; |
| 330 | } |
| 331 | if( i>=g.argc-1 ) return; |
| 332 | |
| 333 | blob_read_from_file(&file, g.argv[i+1]); |
| 334 | z = blob_str(&file); |
| 335 | for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++; |
| 336 | newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) ); |
| 337 | for(j=0; j<i; j++) newArgv[j] = g.argv[j]; |
| 338 | |
| 339 | blob_rewind(&file); |
| 340 | while( (n = blob_line(&file, &line))>0 ){ |
| 341 | if( n<=1 ) continue; |
| 342 | z = blob_buffer(&line); |
| 343 | z[n-1] = 0; |
| 344 | newArgv[j++] = z; |
| 345 | if( z[0]=='-' ){ |
| 346 | for(k=1; z[k] && !fossil_isspace(z[k]); k++){} |
| 347 | if( z[k] ){ |
| 348 | z[k] = 0; |
| 349 | k++; |
| 350 | if( z[k] ) newArgv[j++] = &z[k]; |
| 351 | } |
| 352 | } |
| 353 | } |
| 354 | i += 2; |
| 355 | while( i<g.argc ) newArgv[j++] = g.argv[i++]; |
| 356 | newArgv[j] = 0; |
| 357 | g.argc = j; |
| 358 | g.argv = newArgv; |
| 359 | } |
| 360 | |
| 361 | /* |
| 362 | ** This procedure runs first. |
| 363 | */ |
| 364 | int main(int argc, char **argv){ |
| @@ -324,10 +381,13 @@ | |
| 381 | g.json.errorDetailParanoia = 0; |
| 382 | #endif |
| 383 | g.json.outOpt = cson_output_opt_empty; |
| 384 | g.json.outOpt.addNewline = 1; |
| 385 | g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */; |
| 386 | expand_args_option(); |
| 387 | argc = g.argc; |
| 388 | argv = g.argv; |
| 389 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 390 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 391 | zCmdName = "cgi"; |
| 392 | g.isHTTP = 1; |
| 393 | }else if( argc<2 ){ |
| 394 |
+2
-2
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -573,11 +573,11 @@ | ||
| 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 |
| @@ -588,11 +588,11 @@ | ||
| 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 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -573,11 +573,11 @@ | |
| 573 | blob_zero(&combined); |
| 574 | blob_copy(&combined, pNonce); |
| 575 | blob_append(&combined, blob_buffer(&pw), szPw); |
| 576 | sha1sum_blob(&combined, &hash); |
| 577 | assert( blob_size(&hash)==40 ); |
| 578 | rc = blob_compare(&hash, pSig); |
| 579 | blob_reset(&hash); |
| 580 | blob_reset(&combined); |
| 581 | if( rc!=0 && szPw!=40 ){ |
| 582 | /* If this server stores cleartext passwords and the password did not |
| 583 | ** match, then perhaps the client is sending SHA1 passwords. Try |
| @@ -588,11 +588,11 @@ | |
| 588 | blob_zero(&combined); |
| 589 | blob_copy(&combined, pNonce); |
| 590 | blob_append(&combined, zSecret, -1); |
| 591 | free(zSecret); |
| 592 | sha1sum_blob(&combined, &hash); |
| 593 | rc = blob_compare(&hash, pSig); |
| 594 | blob_reset(&hash); |
| 595 | blob_reset(&combined); |
| 596 | } |
| 597 | if( rc==0 ){ |
| 598 | const char *zCap; |
| 599 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -573,11 +573,11 @@ | |
| 573 | blob_zero(&combined); |
| 574 | blob_copy(&combined, pNonce); |
| 575 | blob_append(&combined, blob_buffer(&pw), szPw); |
| 576 | sha1sum_blob(&combined, &hash); |
| 577 | assert( blob_size(&hash)==40 ); |
| 578 | rc = blob_constant_time_cmp(&hash, pSig); |
| 579 | blob_reset(&hash); |
| 580 | blob_reset(&combined); |
| 581 | if( rc!=0 && szPw!=40 ){ |
| 582 | /* If this server stores cleartext passwords and the password did not |
| 583 | ** match, then perhaps the client is sending SHA1 passwords. Try |
| @@ -588,11 +588,11 @@ | |
| 588 | blob_zero(&combined); |
| 589 | blob_copy(&combined, pNonce); |
| 590 | blob_append(&combined, zSecret, -1); |
| 591 | free(zSecret); |
| 592 | sha1sum_blob(&combined, &hash); |
| 593 | rc = blob_constant_time_cmp(&hash, pSig); |
| 594 | blob_reset(&hash); |
| 595 | blob_reset(&combined); |
| 596 | } |
| 597 | if( rc==0 ){ |
| 598 | const char *zCap; |
| 599 |