Fossil SCM

New setting "anon-cookie-lifespan" sets the life span of an anonymous login cookie. The default is 8 hours. Set to zero to disable anonymous login.

drh 2025-08-18 15:49 trunk
Commit 7d2b47a7c3f068ebf948e2545c996897d98d167c4dd021931f643f5a490bea78
2 files changed +29 -8 +14
+29 -8
--- src/login.c
+++ src/login.c
@@ -160,10 +160,11 @@
160160
161161
if( zUsername==0 ) return 0;
162162
else if( zPassword==0 ) return 0;
163163
else if( zCS==0 ) return 0;
164164
else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
165
+ else if( anon_cookie_lifespan()==0 ) return 0;
165166
while( 1/*exit-by-break*/ ){
166167
zPw = captcha_decode((unsigned int)atoi(zCS), n);
167168
if( zPw==0 ) return 0;
168169
if( fossil_stricmp(zPw, zPassword)==0 ) break;
169170
n++;
@@ -340,13 +341,32 @@
340341
free(zCookie);
341342
}
342343
}
343344
344345
/*
345
-** Lifetime of an anoymous cookie, in seconds.
346
+** SETTING: anon-cookie-lifespan width=10 default=480
347
+** The number of minutes for which an anonymous login cookie is
348
+** valid. Anonymous logins are prohibited if this value is zero.
349
+*/
350
+
351
+
352
+/*
353
+** The default lifetime of an anoymous cookie, in minutes.
354
+*/
355
+#define ANONYMOUS_COOKIE_LIFESPAN (8*60)
356
+
357
+/*
358
+** Return the lifetime of an anonymous cookie, in minutes.
346359
*/
347
-#define ANONYMOUS_COOKIE_LIFESPAN 28800 /* 28800 seconds == 8 hours */
360
+int anon_cookie_lifespan(void){
361
+ static int lifespan = -1;
362
+ if( lifespan<0 ){
363
+ lifespan = db_get_int("anon-cookie-lifespan", ANONYMOUS_COOKIE_LIFESPAN);
364
+ if( lifespan<0 ) lifespan = 0;
365
+ }
366
+ return lifespan;
367
+}
348368
349369
/* Sets a cookie for an anonymous user login, which looks like this:
350370
**
351371
** HASH/TIME/anonymous
352372
**
@@ -364,11 +384,11 @@
364384
char *zNow; /* Current time (julian day number) */
365385
char *zCookie; /* The login cookie */
366386
const char *zUserAgent; /* The user agent */
367387
const char *zCookieName; /* Name of the login cookie */
368388
Blob b; /* Blob used during cookie construction */
369
- int expires = bSessionCookie ? 0 : ANONYMOUS_COOKIE_LIFESPAN;
389
+ int expires = bSessionCookie ? 0 : anon_cookie_lifespan();
370390
zCookieName = login_cookie_name();
371391
zNow = db_text("0", "SELECT julianday('now')");
372392
assert( zCookieName && zNow );
373393
blob_init(&b, zNow, -1);
374394
zUserAgent = PD("HTTP_USER_AGENT","nil");
@@ -602,11 +622,11 @@
602622
** visitors.
603623
**
604624
** anon=1 is advisory and only has effect if there is not some other login
605625
** cookie. anon=2 means always show the captcha.
606626
*/
607
- anonFlag = atoi(PD("anon","0"));
627
+ anonFlag = anon_cookie_lifespan()>0 ? atoi(PD("anon","0")) : 0;
608628
if( anonFlag==2 ){
609629
g.zLogin = 0;
610630
}else{
611631
login_check_credentials();
612632
if( g.zLogin!=0 ) anonFlag = 0;
@@ -785,11 +805,11 @@
785805
@ <p>Currently logged in as <b>%h(g.zLogin)</b>.
786806
@ <input type="submit" name="out" value="Logout" autofocus></p>
787807
@ </form>
788808
}else{
789809
unsigned int uSeed = captcha_seed();
790
- if( g.zLogin==0 && (anonFlag || zGoto==0) ){
810
+ if( g.zLogin==0 && (anonFlag || zGoto==0) && anon_cookie_lifespan()>0 ){
791811
zAnonPw = db_text(0, "SELECT pw FROM user"
792812
" WHERE login='anonymous'"
793813
" AND cap!=''");
794814
}else{
795815
zAnonPw = 0;
@@ -1418,11 +1438,12 @@
14181438
}
14191439
}
14201440
}
14211441
if( zUser==0 ){
14221442
/* Invalid cookie */
1423
- }else if( fossil_strcmp(zUser, "anonymous")==0 ){
1443
+ }else if( fossil_strcmp(zUser, "anonymous")==0
1444
+ && anon_cookie_lifespan()>0 ){
14241445
/* Cookies of the form "HASH/TIME/anonymous". The TIME must
14251446
** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and
14261447
** the sha1 hash of TIME/USERAGENT/SECRET must match HASH. USERAGENT
14271448
** is the HTTP_USER_AGENT of the client and SECRET is the
14281449
** "captcha-secret" value in the repository. See tag-20250817a
@@ -1444,11 +1465,11 @@
14441465
uid = db_int(0,
14451466
"SELECT uid FROM user WHERE login='anonymous'"
14461467
" AND octet_length(cap)>0"
14471468
" AND octet_length(pw)>0"
14481469
" AND %.17g>julianday('now')",
1449
- rTime+ANONYMOUS_COOKIE_LIFESPAN/86400.0
1470
+ rTime+anon_cookie_lifespan()/1440.0
14501471
);
14511472
}
14521473
}while( uid==0 );
14531474
blob_reset(&b);
14541475
}else{
@@ -1909,11 +1930,11 @@
19091930
** the anonymous user has Hyperlink permission, then paint a mesage
19101931
** to inform the user that much more information is available by
19111932
** logging in as anonymous.
19121933
*/
19131934
void login_anonymous_available(void){
1914
- if( !g.perm.Hyperlink && g.anon.Hyperlink ){
1935
+ if( !g.perm.Hyperlink && g.anon.Hyperlink && anon_cookie_lifespan()>0 ){
19151936
const char *zUrl = PD("PATH_INFO", "");
19161937
@ <p>Many <span class="disabled">hyperlinks are disabled.</span><br>
19171938
@ Use <a href="%R/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
19181939
@ to enable hyperlinks.</p>
19191940
}
19201941
--- src/login.c
+++ src/login.c
@@ -160,10 +160,11 @@
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++;
@@ -340,13 +341,32 @@
340 free(zCookie);
341 }
342 }
343
344 /*
345 ** Lifetime of an anoymous cookie, in seconds.
 
 
 
 
 
 
 
 
 
 
 
 
346 */
347 #define ANONYMOUS_COOKIE_LIFESPAN 28800 /* 28800 seconds == 8 hours */
 
 
 
 
 
 
 
348
349 /* Sets a cookie for an anonymous user login, which looks like this:
350 **
351 ** HASH/TIME/anonymous
352 **
@@ -364,11 +384,11 @@
364 char *zNow; /* Current time (julian day number) */
365 char *zCookie; /* The login cookie */
366 const char *zUserAgent; /* The user agent */
367 const char *zCookieName; /* Name of the login cookie */
368 Blob b; /* Blob used during cookie construction */
369 int expires = bSessionCookie ? 0 : ANONYMOUS_COOKIE_LIFESPAN;
370 zCookieName = login_cookie_name();
371 zNow = db_text("0", "SELECT julianday('now')");
372 assert( zCookieName && zNow );
373 blob_init(&b, zNow, -1);
374 zUserAgent = PD("HTTP_USER_AGENT","nil");
@@ -602,11 +622,11 @@
602 ** visitors.
603 **
604 ** anon=1 is advisory and only has effect if there is not some other login
605 ** cookie. anon=2 means always show the captcha.
606 */
607 anonFlag = atoi(PD("anon","0"));
608 if( anonFlag==2 ){
609 g.zLogin = 0;
610 }else{
611 login_check_credentials();
612 if( g.zLogin!=0 ) anonFlag = 0;
@@ -785,11 +805,11 @@
785 @ <p>Currently logged in as <b>%h(g.zLogin)</b>.
786 @ <input type="submit" name="out" value="Logout" autofocus></p>
787 @ </form>
788 }else{
789 unsigned int uSeed = captcha_seed();
790 if( g.zLogin==0 && (anonFlag || zGoto==0) ){
791 zAnonPw = db_text(0, "SELECT pw FROM user"
792 " WHERE login='anonymous'"
793 " AND cap!=''");
794 }else{
795 zAnonPw = 0;
@@ -1418,11 +1438,12 @@
1418 }
1419 }
1420 }
1421 if( zUser==0 ){
1422 /* Invalid cookie */
1423 }else if( fossil_strcmp(zUser, "anonymous")==0 ){
 
1424 /* Cookies of the form "HASH/TIME/anonymous". The TIME must
1425 ** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and
1426 ** the sha1 hash of TIME/USERAGENT/SECRET must match HASH. USERAGENT
1427 ** is the HTTP_USER_AGENT of the client and SECRET is the
1428 ** "captcha-secret" value in the repository. See tag-20250817a
@@ -1444,11 +1465,11 @@
1444 uid = db_int(0,
1445 "SELECT uid FROM user WHERE login='anonymous'"
1446 " AND octet_length(cap)>0"
1447 " AND octet_length(pw)>0"
1448 " AND %.17g>julianday('now')",
1449 rTime+ANONYMOUS_COOKIE_LIFESPAN/86400.0
1450 );
1451 }
1452 }while( uid==0 );
1453 blob_reset(&b);
1454 }else{
@@ -1909,11 +1930,11 @@
1909 ** the anonymous user has Hyperlink permission, then paint a mesage
1910 ** to inform the user that much more information is available by
1911 ** logging in as anonymous.
1912 */
1913 void login_anonymous_available(void){
1914 if( !g.perm.Hyperlink && g.anon.Hyperlink ){
1915 const char *zUrl = PD("PATH_INFO", "");
1916 @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br>
1917 @ Use <a href="%R/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
1918 @ to enable hyperlinks.</p>
1919 }
1920
--- src/login.c
+++ src/login.c
@@ -160,10 +160,11 @@
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 else if( anon_cookie_lifespan()==0 ) return 0;
166 while( 1/*exit-by-break*/ ){
167 zPw = captcha_decode((unsigned int)atoi(zCS), n);
168 if( zPw==0 ) return 0;
169 if( fossil_stricmp(zPw, zPassword)==0 ) break;
170 n++;
@@ -340,13 +341,32 @@
341 free(zCookie);
342 }
343 }
344
345 /*
346 ** SETTING: anon-cookie-lifespan width=10 default=480
347 ** The number of minutes for which an anonymous login cookie is
348 ** valid. Anonymous logins are prohibited if this value is zero.
349 */
350
351
352 /*
353 ** The default lifetime of an anoymous cookie, in minutes.
354 */
355 #define ANONYMOUS_COOKIE_LIFESPAN (8*60)
356
357 /*
358 ** Return the lifetime of an anonymous cookie, in minutes.
359 */
360 int anon_cookie_lifespan(void){
361 static int lifespan = -1;
362 if( lifespan<0 ){
363 lifespan = db_get_int("anon-cookie-lifespan", ANONYMOUS_COOKIE_LIFESPAN);
364 if( lifespan<0 ) lifespan = 0;
365 }
366 return lifespan;
367 }
368
369 /* Sets a cookie for an anonymous user login, which looks like this:
370 **
371 ** HASH/TIME/anonymous
372 **
@@ -364,11 +384,11 @@
384 char *zNow; /* Current time (julian day number) */
385 char *zCookie; /* The login cookie */
386 const char *zUserAgent; /* The user agent */
387 const char *zCookieName; /* Name of the login cookie */
388 Blob b; /* Blob used during cookie construction */
389 int expires = bSessionCookie ? 0 : anon_cookie_lifespan();
390 zCookieName = login_cookie_name();
391 zNow = db_text("0", "SELECT julianday('now')");
392 assert( zCookieName && zNow );
393 blob_init(&b, zNow, -1);
394 zUserAgent = PD("HTTP_USER_AGENT","nil");
@@ -602,11 +622,11 @@
622 ** visitors.
623 **
624 ** anon=1 is advisory and only has effect if there is not some other login
625 ** cookie. anon=2 means always show the captcha.
626 */
627 anonFlag = anon_cookie_lifespan()>0 ? atoi(PD("anon","0")) : 0;
628 if( anonFlag==2 ){
629 g.zLogin = 0;
630 }else{
631 login_check_credentials();
632 if( g.zLogin!=0 ) anonFlag = 0;
@@ -785,11 +805,11 @@
805 @ <p>Currently logged in as <b>%h(g.zLogin)</b>.
806 @ <input type="submit" name="out" value="Logout" autofocus></p>
807 @ </form>
808 }else{
809 unsigned int uSeed = captcha_seed();
810 if( g.zLogin==0 && (anonFlag || zGoto==0) && anon_cookie_lifespan()>0 ){
811 zAnonPw = db_text(0, "SELECT pw FROM user"
812 " WHERE login='anonymous'"
813 " AND cap!=''");
814 }else{
815 zAnonPw = 0;
@@ -1418,11 +1438,12 @@
1438 }
1439 }
1440 }
1441 if( zUser==0 ){
1442 /* Invalid cookie */
1443 }else if( fossil_strcmp(zUser, "anonymous")==0
1444 && anon_cookie_lifespan()>0 ){
1445 /* Cookies of the form "HASH/TIME/anonymous". The TIME must
1446 ** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and
1447 ** the sha1 hash of TIME/USERAGENT/SECRET must match HASH. USERAGENT
1448 ** is the HTTP_USER_AGENT of the client and SECRET is the
1449 ** "captcha-secret" value in the repository. See tag-20250817a
@@ -1444,11 +1465,11 @@
1465 uid = db_int(0,
1466 "SELECT uid FROM user WHERE login='anonymous'"
1467 " AND octet_length(cap)>0"
1468 " AND octet_length(pw)>0"
1469 " AND %.17g>julianday('now')",
1470 rTime+anon_cookie_lifespan()/1440.0
1471 );
1472 }
1473 }while( uid==0 );
1474 blob_reset(&b);
1475 }else{
@@ -1909,11 +1930,11 @@
1930 ** the anonymous user has Hyperlink permission, then paint a mesage
1931 ** to inform the user that much more information is available by
1932 ** logging in as anonymous.
1933 */
1934 void login_anonymous_available(void){
1935 if( !g.perm.Hyperlink && g.anon.Hyperlink && anon_cookie_lifespan()>0 ){
1936 const char *zUrl = PD("PATH_INFO", "");
1937 @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br>
1938 @ Use <a href="%R/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
1939 @ to enable hyperlinks.</p>
1940 }
1941
+14
--- src/setup.c
+++ src/setup.c
@@ -518,10 +518,17 @@
518518
"robot-restrict", "rbrestrict", robot_restrict_default(), 0);
519519
520520
@ <hr>
521521
addAutoHyperlinkSettings();
522522
523
+ @ <hr>
524
+ entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
525
+ "anoncookls", "840", 0);
526
+ @ <p>The number of minutes for which an anonymous login cookie is valid.
527
+ @ Set to zero to disable anonymous login.
528
+ @ (property: anon-cookie-lifespan)
529
+
523530
@ <hr>
524531
entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
525532
"0.0", 0);
526533
@ <p>Some expensive operations (such as computing tarballs, zip archives,
527534
@ or annotation/blame pages) are prohibited if the load average on the host
@@ -769,10 +776,17 @@
769776
@ "anonymous" that will automatically fill in the CAPTCHA password.
770777
@ This is less secure than forcing the user to do it manually, but is
771778
@ probably secure enough and it is certainly more convenient for
772779
@ anonymous users. (Property: "auto-captcha")</p>
773780
781
+ @ <hr>
782
+ entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
783
+ "anoncookls", "840", 0);
784
+ @ <p>The number of minutes for which an anonymous login cookie is valid.
785
+ @ Set to zero to disable anonymous logins.
786
+ @ (property: anon-cookie-lifespan)
787
+
774788
@ <hr>
775789
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
776790
@ </div></form>
777791
db_end_transaction(0);
778792
style_finish_page();
779793
--- src/setup.c
+++ src/setup.c
@@ -518,10 +518,17 @@
518 "robot-restrict", "rbrestrict", robot_restrict_default(), 0);
519
520 @ <hr>
521 addAutoHyperlinkSettings();
522
 
 
 
 
 
 
 
523 @ <hr>
524 entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
525 "0.0", 0);
526 @ <p>Some expensive operations (such as computing tarballs, zip archives,
527 @ or annotation/blame pages) are prohibited if the load average on the host
@@ -769,10 +776,17 @@
769 @ "anonymous" that will automatically fill in the CAPTCHA password.
770 @ This is less secure than forcing the user to do it manually, but is
771 @ probably secure enough and it is certainly more convenient for
772 @ anonymous users. (Property: "auto-captcha")</p>
773
 
 
 
 
 
 
 
774 @ <hr>
775 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
776 @ </div></form>
777 db_end_transaction(0);
778 style_finish_page();
779
--- src/setup.c
+++ src/setup.c
@@ -518,10 +518,17 @@
518 "robot-restrict", "rbrestrict", robot_restrict_default(), 0);
519
520 @ <hr>
521 addAutoHyperlinkSettings();
522
523 @ <hr>
524 entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
525 "anoncookls", "840", 0);
526 @ <p>The number of minutes for which an anonymous login cookie is valid.
527 @ Set to zero to disable anonymous login.
528 @ (property: anon-cookie-lifespan)
529
530 @ <hr>
531 entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
532 "0.0", 0);
533 @ <p>Some expensive operations (such as computing tarballs, zip archives,
534 @ or annotation/blame pages) are prohibited if the load average on the host
@@ -769,10 +776,17 @@
776 @ "anonymous" that will automatically fill in the CAPTCHA password.
777 @ This is less secure than forcing the user to do it manually, but is
778 @ probably secure enough and it is certainly more convenient for
779 @ anonymous users. (Property: "auto-captcha")</p>
780
781 @ <hr>
782 entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
783 "anoncookls", "840", 0);
784 @ <p>The number of minutes for which an anonymous login cookie is valid.
785 @ Set to zero to disable anonymous logins.
786 @ (property: anon-cookie-lifespan)
787
788 @ <hr>
789 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
790 @ </div></form>
791 db_end_transaction(0);
792 style_finish_page();
793

Keyboard Shortcuts

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