Fossil SCM
Add the ability to rotate the captcha-secret, setting up a new secret that is common to all members of a login-group.
Commit
acfaf4e48e776d84222cdad6cda8dbb4b39c49607494de5057b7e744173dc697
Parent
8659d84aff28747…
2 files changed
+48
+6
-1
+48
| --- src/captcha.c | ||
| +++ src/captcha.c | ||
| @@ -508,10 +508,58 @@ | ||
| 508 | 508 | unsigned int x; |
| 509 | 509 | sqlite3_randomness(sizeof(x), &x); |
| 510 | 510 | x &= 0x7fffffff; |
| 511 | 511 | return x; |
| 512 | 512 | } |
| 513 | + | |
| 514 | +/* The SQL that will rotate the the captcha-secret. */ | |
| 515 | +static const char captchaSecretRotationSql[] = | |
| 516 | +@ SAVEPOINT rotate; | |
| 517 | +@ DELETE FROM config | |
| 518 | +@ WHERE name GLOB 'captcha-secret-*' | |
| 519 | +@ AND mtime<unixepoch('now','-6 hours'); | |
| 520 | +@ UPDATE config | |
| 521 | +@ SET name=format('captcha-secret-%%d',substr(name,16)+1) | |
| 522 | +@ WHERE name GLOB 'captcha-secret-*'; | |
| 523 | +@ UPDATE config | |
| 524 | +@ SET name='captcha-secret-1', mtime=unixepoch() | |
| 525 | +@ WHERE name='captcha-secret'; | |
| 526 | +@ REPLACE INTO config(name,value,mtime) | |
| 527 | +@ VALUES('captcha-secret',%Q,unixepoch()); | |
| 528 | +@ RELEASE rotate; | |
| 529 | +; | |
| 530 | + | |
| 531 | + | |
| 532 | +/* | |
| 533 | +** Create a new random captcha-secret. Rotate the old one into | |
| 534 | +** the captcha-secret-N backups. Purge captch-secret-N backups | |
| 535 | +** older than 6 hours. | |
| 536 | +** | |
| 537 | +** Do this on the current database and in all other databases of | |
| 538 | +** the same login group. | |
| 539 | +*/ | |
| 540 | +void captcha_secret_rotate(void){ | |
| 541 | + char *zNew = db_text(0, "SELECT lower(hex(randomblob(20)))"); | |
| 542 | + char *zSql = mprintf(captchaSecretRotationSql/*works-like:"%Q"*/, zNew); | |
| 543 | + char *zErrs = 0; | |
| 544 | + fossil_free(zNew); | |
| 545 | + db_unprotect(PROTECT_CONFIG); | |
| 546 | + db_begin_transaction(); | |
| 547 | + sqlite3_exec(g.db, zSql, 0, 0, &zErrs); | |
| 548 | + db_protect_pop(); | |
| 549 | + if( zErrs && zErrs[0] ){ | |
| 550 | + db_rollback_transaction(); | |
| 551 | + fossil_fatal("Unable to rotate captcha-secret\n%s\nERROR: %s\n", | |
| 552 | + zSql, zErrs); | |
| 553 | + } | |
| 554 | + db_end_transaction(0); | |
| 555 | + login_group_sql(zSql, "", "", &zErrs); | |
| 556 | + if( zErrs ){ | |
| 557 | + sqlite3_free(zErrs); /* Silently ignore errors on other repos */ | |
| 558 | + } | |
| 559 | + fossil_free(zSql); | |
| 560 | +} | |
| 513 | 561 | |
| 514 | 562 | /* |
| 515 | 563 | ** Return the value of the N-th more recent captcha-secret. The |
| 516 | 564 | ** most recent captch-secret is 0. Others are prior captcha-secrets |
| 517 | 565 | ** that have expired, but are retained for a limited period of time |
| 518 | 566 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -508,10 +508,58 @@ | |
| 508 | unsigned int x; |
| 509 | sqlite3_randomness(sizeof(x), &x); |
| 510 | x &= 0x7fffffff; |
| 511 | return x; |
| 512 | } |
| 513 | |
| 514 | /* |
| 515 | ** Return the value of the N-th more recent captcha-secret. The |
| 516 | ** most recent captch-secret is 0. Others are prior captcha-secrets |
| 517 | ** that have expired, but are retained for a limited period of time |
| 518 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -508,10 +508,58 @@ | |
| 508 | unsigned int x; |
| 509 | sqlite3_randomness(sizeof(x), &x); |
| 510 | x &= 0x7fffffff; |
| 511 | return x; |
| 512 | } |
| 513 | |
| 514 | /* The SQL that will rotate the the captcha-secret. */ |
| 515 | static const char captchaSecretRotationSql[] = |
| 516 | @ SAVEPOINT rotate; |
| 517 | @ DELETE FROM config |
| 518 | @ WHERE name GLOB 'captcha-secret-*' |
| 519 | @ AND mtime<unixepoch('now','-6 hours'); |
| 520 | @ UPDATE config |
| 521 | @ SET name=format('captcha-secret-%%d',substr(name,16)+1) |
| 522 | @ WHERE name GLOB 'captcha-secret-*'; |
| 523 | @ UPDATE config |
| 524 | @ SET name='captcha-secret-1', mtime=unixepoch() |
| 525 | @ WHERE name='captcha-secret'; |
| 526 | @ REPLACE INTO config(name,value,mtime) |
| 527 | @ VALUES('captcha-secret',%Q,unixepoch()); |
| 528 | @ RELEASE rotate; |
| 529 | ; |
| 530 | |
| 531 | |
| 532 | /* |
| 533 | ** Create a new random captcha-secret. Rotate the old one into |
| 534 | ** the captcha-secret-N backups. Purge captch-secret-N backups |
| 535 | ** older than 6 hours. |
| 536 | ** |
| 537 | ** Do this on the current database and in all other databases of |
| 538 | ** the same login group. |
| 539 | */ |
| 540 | void captcha_secret_rotate(void){ |
| 541 | char *zNew = db_text(0, "SELECT lower(hex(randomblob(20)))"); |
| 542 | char *zSql = mprintf(captchaSecretRotationSql/*works-like:"%Q"*/, zNew); |
| 543 | char *zErrs = 0; |
| 544 | fossil_free(zNew); |
| 545 | db_unprotect(PROTECT_CONFIG); |
| 546 | db_begin_transaction(); |
| 547 | sqlite3_exec(g.db, zSql, 0, 0, &zErrs); |
| 548 | db_protect_pop(); |
| 549 | if( zErrs && zErrs[0] ){ |
| 550 | db_rollback_transaction(); |
| 551 | fossil_fatal("Unable to rotate captcha-secret\n%s\nERROR: %s\n", |
| 552 | zSql, zErrs); |
| 553 | } |
| 554 | db_end_transaction(0); |
| 555 | login_group_sql(zSql, "", "", &zErrs); |
| 556 | if( zErrs ){ |
| 557 | sqlite3_free(zErrs); /* Silently ignore errors on other repos */ |
| 558 | } |
| 559 | fossil_free(zSql); |
| 560 | } |
| 561 | |
| 562 | /* |
| 563 | ** Return the value of the N-th more recent captcha-secret. The |
| 564 | ** most recent captch-secret is 0. Others are prior captcha-secrets |
| 565 | ** that have expired, but are retained for a limited period of time |
| 566 |
+6
-1
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -780,10 +780,12 @@ | ||
| 780 | 780 | blob_reset(&fullName); |
| 781 | 781 | if( P("join")!=0 ){ |
| 782 | 782 | login_group_join(zRepo, 1, zLogin, zPw, zNewName, &zErrMsg); |
| 783 | 783 | }else if( P("leave") ){ |
| 784 | 784 | login_group_leave(&zErrMsg); |
| 785 | + }else if( P("rotate") ){ | |
| 786 | + captcha_secret_rotate(); | |
| 785 | 787 | } |
| 786 | 788 | style_set_current_feature("setup"); |
| 787 | 789 | style_header("Login Group Configuration"); |
| 788 | 790 | if( zErrMsg ){ |
| 789 | 791 | @ <p class="generalError">%s(zErrMsg)</p> |
| @@ -849,12 +851,15 @@ | ||
| 849 | 851 | db_finalize(&q); |
| 850 | 852 | @ </table> |
| 851 | 853 | @ |
| 852 | 854 | @ <p><form action="%R/setup_login_group" method="post"><div> |
| 853 | 855 | login_insert_csrf_secret(); |
| 854 | - @ To leave this login group press | |
| 856 | + @ <p>To leave this login group press: | |
| 855 | 857 | @ <input type="submit" value="Leave Login Group" name="leave"> |
| 858 | + @ <p>To rotate the captcha-secret on all members of the login-group | |
| 859 | + @ so that they can all share anonymous logins, press: | |
| 860 | + @ <input type="submit" name="rotate" value="Rotate the captcha-secret"> | |
| 856 | 861 | @ </form></p> |
| 857 | 862 | } |
| 858 | 863 | @ <hr><h2>Implementation Details</h2> |
| 859 | 864 | @ <p>The following are fields from the CONFIG table related to login-groups. |
| 860 | 865 | @ </p> |
| 861 | 866 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -780,10 +780,12 @@ | |
| 780 | blob_reset(&fullName); |
| 781 | if( P("join")!=0 ){ |
| 782 | login_group_join(zRepo, 1, zLogin, zPw, zNewName, &zErrMsg); |
| 783 | }else if( P("leave") ){ |
| 784 | login_group_leave(&zErrMsg); |
| 785 | } |
| 786 | style_set_current_feature("setup"); |
| 787 | style_header("Login Group Configuration"); |
| 788 | if( zErrMsg ){ |
| 789 | @ <p class="generalError">%s(zErrMsg)</p> |
| @@ -849,12 +851,15 @@ | |
| 849 | db_finalize(&q); |
| 850 | @ </table> |
| 851 | @ |
| 852 | @ <p><form action="%R/setup_login_group" method="post"><div> |
| 853 | login_insert_csrf_secret(); |
| 854 | @ To leave this login group press |
| 855 | @ <input type="submit" value="Leave Login Group" name="leave"> |
| 856 | @ </form></p> |
| 857 | } |
| 858 | @ <hr><h2>Implementation Details</h2> |
| 859 | @ <p>The following are fields from the CONFIG table related to login-groups. |
| 860 | @ </p> |
| 861 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -780,10 +780,12 @@ | |
| 780 | blob_reset(&fullName); |
| 781 | if( P("join")!=0 ){ |
| 782 | login_group_join(zRepo, 1, zLogin, zPw, zNewName, &zErrMsg); |
| 783 | }else if( P("leave") ){ |
| 784 | login_group_leave(&zErrMsg); |
| 785 | }else if( P("rotate") ){ |
| 786 | captcha_secret_rotate(); |
| 787 | } |
| 788 | style_set_current_feature("setup"); |
| 789 | style_header("Login Group Configuration"); |
| 790 | if( zErrMsg ){ |
| 791 | @ <p class="generalError">%s(zErrMsg)</p> |
| @@ -849,12 +851,15 @@ | |
| 851 | db_finalize(&q); |
| 852 | @ </table> |
| 853 | @ |
| 854 | @ <p><form action="%R/setup_login_group" method="post"><div> |
| 855 | login_insert_csrf_secret(); |
| 856 | @ <p>To leave this login group press: |
| 857 | @ <input type="submit" value="Leave Login Group" name="leave"> |
| 858 | @ <p>To rotate the captcha-secret on all members of the login-group |
| 859 | @ so that they can all share anonymous logins, press: |
| 860 | @ <input type="submit" name="rotate" value="Rotate the captcha-secret"> |
| 861 | @ </form></p> |
| 862 | } |
| 863 | @ <hr><h2>Implementation Details</h2> |
| 864 | @ <p>The following are fields from the CONFIG table related to login-groups. |
| 865 | @ </p> |
| 866 |