Fossil SCM

Allow for multiple captcha-secret values. The primary is still 'captcha-secret'. Backups are in 'captcha-secret-N' where N is a small integer. The backups are only valid for 6 hours. This allows the captcha-secret to be changed without disrupting anonymous logins and/or captcha dialogs that are in progress when the secret changes.

drh 2024-08-23 13:55 trunk
Commit 8659d84aff2874737b58cda1b23f781f17509e71f16c32bc6c28d03380901045
+3 -3
--- src/alerts.c
+++ src/alerts.c
@@ -1649,11 +1649,11 @@
16491649
uSeed = strtoul(P("captchaseed"),0,10);
16501650
zInit = P("captcha");
16511651
}else{
16521652
uSeed = captcha_seed();
16531653
}
1654
- zDecoded = captcha_decode(uSeed);
1654
+ zDecoded = captcha_decode(uSeed, 0);
16551655
zCaptcha = captcha_render(zDecoded);
16561656
@ <tr>
16571657
@ <td class="form_label">Security Code:</td>
16581658
@ <td><input type="text" name="captcha" value="%h(zInit)" size="30">
16591659
captcha_speakit_button(uSeed, "Speak the code");
@@ -2353,11 +2353,11 @@
23532353
if( eErr==1 ){
23542354
@ <td><span class="loginError">&larr; %h(zErr)</span></td>
23552355
}
23562356
@ </tr>
23572357
uSeed = captcha_seed();
2358
- zDecoded = captcha_decode(uSeed);
2358
+ zDecoded = captcha_decode(uSeed, 0);
23592359
zCaptcha = captcha_render(zDecoded);
23602360
@ <tr>
23612361
@ <td class="form_label">Security Code:</td>
23622362
@ <td><input type="text" name="captcha" value="" size="30">
23632363
captcha_speakit_button(uSeed, "Speak the code");
@@ -3356,11 +3356,11 @@
33563356
style_finish_page();
33573357
return;
33583358
}
33593359
if( captcha_needed() ){
33603360
uSeed = captcha_seed();
3361
- zDecoded = captcha_decode(uSeed);
3361
+ zDecoded = captcha_decode(uSeed, 0);
33623362
zCaptcha = captcha_render(zDecoded);
33633363
}
33643364
style_set_current_feature("alerts");
33653365
style_header("Message To Administrator");
33663366
form_begin(0, "%R/contact_admin");
33673367
--- src/alerts.c
+++ src/alerts.c
@@ -1649,11 +1649,11 @@
1649 uSeed = strtoul(P("captchaseed"),0,10);
1650 zInit = P("captcha");
1651 }else{
1652 uSeed = captcha_seed();
1653 }
1654 zDecoded = captcha_decode(uSeed);
1655 zCaptcha = captcha_render(zDecoded);
1656 @ <tr>
1657 @ <td class="form_label">Security Code:</td>
1658 @ <td><input type="text" name="captcha" value="%h(zInit)" size="30">
1659 captcha_speakit_button(uSeed, "Speak the code");
@@ -2353,11 +2353,11 @@
2353 if( eErr==1 ){
2354 @ <td><span class="loginError">&larr; %h(zErr)</span></td>
2355 }
2356 @ </tr>
2357 uSeed = captcha_seed();
2358 zDecoded = captcha_decode(uSeed);
2359 zCaptcha = captcha_render(zDecoded);
2360 @ <tr>
2361 @ <td class="form_label">Security Code:</td>
2362 @ <td><input type="text" name="captcha" value="" size="30">
2363 captcha_speakit_button(uSeed, "Speak the code");
@@ -3356,11 +3356,11 @@
3356 style_finish_page();
3357 return;
3358 }
3359 if( captcha_needed() ){
3360 uSeed = captcha_seed();
3361 zDecoded = captcha_decode(uSeed);
3362 zCaptcha = captcha_render(zDecoded);
3363 }
3364 style_set_current_feature("alerts");
3365 style_header("Message To Administrator");
3366 form_begin(0, "%R/contact_admin");
3367
--- src/alerts.c
+++ src/alerts.c
@@ -1649,11 +1649,11 @@
1649 uSeed = strtoul(P("captchaseed"),0,10);
1650 zInit = P("captcha");
1651 }else{
1652 uSeed = captcha_seed();
1653 }
1654 zDecoded = captcha_decode(uSeed, 0);
1655 zCaptcha = captcha_render(zDecoded);
1656 @ <tr>
1657 @ <td class="form_label">Security Code:</td>
1658 @ <td><input type="text" name="captcha" value="%h(zInit)" size="30">
1659 captcha_speakit_button(uSeed, "Speak the code");
@@ -2353,11 +2353,11 @@
2353 if( eErr==1 ){
2354 @ <td><span class="loginError">&larr; %h(zErr)</span></td>
2355 }
2356 @ </tr>
2357 uSeed = captcha_seed();
2358 zDecoded = captcha_decode(uSeed, 0);
2359 zCaptcha = captcha_render(zDecoded);
2360 @ <tr>
2361 @ <td class="form_label">Security Code:</td>
2362 @ <td><input type="text" name="captcha" value="" size="30">
2363 captcha_speakit_button(uSeed, "Speak the code");
@@ -3356,11 +3356,11 @@
3356 style_finish_page();
3357 return;
3358 }
3359 if( captcha_needed() ){
3360 uSeed = captcha_seed();
3361 zDecoded = captcha_decode(uSeed, 0);
3362 zCaptcha = captcha_render(zDecoded);
3363 }
3364 style_set_current_feature("alerts");
3365 style_header("Message To Administrator");
3366 form_begin(0, "%R/contact_admin");
3367
+53 -15
--- src/captcha.c
+++ src/captcha.c
@@ -508,39 +508,74 @@
508508
unsigned int x;
509509
sqlite3_randomness(sizeof(x), &x);
510510
x &= 0x7fffffff;
511511
return x;
512512
}
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
+** so that pending anonymous login cookies and/or captcha dialogs
519
+** don't malfunction when the captcha-secret changes.
520
+**
521
+** Clients should start by using the 0-th captcha-secret. Only if
522
+** that one does not work should they advance to 1 and 2 and so forth,
523
+** until this routine returns a NULL pointer.
524
+**
525
+** The value returned is a string obtained from fossil_malloc() and
526
+** should be freed by the caller.
527
+**
528
+** The 0-th captcha secret is the value of Config.Name='captcha-secret'.
529
+** For N>0, the value is in Config.Name='captcha-secret-$N'.
530
+*/
531
+char *captcha_secret(int N){
532
+ if( N==0 ){
533
+ return db_text(0, "SELECT value FROM config WHERE name='captcha-secret'");
534
+ }else{
535
+ return db_text(0,
536
+ "SELECT value FROM config"
537
+ " WHERE name='captcha-secret-%d'"
538
+ " AND mtime>unixepoch('now','-6 hours')", N);
539
+ }
540
+}
513541
514542
/*
515543
** Translate a captcha seed value into the captcha password string.
516544
** The returned string is static and overwritten on each call to
517545
** this function.
546
+**
547
+** Use the N-th captcha secret to compute the password. When N==0,
548
+** a valid password is always returned. A new captcha-secret will
549
+** be created if necessary. But for N>0, the return value might
550
+** be NULL to indicate that there is no N-th captcha-secret.
518551
*/
519
-const char *captcha_decode(unsigned int seed){
520
- const char *zSecret;
552
+const char *captcha_decode(unsigned int seed, int N){
553
+ char *zSecret;
521554
const char *z;
522555
Blob b;
523556
static char zRes[20];
524557
525
- zSecret = db_get("captcha-secret", 0);
558
+ zSecret = captcha_secret(N);
526559
if( zSecret==0 ){
560
+ if( N>0 ) return 0;
527561
db_unprotect(PROTECT_CONFIG);
528562
db_multi_exec(
529563
"REPLACE INTO config(name,value)"
530564
" VALUES('captcha-secret', lower(hex(randomblob(20))));"
531565
);
532566
db_protect_pop();
533
- zSecret = db_get("captcha-secret", 0);
567
+ zSecret = captcha_secret(0);
534568
assert( zSecret!=0 );
535569
}
536570
blob_init(&b, 0, 0);
537571
blob_appendf(&b, "%s-%x", zSecret, seed);
538572
sha1sum_blob(&b, &b);
539573
z = blob_buffer(&b);
540574
memcpy(zRes, z, 8);
541575
zRes[8] = 0;
576
+ fossil_free(zSecret);
542577
return zRes;
543578
}
544579
545580
/*
546581
** Return true if a CAPTCHA is required for editing wiki or tickets or for
@@ -569,26 +604,29 @@
569604
const char *zSeed;
570605
const char *zEntered;
571606
const char *zDecode;
572607
char z[30];
573608
int i;
609
+ int n = 0;
574610
if( !bAlwaysNeeded && !captcha_needed() ){
575611
return 1; /* No captcha needed */
576612
}
577613
zSeed = P("captchaseed");
578614
if( zSeed==0 ) return 0;
579615
zEntered = P("captcha");
580616
if( zEntered==0 || strlen(zEntered)!=8 ) return 0;
581
- zDecode = captcha_decode((unsigned int)atoi(zSeed));
582
- assert( strlen(zDecode)==8 );
583
- for(i=0; i<8; i++){
584
- char c = zEntered[i];
585
- if( c>='A' && c<='F' ) c += 'a' - 'A';
586
- if( c=='O' ) c = '0';
587
- z[i] = c;
588
- }
589
- if( strncmp(zDecode,z,8)!=0 ) return 0;
617
+ do{
618
+ zDecode = captcha_decode((unsigned int)atoi(zSeed), n++);
619
+ if( zDecode==0 ) return 0;
620
+ assert( strlen(zDecode)==8 );
621
+ for(i=0; i<8; i++){
622
+ char c = zEntered[i];
623
+ if( c>='A' && c<='F' ) c += 'a' - 'A';
624
+ if( c=='O' ) c = '0';
625
+ z[i] = c;
626
+ }
627
+ }while( strncmp(zDecode,z,8)!=0 );
590628
return 1;
591629
}
592630
593631
/*
594632
** Generate a captcha display together with the necessary hidden parameter
@@ -607,11 +645,11 @@
607645
const char *zDecoded;
608646
char *zCaptcha;
609647
610648
if( !captcha_needed() && (mFlags & 0x02)==0 ) return;
611649
uSeed = captcha_seed();
612
- zDecoded = captcha_decode(uSeed);
650
+ zDecoded = captcha_decode(uSeed, 0);
613651
zCaptcha = captcha_render(zDecoded);
614652
@ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
615653
@ %h(zCaptcha)
616654
@ </pre>
617655
@ Enter security code shown above:
@@ -808,11 +846,11 @@
808846
** Return a WAV file that pronounces the digits of the captcha that
809847
** is determined by the seed given in the name= query parameter.
810848
*/
811849
void captcha_wav_page(void){
812850
const char *zSeed = PD("name","0");
813
- const char *zDecode = captcha_decode((unsigned int)atoi(zSeed));
851
+ const char *zDecode = captcha_decode((unsigned int)atoi(zSeed), 0);
814852
Blob audio;
815853
captcha_wav(zDecode, &audio);
816854
cgi_set_content_type("audio/wav");
817855
cgi_set_content(&audio);
818856
}
819857
--- src/captcha.c
+++ src/captcha.c
@@ -508,39 +508,74 @@
508 unsigned int x;
509 sqlite3_randomness(sizeof(x), &x);
510 x &= 0x7fffffff;
511 return x;
512 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
514 /*
515 ** Translate a captcha seed value into the captcha password string.
516 ** The returned string is static and overwritten on each call to
517 ** this function.
 
 
 
 
 
518 */
519 const char *captcha_decode(unsigned int seed){
520 const char *zSecret;
521 const char *z;
522 Blob b;
523 static char zRes[20];
524
525 zSecret = db_get("captcha-secret", 0);
526 if( zSecret==0 ){
 
527 db_unprotect(PROTECT_CONFIG);
528 db_multi_exec(
529 "REPLACE INTO config(name,value)"
530 " VALUES('captcha-secret', lower(hex(randomblob(20))));"
531 );
532 db_protect_pop();
533 zSecret = db_get("captcha-secret", 0);
534 assert( zSecret!=0 );
535 }
536 blob_init(&b, 0, 0);
537 blob_appendf(&b, "%s-%x", zSecret, seed);
538 sha1sum_blob(&b, &b);
539 z = blob_buffer(&b);
540 memcpy(zRes, z, 8);
541 zRes[8] = 0;
 
542 return zRes;
543 }
544
545 /*
546 ** Return true if a CAPTCHA is required for editing wiki or tickets or for
@@ -569,26 +604,29 @@
569 const char *zSeed;
570 const char *zEntered;
571 const char *zDecode;
572 char z[30];
573 int i;
 
574 if( !bAlwaysNeeded && !captcha_needed() ){
575 return 1; /* No captcha needed */
576 }
577 zSeed = P("captchaseed");
578 if( zSeed==0 ) return 0;
579 zEntered = P("captcha");
580 if( zEntered==0 || strlen(zEntered)!=8 ) return 0;
581 zDecode = captcha_decode((unsigned int)atoi(zSeed));
582 assert( strlen(zDecode)==8 );
583 for(i=0; i<8; i++){
584 char c = zEntered[i];
585 if( c>='A' && c<='F' ) c += 'a' - 'A';
586 if( c=='O' ) c = '0';
587 z[i] = c;
588 }
589 if( strncmp(zDecode,z,8)!=0 ) return 0;
 
 
590 return 1;
591 }
592
593 /*
594 ** Generate a captcha display together with the necessary hidden parameter
@@ -607,11 +645,11 @@
607 const char *zDecoded;
608 char *zCaptcha;
609
610 if( !captcha_needed() && (mFlags & 0x02)==0 ) return;
611 uSeed = captcha_seed();
612 zDecoded = captcha_decode(uSeed);
613 zCaptcha = captcha_render(zDecoded);
614 @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
615 @ %h(zCaptcha)
616 @ </pre>
617 @ Enter security code shown above:
@@ -808,11 +846,11 @@
808 ** Return a WAV file that pronounces the digits of the captcha that
809 ** is determined by the seed given in the name= query parameter.
810 */
811 void captcha_wav_page(void){
812 const char *zSeed = PD("name","0");
813 const char *zDecode = captcha_decode((unsigned int)atoi(zSeed));
814 Blob audio;
815 captcha_wav(zDecode, &audio);
816 cgi_set_content_type("audio/wav");
817 cgi_set_content(&audio);
818 }
819
--- src/captcha.c
+++ src/captcha.c
@@ -508,39 +508,74 @@
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 ** so that pending anonymous login cookies and/or captcha dialogs
519 ** don't malfunction when the captcha-secret changes.
520 **
521 ** Clients should start by using the 0-th captcha-secret. Only if
522 ** that one does not work should they advance to 1 and 2 and so forth,
523 ** until this routine returns a NULL pointer.
524 **
525 ** The value returned is a string obtained from fossil_malloc() and
526 ** should be freed by the caller.
527 **
528 ** The 0-th captcha secret is the value of Config.Name='captcha-secret'.
529 ** For N>0, the value is in Config.Name='captcha-secret-$N'.
530 */
531 char *captcha_secret(int N){
532 if( N==0 ){
533 return db_text(0, "SELECT value FROM config WHERE name='captcha-secret'");
534 }else{
535 return db_text(0,
536 "SELECT value FROM config"
537 " WHERE name='captcha-secret-%d'"
538 " AND mtime>unixepoch('now','-6 hours')", N);
539 }
540 }
541
542 /*
543 ** Translate a captcha seed value into the captcha password string.
544 ** The returned string is static and overwritten on each call to
545 ** this function.
546 **
547 ** Use the N-th captcha secret to compute the password. When N==0,
548 ** a valid password is always returned. A new captcha-secret will
549 ** be created if necessary. But for N>0, the return value might
550 ** be NULL to indicate that there is no N-th captcha-secret.
551 */
552 const char *captcha_decode(unsigned int seed, int N){
553 char *zSecret;
554 const char *z;
555 Blob b;
556 static char zRes[20];
557
558 zSecret = captcha_secret(N);
559 if( zSecret==0 ){
560 if( N>0 ) return 0;
561 db_unprotect(PROTECT_CONFIG);
562 db_multi_exec(
563 "REPLACE INTO config(name,value)"
564 " VALUES('captcha-secret', lower(hex(randomblob(20))));"
565 );
566 db_protect_pop();
567 zSecret = captcha_secret(0);
568 assert( zSecret!=0 );
569 }
570 blob_init(&b, 0, 0);
571 blob_appendf(&b, "%s-%x", zSecret, seed);
572 sha1sum_blob(&b, &b);
573 z = blob_buffer(&b);
574 memcpy(zRes, z, 8);
575 zRes[8] = 0;
576 fossil_free(zSecret);
577 return zRes;
578 }
579
580 /*
581 ** Return true if a CAPTCHA is required for editing wiki or tickets or for
@@ -569,26 +604,29 @@
604 const char *zSeed;
605 const char *zEntered;
606 const char *zDecode;
607 char z[30];
608 int i;
609 int n = 0;
610 if( !bAlwaysNeeded && !captcha_needed() ){
611 return 1; /* No captcha needed */
612 }
613 zSeed = P("captchaseed");
614 if( zSeed==0 ) return 0;
615 zEntered = P("captcha");
616 if( zEntered==0 || strlen(zEntered)!=8 ) return 0;
617 do{
618 zDecode = captcha_decode((unsigned int)atoi(zSeed), n++);
619 if( zDecode==0 ) return 0;
620 assert( strlen(zDecode)==8 );
621 for(i=0; i<8; i++){
622 char c = zEntered[i];
623 if( c>='A' && c<='F' ) c += 'a' - 'A';
624 if( c=='O' ) c = '0';
625 z[i] = c;
626 }
627 }while( strncmp(zDecode,z,8)!=0 );
628 return 1;
629 }
630
631 /*
632 ** Generate a captcha display together with the necessary hidden parameter
@@ -607,11 +645,11 @@
645 const char *zDecoded;
646 char *zCaptcha;
647
648 if( !captcha_needed() && (mFlags & 0x02)==0 ) return;
649 uSeed = captcha_seed();
650 zDecoded = captcha_decode(uSeed, 0);
651 zCaptcha = captcha_render(zDecoded);
652 @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
653 @ %h(zCaptcha)
654 @ </pre>
655 @ Enter security code shown above:
@@ -808,11 +846,11 @@
846 ** Return a WAV file that pronounces the digits of the captcha that
847 ** is determined by the seed given in the name= query parameter.
848 */
849 void captcha_wav_page(void){
850 const char *zSeed = PD("name","0");
851 const char *zDecode = captcha_decode((unsigned int)atoi(zSeed), 0);
852 Blob audio;
853 captcha_wav(zDecode, &audio);
854 cgi_set_content_type("audio/wav");
855 cgi_set_content(&audio);
856 }
857
--- src/json_login.c
+++ src/json_login.c
@@ -213,11 +213,11 @@
213213
*/
214214
cson_value * json_page_anon_password(void){
215215
cson_value * v = cson_value_new_object();
216216
cson_object * o = cson_value_get_object(v);
217217
unsigned const int seed = captcha_seed();
218
- char const * zCaptcha = captcha_decode(seed);
218
+ char const * zCaptcha = captcha_decode(seed, 0);
219219
cson_object_set(o, "seed",
220220
cson_value_new_integer( (cson_int_t)seed )
221221
);
222222
cson_object_set(o, "password",
223223
cson_value_new_string( zCaptcha, strlen(zCaptcha) )
224224
--- src/json_login.c
+++ src/json_login.c
@@ -213,11 +213,11 @@
213 */
214 cson_value * json_page_anon_password(void){
215 cson_value * v = cson_value_new_object();
216 cson_object * o = cson_value_get_object(v);
217 unsigned const int seed = captcha_seed();
218 char const * zCaptcha = captcha_decode(seed);
219 cson_object_set(o, "seed",
220 cson_value_new_integer( (cson_int_t)seed )
221 );
222 cson_object_set(o, "password",
223 cson_value_new_string( zCaptcha, strlen(zCaptcha) )
224
--- src/json_login.c
+++ src/json_login.c
@@ -213,11 +213,11 @@
213 */
214 cson_value * json_page_anon_password(void){
215 cson_value * v = cson_value_new_object();
216 cson_object * o = cson_value_get_object(v);
217 unsigned const int seed = captcha_seed();
218 char const * zCaptcha = captcha_decode(seed, 0);
219 cson_object_set(o, "seed",
220 cson_value_new_integer( (cson_int_t)seed )
221 );
222 cson_object_set(o, "password",
223 cson_value_new_string( zCaptcha, strlen(zCaptcha) )
224
+32 -19
--- src/login.c
+++ src/login.c
@@ -154,17 +154,22 @@
154154
const char *zPassword, /* The supplied password */
155155
const char *zCS /* The captcha seed value */
156156
){
157157
const char *zPw; /* The correct password shown in the captcha */
158158
int uid; /* The user ID of anonymous */
159
+ int n = 0; /* Counter of captcha-secrets */
159160
160161
if( zUsername==0 ) return 0;
161162
else if( zPassword==0 ) return 0;
162163
else if( zCS==0 ) return 0;
163164
else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
164
- zPw = captcha_decode((unsigned int)atoi(zCS));
165
- if( fossil_stricmp(zPw, zPassword)!=0 ) return 0;
165
+ while( 1/*exit-by-break*/ ){
166
+ zPw = captcha_decode((unsigned int)atoi(zCS), n);
167
+ if( zPw==0 ) return 0;
168
+ if( fossil_stricmp(zPw, zPassword)==0 ) break;
169
+ n++;
170
+ }
166171
uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
167172
" AND octet_length(pw)>0 AND octet_length(cap)>0");
168173
return uid;
169174
}
170175
@@ -346,29 +351,30 @@
346351
** *zCookieDest and the caller must eventually free() it.
347352
**
348353
** If bSessionCookie is true, the cookie will be a session cookie.
349354
*/
350355
void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
351
- const char *zNow; /* Current time (julian day number) */
356
+ char *zNow; /* Current time (julian day number) */
352357
char *zCookie; /* The login cookie */
353358
const char *zCookieName; /* Name of the login cookie */
354359
Blob b; /* Blob used during cookie construction */
355360
int expires = bSessionCookie ? 0 : 6*3600;
356361
zCookieName = login_cookie_name();
357362
zNow = db_text("0", "SELECT julianday('now')");
358363
assert( zCookieName && zNow );
359364
blob_init(&b, zNow, -1);
360
- blob_appendf(&b, "/%s", db_get("captcha-secret",""));
365
+ blob_appendf(&b, "/%z", captcha_secret(0));
361366
sha1sum_blob(&b, &b);
362367
zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
363368
blob_reset(&b);
364369
cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
365370
if( zCookieDest ){
366371
*zCookieDest = zCookie;
367372
}else{
368373
free(zCookie);
369374
}
375
+ fossil_free(zNow);
370376
}
371377
372378
/*
373379
** "Unsets" the login cookie (insofar as cookies can be unset) and
374380
** clears the current user's (g.userUid) login information from the
@@ -806,11 +812,11 @@
806812
@ <td><input type="submit" name="pwreset" value="Reset My Password">
807813
@ </tr>
808814
}
809815
@ </table>
810816
if( zAnonPw && !noAnon ){
811
- const char *zDecoded = captcha_decode(uSeed);
817
+ const char *zDecoded = captcha_decode(uSeed, 0);
812818
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
813819
char *zCaptcha = captcha_render(zDecoded);
814820
815821
@ <p><input type="hidden" name="cs" value="%u(uSeed)">
816822
@ Visitors may enter <b>anonymous</b> as the user-ID with
@@ -1428,22 +1434,29 @@
14281434
** too old and the sha1 hash of TIME/SECRET must match HASH.
14291435
** SECRET is the "captcha-secret" value in the repository.
14301436
*/
14311437
double rTime = atof(zArg);
14321438
Blob b;
1433
- blob_zero(&b);
1434
- blob_appendf(&b, "%s/%s", zArg, db_get("captcha-secret",""));
1435
- sha1sum_blob(&b, &b);
1436
- if( fossil_strcmp(zHash, blob_str(&b))==0 ){
1437
- uid = db_int(0,
1438
- "SELECT uid FROM user WHERE login='anonymous'"
1439
- " AND octet_length(cap)>0"
1440
- " AND octet_length(pw)>0"
1441
- " AND %.17g+0.25>julianday('now')",
1442
- rTime
1443
- );
1444
- }
1439
+ char *zSecret;
1440
+ int n = 0;
1441
+
1442
+ do{
1443
+ blob_zero(&b);
1444
+ zSecret = captcha_secret(n++);
1445
+ if( zSecret==0 ) break;
1446
+ blob_appendf(&b, "%s/%s", zArg, zSecret);
1447
+ sha1sum_blob(&b, &b);
1448
+ if( fossil_strcmp(zHash, blob_str(&b))==0 ){
1449
+ uid = db_int(0,
1450
+ "SELECT uid FROM user WHERE login='anonymous'"
1451
+ " AND octet_length(cap)>0"
1452
+ " AND octet_length(pw)>0"
1453
+ " AND %.17g+0.25>julianday('now')",
1454
+ rTime
1455
+ );
1456
+ }
1457
+ }while( uid==0 );
14451458
blob_reset(&b);
14461459
}else{
14471460
/* Cookies of the form "HASH/CODE/USER". Search first in the
14481461
** local user table, then the user table for project CODE if we
14491462
** are part of a login-group.
@@ -2213,11 +2226,11 @@
22132226
if( captchaIsCorrect ){
22142227
uSeed = strtoul(P("captchaseed"),0,10);
22152228
}else{
22162229
uSeed = captcha_seed();
22172230
}
2218
- zDecoded = captcha_decode(uSeed);
2231
+ zDecoded = captcha_decode(uSeed, 0);
22192232
zCaptcha = captcha_render(zDecoded);
22202233
22212234
style_header("Register");
22222235
/* Print out the registration form. */
22232236
g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
@@ -2423,11 +2436,11 @@
24232436
if( captchaIsCorrect ){
24242437
uSeed = strtoul(P("captchaseed"),0,10);
24252438
}else{
24262439
uSeed = captcha_seed();
24272440
}
2428
- zDecoded = captcha_decode(uSeed);
2441
+ zDecoded = captcha_decode(uSeed, 0);
24292442
zCaptcha = captcha_render(zDecoded);
24302443
24312444
style_header("Request Password Reset");
24322445
/* Print out the registration form. */
24332446
g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
24342447
--- src/login.c
+++ src/login.c
@@ -154,17 +154,22 @@
154 const char *zPassword, /* The supplied password */
155 const char *zCS /* The captcha seed value */
156 ){
157 const char *zPw; /* The correct password shown in the captcha */
158 int uid; /* The user ID of anonymous */
 
159
160 if( zUsername==0 ) return 0;
161 else if( zPassword==0 ) return 0;
162 else if( zCS==0 ) return 0;
163 else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
164 zPw = captcha_decode((unsigned int)atoi(zCS));
165 if( fossil_stricmp(zPw, zPassword)!=0 ) return 0;
 
 
 
 
166 uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
167 " AND octet_length(pw)>0 AND octet_length(cap)>0");
168 return uid;
169 }
170
@@ -346,29 +351,30 @@
346 ** *zCookieDest and the caller must eventually free() it.
347 **
348 ** If bSessionCookie is true, the cookie will be a session cookie.
349 */
350 void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
351 const char *zNow; /* Current time (julian day number) */
352 char *zCookie; /* The login cookie */
353 const char *zCookieName; /* Name of the login cookie */
354 Blob b; /* Blob used during cookie construction */
355 int expires = bSessionCookie ? 0 : 6*3600;
356 zCookieName = login_cookie_name();
357 zNow = db_text("0", "SELECT julianday('now')");
358 assert( zCookieName && zNow );
359 blob_init(&b, zNow, -1);
360 blob_appendf(&b, "/%s", db_get("captcha-secret",""));
361 sha1sum_blob(&b, &b);
362 zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
363 blob_reset(&b);
364 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
365 if( zCookieDest ){
366 *zCookieDest = zCookie;
367 }else{
368 free(zCookie);
369 }
 
370 }
371
372 /*
373 ** "Unsets" the login cookie (insofar as cookies can be unset) and
374 ** clears the current user's (g.userUid) login information from the
@@ -806,11 +812,11 @@
806 @ <td><input type="submit" name="pwreset" value="Reset My Password">
807 @ </tr>
808 }
809 @ </table>
810 if( zAnonPw && !noAnon ){
811 const char *zDecoded = captcha_decode(uSeed);
812 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
813 char *zCaptcha = captcha_render(zDecoded);
814
815 @ <p><input type="hidden" name="cs" value="%u(uSeed)">
816 @ Visitors may enter <b>anonymous</b> as the user-ID with
@@ -1428,22 +1434,29 @@
1428 ** too old and the sha1 hash of TIME/SECRET must match HASH.
1429 ** SECRET is the "captcha-secret" value in the repository.
1430 */
1431 double rTime = atof(zArg);
1432 Blob b;
1433 blob_zero(&b);
1434 blob_appendf(&b, "%s/%s", zArg, db_get("captcha-secret",""));
1435 sha1sum_blob(&b, &b);
1436 if( fossil_strcmp(zHash, blob_str(&b))==0 ){
1437 uid = db_int(0,
1438 "SELECT uid FROM user WHERE login='anonymous'"
1439 " AND octet_length(cap)>0"
1440 " AND octet_length(pw)>0"
1441 " AND %.17g+0.25>julianday('now')",
1442 rTime
1443 );
1444 }
 
 
 
 
 
 
 
1445 blob_reset(&b);
1446 }else{
1447 /* Cookies of the form "HASH/CODE/USER". Search first in the
1448 ** local user table, then the user table for project CODE if we
1449 ** are part of a login-group.
@@ -2213,11 +2226,11 @@
2213 if( captchaIsCorrect ){
2214 uSeed = strtoul(P("captchaseed"),0,10);
2215 }else{
2216 uSeed = captcha_seed();
2217 }
2218 zDecoded = captcha_decode(uSeed);
2219 zCaptcha = captcha_render(zDecoded);
2220
2221 style_header("Register");
2222 /* Print out the registration form. */
2223 g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
@@ -2423,11 +2436,11 @@
2423 if( captchaIsCorrect ){
2424 uSeed = strtoul(P("captchaseed"),0,10);
2425 }else{
2426 uSeed = captcha_seed();
2427 }
2428 zDecoded = captcha_decode(uSeed);
2429 zCaptcha = captcha_render(zDecoded);
2430
2431 style_header("Request Password Reset");
2432 /* Print out the registration form. */
2433 g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
2434
--- src/login.c
+++ src/login.c
@@ -154,17 +154,22 @@
154 const char *zPassword, /* The supplied password */
155 const char *zCS /* The captcha seed value */
156 ){
157 const char *zPw; /* The correct password shown in the captcha */
158 int uid; /* The user ID of anonymous */
159 int n = 0; /* Counter of captcha-secrets */
160
161 if( zUsername==0 ) return 0;
162 else if( zPassword==0 ) return 0;
163 else if( zCS==0 ) return 0;
164 else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
165 while( 1/*exit-by-break*/ ){
166 zPw = captcha_decode((unsigned int)atoi(zCS), n);
167 if( zPw==0 ) return 0;
168 if( fossil_stricmp(zPw, zPassword)==0 ) break;
169 n++;
170 }
171 uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
172 " AND octet_length(pw)>0 AND octet_length(cap)>0");
173 return uid;
174 }
175
@@ -346,29 +351,30 @@
351 ** *zCookieDest and the caller must eventually free() it.
352 **
353 ** If bSessionCookie is true, the cookie will be a session cookie.
354 */
355 void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
356 char *zNow; /* Current time (julian day number) */
357 char *zCookie; /* The login cookie */
358 const char *zCookieName; /* Name of the login cookie */
359 Blob b; /* Blob used during cookie construction */
360 int expires = bSessionCookie ? 0 : 6*3600;
361 zCookieName = login_cookie_name();
362 zNow = db_text("0", "SELECT julianday('now')");
363 assert( zCookieName && zNow );
364 blob_init(&b, zNow, -1);
365 blob_appendf(&b, "/%z", captcha_secret(0));
366 sha1sum_blob(&b, &b);
367 zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
368 blob_reset(&b);
369 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
370 if( zCookieDest ){
371 *zCookieDest = zCookie;
372 }else{
373 free(zCookie);
374 }
375 fossil_free(zNow);
376 }
377
378 /*
379 ** "Unsets" the login cookie (insofar as cookies can be unset) and
380 ** clears the current user's (g.userUid) login information from the
@@ -806,11 +812,11 @@
812 @ <td><input type="submit" name="pwreset" value="Reset My Password">
813 @ </tr>
814 }
815 @ </table>
816 if( zAnonPw && !noAnon ){
817 const char *zDecoded = captcha_decode(uSeed, 0);
818 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
819 char *zCaptcha = captcha_render(zDecoded);
820
821 @ <p><input type="hidden" name="cs" value="%u(uSeed)">
822 @ Visitors may enter <b>anonymous</b> as the user-ID with
@@ -1428,22 +1434,29 @@
1434 ** too old and the sha1 hash of TIME/SECRET must match HASH.
1435 ** SECRET is the "captcha-secret" value in the repository.
1436 */
1437 double rTime = atof(zArg);
1438 Blob b;
1439 char *zSecret;
1440 int n = 0;
1441
1442 do{
1443 blob_zero(&b);
1444 zSecret = captcha_secret(n++);
1445 if( zSecret==0 ) break;
1446 blob_appendf(&b, "%s/%s", zArg, zSecret);
1447 sha1sum_blob(&b, &b);
1448 if( fossil_strcmp(zHash, blob_str(&b))==0 ){
1449 uid = db_int(0,
1450 "SELECT uid FROM user WHERE login='anonymous'"
1451 " AND octet_length(cap)>0"
1452 " AND octet_length(pw)>0"
1453 " AND %.17g+0.25>julianday('now')",
1454 rTime
1455 );
1456 }
1457 }while( uid==0 );
1458 blob_reset(&b);
1459 }else{
1460 /* Cookies of the form "HASH/CODE/USER". Search first in the
1461 ** local user table, then the user table for project CODE if we
1462 ** are part of a login-group.
@@ -2213,11 +2226,11 @@
2226 if( captchaIsCorrect ){
2227 uSeed = strtoul(P("captchaseed"),0,10);
2228 }else{
2229 uSeed = captcha_seed();
2230 }
2231 zDecoded = captcha_decode(uSeed, 0);
2232 zCaptcha = captcha_render(zDecoded);
2233
2234 style_header("Register");
2235 /* Print out the registration form. */
2236 g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
@@ -2423,11 +2436,11 @@
2436 if( captchaIsCorrect ){
2437 uSeed = strtoul(P("captchaseed"),0,10);
2438 }else{
2439 uSeed = captcha_seed();
2440 }
2441 zDecoded = captcha_decode(uSeed, 0);
2442 zCaptcha = captcha_render(zDecoded);
2443
2444 style_header("Request Password Reset");
2445 /* Print out the registration form. */
2446 g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
2447
+1 -1
--- src/style.c
+++ src/style.c
@@ -1355,11 +1355,11 @@
13551355
** WEBPAGE: honeypot
13561356
** This page is a honeypot for spiders and bots.
13571357
*/
13581358
void honeypot_page(void){
13591359
unsigned int uSeed = captcha_seed();
1360
- const char *zDecoded = captcha_decode(uSeed);
1360
+ const char *zDecoded = captcha_decode(uSeed, 0);
13611361
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
13621362
char *zCaptcha = captcha_render(zDecoded);
13631363
style_header("I think you are a robot");
13641364
@ <p>You seem like a robot.</p>
13651365
@
13661366
--- src/style.c
+++ src/style.c
@@ -1355,11 +1355,11 @@
1355 ** WEBPAGE: honeypot
1356 ** This page is a honeypot for spiders and bots.
1357 */
1358 void honeypot_page(void){
1359 unsigned int uSeed = captcha_seed();
1360 const char *zDecoded = captcha_decode(uSeed);
1361 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
1362 char *zCaptcha = captcha_render(zDecoded);
1363 style_header("I think you are a robot");
1364 @ <p>You seem like a robot.</p>
1365 @
1366
--- src/style.c
+++ src/style.c
@@ -1355,11 +1355,11 @@
1355 ** WEBPAGE: honeypot
1356 ** This page is a honeypot for spiders and bots.
1357 */
1358 void honeypot_page(void){
1359 unsigned int uSeed = captcha_seed();
1360 const char *zDecoded = captcha_decode(uSeed, 0);
1361 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
1362 char *zCaptcha = captcha_render(zDecoded);
1363 style_header("I think you are a robot");
1364 @ <p>You seem like a robot.</p>
1365 @
1366

Keyboard Shortcuts

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