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.

drh 2024-08-23 14:43 trunk
Commit acfaf4e48e776d84222cdad6cda8dbb4b39c49607494de5057b7e744173dc697
2 files changed +48 +6 -1
--- src/captcha.c
+++ src/captcha.c
@@ -508,10 +508,58 @@
508508
unsigned int x;
509509
sqlite3_randomness(sizeof(x), &x);
510510
x &= 0x7fffffff;
511511
return x;
512512
}
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
+}
513561
514562
/*
515563
** Return the value of the N-th more recent captcha-secret. The
516564
** most recent captch-secret is 0. Others are prior captcha-secrets
517565
** that have expired, but are retained for a limited period of time
518566
--- 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 @@
780780
blob_reset(&fullName);
781781
if( P("join")!=0 ){
782782
login_group_join(zRepo, 1, zLogin, zPw, zNewName, &zErrMsg);
783783
}else if( P("leave") ){
784784
login_group_leave(&zErrMsg);
785
+ }else if( P("rotate") ){
786
+ captcha_secret_rotate();
785787
}
786788
style_set_current_feature("setup");
787789
style_header("Login Group Configuration");
788790
if( zErrMsg ){
789791
@ <p class="generalError">%s(zErrMsg)</p>
@@ -849,12 +851,15 @@
849851
db_finalize(&q);
850852
@ </table>
851853
@
852854
@ <p><form action="%R/setup_login_group" method="post"><div>
853855
login_insert_csrf_secret();
854
- @ To leave this login group press
856
+ @ <p>To leave this login group press:
855857
@ <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">
856861
@ </form></p>
857862
}
858863
@ <hr><h2>Implementation Details</h2>
859864
@ <p>The following are fields from the CONFIG table related to login-groups.
860865
@ </p>
861866
--- 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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button