Fossil SCM

Try to simplify and rationalize the defenses against cross-site request forgery attacks. A hodgepodge of techniques have been used in the past. This changes attempts to make everything work more alike and to centralize CSRF defenses for easier auditing.

drh 2023-09-18 13:18 trunk
Commit 88a402fe2a5641990497eeed13f4848903c94ba37d860997c430da874849d7a8
+46 -22
--- src/cgi.c
+++ src/cgi.c
@@ -700,42 +700,66 @@
700700
nBase = (int)strlen(g.zBaseURL);
701701
if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0;
702702
if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0;
703703
return 1;
704704
}
705
+
706
+/*
707
+** Return true if the current CGI request is a POST request
708
+*/
709
+static int cgi_is_post_request(void){
710
+ const char *zMethod = P("REQUEST_METHOD");
711
+ if( zMethod==0 ) return 0;
712
+ if( strcmp(zMethod,"POST")!=0 ) return 0;
713
+ return 1;
714
+}
705715
706716
/*
707717
** Return true if the current request appears to be safe from a
708
-** Cross-Site Request Forgery (CSRF) attack. Conditions that must
709
-** be met:
718
+** Cross-Site Request Forgery (CSRF) attack. The level of checking
719
+** is determined by the parameter. The higher the number, the more
720
+** secure we are:
721
+**
722
+** 0: Request must come from the same origin
723
+** 1: Same origin and must be a POST request
724
+** 2: All of the above plus must have a valid CSRF token
725
+**
726
+** Results are cached in the g.okCsrf variable. The g.okCsrf value
727
+** has meaning as follows:
710728
**
711
-** * The HTTP_REFERER must have the same origin
712
-** * The REQUEST_METHOD must be POST - or requirePost==0
729
+** -1: Not a secure request
730
+** 0: Status unknown
731
+** 1: Request comes from the same origin
732
+** 2: (1) plus it is a POST request
733
+** 3: (2) plus there is a valid "csrf" token in the request
713734
*/
714
-int cgi_csrf_safe(int requirePost){
715
- if( requirePost ){
716
- const char *zMethod = P("REQUEST_METHOD");
717
- if( zMethod==0 ) return 0;
718
- if( strcmp(zMethod,"POST")!=0 ) return 0;
735
+int cgi_csrf_safe(int securityLevel){
736
+ if( g.okCsrf<0 ) return 0;
737
+ if( g.okCsrf==0 ){
738
+ if( !cgi_same_origin() ){
739
+ g.okCsrf = -1;
740
+ }else{
741
+ g.okCsrf = 1;
742
+ if( cgi_is_post_request() ){
743
+ g.okCsrf = 2;
744
+ if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){
745
+ g.okCsrf = 3;
746
+ }
747
+ }
748
+ }
719749
}
720
- return cgi_same_origin();
750
+ return g.okCsrf >= (securityLevel+1);
721751
}
722752
723753
/*
724
-** If bLoginVerifyCsrf is true, this calls login_verify_csrf() to
725
-** verify that the secret injected by login_insert_csrf_secret() is in
726
-** the CGI environment and valid. If that fails, it does so
727
-** fatally. If that passes and cgi_csrf_safe(1) returns false, this
728
-** fails fatally with a message about a cross-site scripting attempt,
729
-** else it returns without side effects.
754
+** Verify that CSRF defenses are maximal - that the request comes from
755
+** the same origin, that it is a POST request, and that there is a valid
756
+** "csrf" token. If this is not the case, fail immediately.
730757
*/
731
-void cgi_csrf_verify(int bLoginVerifyCsrf){
732
- if( bLoginVerifyCsrf!=0 ){
733
- login_verify_csrf_secret();
734
- }
735
- if( 0==cgi_csrf_safe(1) ){
736
- fossil_fatal("Cross-site request forgery attempt");
758
+void cgi_csrf_verify(void){
759
+ if( !cgi_csrf_safe(2) ){
760
+ fossil_fatal("Cross-site Request Forgery detected");
737761
}
738762
}
739763
740764
/*
741765
** Information about all query parameters, post parameter, cookies and
742766
--- src/cgi.c
+++ src/cgi.c
@@ -700,42 +700,66 @@
700 nBase = (int)strlen(g.zBaseURL);
701 if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0;
702 if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0;
703 return 1;
704 }
 
 
 
 
 
 
 
 
 
 
705
706 /*
707 ** Return true if the current request appears to be safe from a
708 ** Cross-Site Request Forgery (CSRF) attack. Conditions that must
709 ** be met:
 
 
 
 
 
 
 
 
710 **
711 ** * The HTTP_REFERER must have the same origin
712 ** * The REQUEST_METHOD must be POST - or requirePost==0
 
 
 
713 */
714 int cgi_csrf_safe(int requirePost){
715 if( requirePost ){
716 const char *zMethod = P("REQUEST_METHOD");
717 if( zMethod==0 ) return 0;
718 if( strcmp(zMethod,"POST")!=0 ) return 0;
 
 
 
 
 
 
 
 
 
719 }
720 return cgi_same_origin();
721 }
722
723 /*
724 ** If bLoginVerifyCsrf is true, this calls login_verify_csrf() to
725 ** verify that the secret injected by login_insert_csrf_secret() is in
726 ** the CGI environment and valid. If that fails, it does so
727 ** fatally. If that passes and cgi_csrf_safe(1) returns false, this
728 ** fails fatally with a message about a cross-site scripting attempt,
729 ** else it returns without side effects.
730 */
731 void cgi_csrf_verify(int bLoginVerifyCsrf){
732 if( bLoginVerifyCsrf!=0 ){
733 login_verify_csrf_secret();
734 }
735 if( 0==cgi_csrf_safe(1) ){
736 fossil_fatal("Cross-site request forgery attempt");
737 }
738 }
739
740 /*
741 ** Information about all query parameters, post parameter, cookies and
742
--- src/cgi.c
+++ src/cgi.c
@@ -700,42 +700,66 @@
700 nBase = (int)strlen(g.zBaseURL);
701 if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0;
702 if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0;
703 return 1;
704 }
705
706 /*
707 ** Return true if the current CGI request is a POST request
708 */
709 static int cgi_is_post_request(void){
710 const char *zMethod = P("REQUEST_METHOD");
711 if( zMethod==0 ) return 0;
712 if( strcmp(zMethod,"POST")!=0 ) return 0;
713 return 1;
714 }
715
716 /*
717 ** Return true if the current request appears to be safe from a
718 ** Cross-Site Request Forgery (CSRF) attack. The level of checking
719 ** is determined by the parameter. The higher the number, the more
720 ** secure we are:
721 **
722 ** 0: Request must come from the same origin
723 ** 1: Same origin and must be a POST request
724 ** 2: All of the above plus must have a valid CSRF token
725 **
726 ** Results are cached in the g.okCsrf variable. The g.okCsrf value
727 ** has meaning as follows:
728 **
729 ** -1: Not a secure request
730 ** 0: Status unknown
731 ** 1: Request comes from the same origin
732 ** 2: (1) plus it is a POST request
733 ** 3: (2) plus there is a valid "csrf" token in the request
734 */
735 int cgi_csrf_safe(int securityLevel){
736 if( g.okCsrf<0 ) return 0;
737 if( g.okCsrf==0 ){
738 if( !cgi_same_origin() ){
739 g.okCsrf = -1;
740 }else{
741 g.okCsrf = 1;
742 if( cgi_is_post_request() ){
743 g.okCsrf = 2;
744 if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){
745 g.okCsrf = 3;
746 }
747 }
748 }
749 }
750 return g.okCsrf >= (securityLevel+1);
751 }
752
753 /*
754 ** Verify that CSRF defenses are maximal - that the request comes from
755 ** the same origin, that it is a POST request, and that there is a valid
756 ** "csrf" token. If this is not the case, fail immediately.
 
 
 
757 */
758 void cgi_csrf_verify(void){
759 if( !cgi_csrf_safe(2) ){
760 fossil_fatal("Cross-site Request Forgery detected");
 
 
 
761 }
762 }
763
764 /*
765 ** Information about all query parameters, post parameter, cookies and
766
+1 -2
--- src/event.c
+++ src/event.c
@@ -468,12 +468,11 @@
468468
rid
469469
);
470470
}
471471
}
472472
zETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime);
473
- if( P("submit")!=0 && (zBody!=0 && zComment!=0) ){
474
- login_verify_csrf_secret();
473
+ if( P("submit")!=0 && (zBody!=0 && zComment!=0) && cgi_csrf_safe(2) ){
475474
if ( !event_commit_common(rid, zId, zBody, zETime,
476475
zMimetype, zComment, zTags,
477476
zClrFlag[0] ? zClr : 0) ){
478477
style_header("Error");
479478
@ Internal error: Fossil tried to make an invalid artifact for
480479
--- src/event.c
+++ src/event.c
@@ -468,12 +468,11 @@
468 rid
469 );
470 }
471 }
472 zETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime);
473 if( P("submit")!=0 && (zBody!=0 && zComment!=0) ){
474 login_verify_csrf_secret();
475 if ( !event_commit_common(rid, zId, zBody, zETime,
476 zMimetype, zComment, zTags,
477 zClrFlag[0] ? zClr : 0) ){
478 style_header("Error");
479 @ Internal error: Fossil tried to make an invalid artifact for
480
--- src/event.c
+++ src/event.c
@@ -468,12 +468,11 @@
468 rid
469 );
470 }
471 }
472 zETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime);
473 if( P("submit")!=0 && (zBody!=0 && zComment!=0) && cgi_csrf_safe(2) ){
 
474 if ( !event_commit_common(rid, zId, zBody, zETime,
475 zMimetype, zComment, zTags,
476 zClrFlag[0] ? zClr : 0) ){
477 style_header("Error");
478 @ Internal error: Fossil tried to make an invalid artifact for
479
+2 -3
--- src/forum.c
+++ src/forum.c
@@ -1423,11 +1423,11 @@
14231423
login_check_credentials();
14241424
if( forumpost_may_close()==0 ){
14251425
login_needed(g.anon.Admin);
14261426
return;
14271427
}
1428
- cgi_csrf_verify(1);
1428
+ cgi_csrf_verify();
14291429
fpid = symbolic_name_to_rid(zFpid, "f");
14301430
if( fpid<=0 ){
14311431
webpage_error("Missing or invalid fpid query parameter");
14321432
}
14331433
fClose = sqlite3_strglob("*_close*", g.zPath)==0;
@@ -1842,14 +1842,13 @@
18421842
}
18431843
}
18441844
18451845
@ <h2>Settings</h2>
18461846
@ <p>Configuration settings specific to the forum.</p>
1847
- if( P("submit") && cgi_csrf_safe(1) ){
1847
+ if( P("submit") && cgi_csrf_safe(2) ){
18481848
int i = 0;
18491849
const char *zSetting;
1850
- login_verify_csrf_secret();
18511850
db_begin_transaction();
18521851
while( (zSetting = zSettingsBool[i++]) ){
18531852
const char *z = P(zSetting);
18541853
if( !z || !z[0] ) z = "off";
18551854
db_set(zSetting/*works-like:"x"*/, z, 0);
18561855
--- src/forum.c
+++ src/forum.c
@@ -1423,11 +1423,11 @@
1423 login_check_credentials();
1424 if( forumpost_may_close()==0 ){
1425 login_needed(g.anon.Admin);
1426 return;
1427 }
1428 cgi_csrf_verify(1);
1429 fpid = symbolic_name_to_rid(zFpid, "f");
1430 if( fpid<=0 ){
1431 webpage_error("Missing or invalid fpid query parameter");
1432 }
1433 fClose = sqlite3_strglob("*_close*", g.zPath)==0;
@@ -1842,14 +1842,13 @@
1842 }
1843 }
1844
1845 @ <h2>Settings</h2>
1846 @ <p>Configuration settings specific to the forum.</p>
1847 if( P("submit") && cgi_csrf_safe(1) ){
1848 int i = 0;
1849 const char *zSetting;
1850 login_verify_csrf_secret();
1851 db_begin_transaction();
1852 while( (zSetting = zSettingsBool[i++]) ){
1853 const char *z = P(zSetting);
1854 if( !z || !z[0] ) z = "off";
1855 db_set(zSetting/*works-like:"x"*/, z, 0);
1856
--- src/forum.c
+++ src/forum.c
@@ -1423,11 +1423,11 @@
1423 login_check_credentials();
1424 if( forumpost_may_close()==0 ){
1425 login_needed(g.anon.Admin);
1426 return;
1427 }
1428 cgi_csrf_verify();
1429 fpid = symbolic_name_to_rid(zFpid, "f");
1430 if( fpid<=0 ){
1431 webpage_error("Missing or invalid fpid query parameter");
1432 }
1433 fClose = sqlite3_strglob("*_close*", g.zPath)==0;
@@ -1842,14 +1842,13 @@
1842 }
1843 }
1844
1845 @ <h2>Settings</h2>
1846 @ <p>Configuration settings specific to the forum.</p>
1847 if( P("submit") && cgi_csrf_safe(2) ){
1848 int i = 0;
1849 const char *zSetting;
 
1850 db_begin_transaction();
1851 while( (zSetting = zSettingsBool[i++]) ){
1852 const char *z = P(zSetting);
1853 if( !z || !z[0] ) z = "off";
1854 db_set(zSetting/*works-like:"x"*/, z, 0);
1855
+1 -3
--- src/info.c
+++ src/info.c
@@ -3213,15 +3213,14 @@
32133213
zNewTag = PDT("tagname","");
32143214
zNewBrFlag = P("newbr") ? " checked" : "";
32153215
zNewBranch = PDT("brname","");
32163216
zCloseFlag = P("close") ? " checked" : "";
32173217
zHideFlag = P("hide") ? " checked" : "";
3218
- if( P("apply") && cgi_csrf_safe(1) ){
3218
+ if( P("apply") && cgi_csrf_safe(2) ){
32193219
Blob ctrl;
32203220
char *zNow;
32213221
3222
- login_verify_csrf_secret();
32233222
blob_zero(&ctrl);
32243223
zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
32253224
blob_appendf(&ctrl, "D %s\n", zNow);
32263225
init_newtags();
32273226
if( zNewColorFlag[0]
@@ -3300,11 +3299,10 @@
33003299
blob_reset(&suffix);
33013300
}
33023301
@ <p>Make changes to attributes of check-in
33033302
@ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p>
33043303
form_begin(0, "%R/ci_edit");
3305
- login_insert_csrf_secret();
33063304
@ <div><input type="hidden" name="r" value="%s(zUuid)">
33073305
@ <table border="0" cellspacing="10">
33083306
33093307
@ <tr><th align="right" valign="top">User:</th>
33103308
@ <td valign="top">
33113309
--- src/info.c
+++ src/info.c
@@ -3213,15 +3213,14 @@
3213 zNewTag = PDT("tagname","");
3214 zNewBrFlag = P("newbr") ? " checked" : "";
3215 zNewBranch = PDT("brname","");
3216 zCloseFlag = P("close") ? " checked" : "";
3217 zHideFlag = P("hide") ? " checked" : "";
3218 if( P("apply") && cgi_csrf_safe(1) ){
3219 Blob ctrl;
3220 char *zNow;
3221
3222 login_verify_csrf_secret();
3223 blob_zero(&ctrl);
3224 zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
3225 blob_appendf(&ctrl, "D %s\n", zNow);
3226 init_newtags();
3227 if( zNewColorFlag[0]
@@ -3300,11 +3299,10 @@
3300 blob_reset(&suffix);
3301 }
3302 @ <p>Make changes to attributes of check-in
3303 @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p>
3304 form_begin(0, "%R/ci_edit");
3305 login_insert_csrf_secret();
3306 @ <div><input type="hidden" name="r" value="%s(zUuid)">
3307 @ <table border="0" cellspacing="10">
3308
3309 @ <tr><th align="right" valign="top">User:</th>
3310 @ <td valign="top">
3311
--- src/info.c
+++ src/info.c
@@ -3213,15 +3213,14 @@
3213 zNewTag = PDT("tagname","");
3214 zNewBrFlag = P("newbr") ? " checked" : "";
3215 zNewBranch = PDT("brname","");
3216 zCloseFlag = P("close") ? " checked" : "";
3217 zHideFlag = P("hide") ? " checked" : "";
3218 if( P("apply") && cgi_csrf_safe(2) ){
3219 Blob ctrl;
3220 char *zNow;
3221
 
3222 blob_zero(&ctrl);
3223 zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
3224 blob_appendf(&ctrl, "D %s\n", zNow);
3225 init_newtags();
3226 if( zNewColorFlag[0]
@@ -3300,11 +3299,10 @@
3299 blob_reset(&suffix);
3300 }
3301 @ <p>Make changes to attributes of check-in
3302 @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p>
3303 form_begin(0, "%R/ci_edit");
 
3304 @ <div><input type="hidden" name="r" value="%s(zUuid)">
3305 @ <table border="0" cellspacing="10">
3306
3307 @ <tr><th align="right" valign="top">User:</th>
3308 @ <td valign="top">
3309
+2 -17
--- src/login.c
+++ src/login.c
@@ -583,11 +583,11 @@
583583
constant_time_cmp_function, 0, 0);
584584
zUsername = P("u");
585585
zPasswd = P("p");
586586
anonFlag = g.zLogin==0 && PB("anon");
587587
/* Handle log-out requests */
588
- if( P("out") ){
588
+ if( P("out") && cgi_csrf_safe(2) ){
589589
login_clear_login_data();
590590
redirect_to_g();
591591
return;
592592
}
593593
@@ -598,10 +598,11 @@
598598
}
599599
600600
/* Deal with password-change requests */
601601
if( g.perm.Password && zPasswd
602602
&& (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0
603
+ && cgi_csrf_safe(2)
603604
){
604605
/* If there is not a "real" login, we cannot change any password. */
605606
if( g.zLogin ){
606607
/* The user requests a password change */
607608
zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
@@ -1805,26 +1806,10 @@
18051806
*/
18061807
void login_insert_csrf_secret(void){
18071808
@ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
18081809
}
18091810
1810
-/*
1811
-** Before using the results of a form, first call this routine to verify
1812
-** that this Anti-CSRF token is present and is valid. If the Anti-CSRF token
1813
-** is missing or is incorrect, that indicates a cross-site scripting attack.
1814
-** If the event of an attack is detected, an error message is generated and
1815
-** all further processing is aborted.
1816
-*/
1817
-void login_verify_csrf_secret(void){
1818
- if( g.okCsrf ) return;
1819
- if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){
1820
- g.okCsrf = 1;
1821
- return;
1822
- }
1823
- fossil_fatal("Cross-site request forgery attempt");
1824
-}
1825
-
18261811
/*
18271812
** Check to see if the candidate username zUserID is already used.
18281813
** Return 1 if it is already in use. Return 0 if the name is
18291814
** available for a self-registeration.
18301815
*/
18311816
--- src/login.c
+++ src/login.c
@@ -583,11 +583,11 @@
583 constant_time_cmp_function, 0, 0);
584 zUsername = P("u");
585 zPasswd = P("p");
586 anonFlag = g.zLogin==0 && PB("anon");
587 /* Handle log-out requests */
588 if( P("out") ){
589 login_clear_login_data();
590 redirect_to_g();
591 return;
592 }
593
@@ -598,10 +598,11 @@
598 }
599
600 /* Deal with password-change requests */
601 if( g.perm.Password && zPasswd
602 && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0
 
603 ){
604 /* If there is not a "real" login, we cannot change any password. */
605 if( g.zLogin ){
606 /* The user requests a password change */
607 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
@@ -1805,26 +1806,10 @@
1805 */
1806 void login_insert_csrf_secret(void){
1807 @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
1808 }
1809
1810 /*
1811 ** Before using the results of a form, first call this routine to verify
1812 ** that this Anti-CSRF token is present and is valid. If the Anti-CSRF token
1813 ** is missing or is incorrect, that indicates a cross-site scripting attack.
1814 ** If the event of an attack is detected, an error message is generated and
1815 ** all further processing is aborted.
1816 */
1817 void login_verify_csrf_secret(void){
1818 if( g.okCsrf ) return;
1819 if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){
1820 g.okCsrf = 1;
1821 return;
1822 }
1823 fossil_fatal("Cross-site request forgery attempt");
1824 }
1825
1826 /*
1827 ** Check to see if the candidate username zUserID is already used.
1828 ** Return 1 if it is already in use. Return 0 if the name is
1829 ** available for a self-registeration.
1830 */
1831
--- src/login.c
+++ src/login.c
@@ -583,11 +583,11 @@
583 constant_time_cmp_function, 0, 0);
584 zUsername = P("u");
585 zPasswd = P("p");
586 anonFlag = g.zLogin==0 && PB("anon");
587 /* Handle log-out requests */
588 if( P("out") && cgi_csrf_safe(2) ){
589 login_clear_login_data();
590 redirect_to_g();
591 return;
592 }
593
@@ -598,10 +598,11 @@
598 }
599
600 /* Deal with password-change requests */
601 if( g.perm.Password && zPasswd
602 && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0
603 && cgi_csrf_safe(2)
604 ){
605 /* If there is not a "real" login, we cannot change any password. */
606 if( g.zLogin ){
607 /* The user requests a password change */
608 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
@@ -1805,26 +1806,10 @@
1806 */
1807 void login_insert_csrf_secret(void){
1808 @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
1809 }
1810
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1811 /*
1812 ** Check to see if the candidate username zUserID is already used.
1813 ** Return 1 if it is already in use. Return 0 if the name is
1814 ** available for a self-registeration.
1815 */
1816
+5 -1
--- src/main.c
+++ src/main.c
@@ -257,11 +257,15 @@
257257
struct TclContext tcl;
258258
#endif
259259
260260
/* For defense against Cross-site Request Forgery attacks */
261261
char zCsrfToken[12]; /* Value of the anti-CSRF token */
262
- int okCsrf; /* Anti-CSRF token is present and valid */
262
+ int okCsrf; /* -1: unsafe
263
+ ** 0: unknown
264
+ ** 1: same origin
265
+ ** 2: same origin + is POST
266
+ ** 3: same origin, POST, valid csrf token */
263267
264268
int parseCnt[10]; /* Counts of artifacts parsed */
265269
FILE *fDebug; /* Write debug information here, if the file exists */
266270
#ifdef FOSSIL_ENABLE_TH1_HOOKS
267271
int fNoThHook; /* Disable all TH1 command/webpage hooks */
268272
--- src/main.c
+++ src/main.c
@@ -257,11 +257,15 @@
257 struct TclContext tcl;
258 #endif
259
260 /* For defense against Cross-site Request Forgery attacks */
261 char zCsrfToken[12]; /* Value of the anti-CSRF token */
262 int okCsrf; /* Anti-CSRF token is present and valid */
 
 
 
 
263
264 int parseCnt[10]; /* Counts of artifacts parsed */
265 FILE *fDebug; /* Write debug information here, if the file exists */
266 #ifdef FOSSIL_ENABLE_TH1_HOOKS
267 int fNoThHook; /* Disable all TH1 command/webpage hooks */
268
--- src/main.c
+++ src/main.c
@@ -257,11 +257,15 @@
257 struct TclContext tcl;
258 #endif
259
260 /* For defense against Cross-site Request Forgery attacks */
261 char zCsrfToken[12]; /* Value of the anti-CSRF token */
262 int okCsrf; /* -1: unsafe
263 ** 0: unknown
264 ** 1: same origin
265 ** 2: same origin + is POST
266 ** 3: same origin, POST, valid csrf token */
267
268 int parseCnt[10]; /* Counts of artifacts parsed */
269 FILE *fDebug; /* Write debug information here, if the file exists */
270 #ifdef FOSSIL_ENABLE_TH1_HOOKS
271 int fNoThHook; /* Disable all TH1 command/webpage hooks */
272
+3 -5
--- src/report.c
+++ src/report.c
@@ -471,16 +471,15 @@
471471
zClrKey = trim_string(PD("k",""));
472472
zDesc = trim_string(PD("d",""));
473473
zMimetype = P("m");
474474
zTag = P("x");
475475
report_update_reportfmt_table();
476
- if( rn>0 && P("del2") ){
477
- login_verify_csrf_secret();
476
+ if( rn>0 && P("del2") && cgi_csrf_safe(2) ){
478477
db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn);
479478
cgi_redirect("reportlist");
480479
return;
481
- }else if( rn>0 && P("del1") ){
480
+ }else if( rn>0 && P("del1") && cgi_csrf_safe(2) ){
482481
zTitle = db_text(0, "SELECT title FROM reportfmt "
483482
"WHERE rn=%d", rn);
484483
if( zTitle==0 ) cgi_redirect("reportlist");
485484
486485
style_header("Are You Sure?");
@@ -514,12 +513,11 @@
514513
&& db_exists("SELECT 1 FROM reportfmt WHERE title=%Q and rn<>%d",
515514
zTitle, rn)
516515
){
517516
zErr = mprintf("There is already another report named \"%h\"", zTitle);
518517
}
519
- if( zErr==0 ){
520
- login_verify_csrf_secret();
518
+ if( zErr==0 && cgi_csrf_safe(2) ){
521519
if( zTag && zTag[0]==0 ) zTag = 0;
522520
if( zDesc && zDesc[0]==0 ){ zDesc = 0; zMimetype = 0; }
523521
if( zMimetype && zMimetype[0]==0 ){ zDesc = 0; zMimetype = 0; }
524522
if( rn>0 ){
525523
db_multi_exec(
526524
--- src/report.c
+++ src/report.c
@@ -471,16 +471,15 @@
471 zClrKey = trim_string(PD("k",""));
472 zDesc = trim_string(PD("d",""));
473 zMimetype = P("m");
474 zTag = P("x");
475 report_update_reportfmt_table();
476 if( rn>0 && P("del2") ){
477 login_verify_csrf_secret();
478 db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn);
479 cgi_redirect("reportlist");
480 return;
481 }else if( rn>0 && P("del1") ){
482 zTitle = db_text(0, "SELECT title FROM reportfmt "
483 "WHERE rn=%d", rn);
484 if( zTitle==0 ) cgi_redirect("reportlist");
485
486 style_header("Are You Sure?");
@@ -514,12 +513,11 @@
514 && db_exists("SELECT 1 FROM reportfmt WHERE title=%Q and rn<>%d",
515 zTitle, rn)
516 ){
517 zErr = mprintf("There is already another report named \"%h\"", zTitle);
518 }
519 if( zErr==0 ){
520 login_verify_csrf_secret();
521 if( zTag && zTag[0]==0 ) zTag = 0;
522 if( zDesc && zDesc[0]==0 ){ zDesc = 0; zMimetype = 0; }
523 if( zMimetype && zMimetype[0]==0 ){ zDesc = 0; zMimetype = 0; }
524 if( rn>0 ){
525 db_multi_exec(
526
--- src/report.c
+++ src/report.c
@@ -471,16 +471,15 @@
471 zClrKey = trim_string(PD("k",""));
472 zDesc = trim_string(PD("d",""));
473 zMimetype = P("m");
474 zTag = P("x");
475 report_update_reportfmt_table();
476 if( rn>0 && P("del2") && cgi_csrf_safe(2) ){
 
477 db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn);
478 cgi_redirect("reportlist");
479 return;
480 }else if( rn>0 && P("del1") && cgi_csrf_safe(2) ){
481 zTitle = db_text(0, "SELECT title FROM reportfmt "
482 "WHERE rn=%d", rn);
483 if( zTitle==0 ) cgi_redirect("reportlist");
484
485 style_header("Are You Sure?");
@@ -514,12 +513,11 @@
513 && db_exists("SELECT 1 FROM reportfmt WHERE title=%Q and rn<>%d",
514 zTitle, rn)
515 ){
516 zErr = mprintf("There is already another report named \"%h\"", zTitle);
517 }
518 if( zErr==0 && cgi_csrf_safe(2) ){
 
519 if( zTag && zTag[0]==0 ) zTag = 0;
520 if( zDesc && zDesc[0]==0 ){ zDesc = 0; zMimetype = 0; }
521 if( zMimetype && zMimetype[0]==0 ){ zDesc = 0; zMimetype = 0; }
522 if( rn>0 ){
523 db_multi_exec(
524
+8 -16
--- src/setup.c
+++ src/setup.c
@@ -202,12 +202,11 @@
202202
if( zQ==0 && !disabled && P("submit") ){
203203
zQ = "off";
204204
}
205205
if( zQ ){
206206
int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ);
207
- if( iQ!=iVal ){
208
- login_verify_csrf_secret();
207
+ if( iQ!=iVal && cgi_csrf_safe(2) ){
209208
db_protect_only(PROTECT_NONE);
210209
db_set(zVar/*works-like:"x"*/, iQ ? "1" : "0", 0);
211210
db_protect_pop();
212211
setup_incr_cfgcnt();
213212
admin_log("Set option [%q] to [%q].",
@@ -237,13 +236,12 @@
237236
const char *zDflt, /* Default value if CONFIG table entry does not exist */
238237
int disabled /* 1 if disabled */
239238
){
240239
const char *zVal = db_get(zVar, zDflt);
241240
const char *zQ = P(zQParm);
242
- if( zQ && fossil_strcmp(zQ,zVal)!=0 ){
241
+ if( zQ && fossil_strcmp(zQ,zVal)!=0 && cgi_csrf_safe(2) ){
243242
const int nZQ = (int)strlen(zQ);
244
- login_verify_csrf_secret();
245243
setup_incr_cfgcnt();
246244
db_protect_only(PROTECT_NONE);
247245
db_set(zVar/*works-like:"x"*/, zQ, 0);
248246
db_protect_pop();
249247
admin_log("Set entry_attribute %Q to: %.*s%s",
@@ -270,13 +268,12 @@
270268
const char *zDflt, /* Default value if CONFIG table entry does not exist */
271269
int disabled /* 1 if the textarea should not be editable */
272270
){
273271
const char *z = db_get(zVar, zDflt);
274272
const char *zQ = P(zQP);
275
- if( zQ && !disabled && fossil_strcmp(zQ,z)!=0){
273
+ if( zQ && !disabled && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){
276274
const int nZQ = (int)strlen(zQ);
277
- login_verify_csrf_secret();
278275
db_protect_only(PROTECT_NONE);
279276
db_set(zVar/*works-like:"x"*/, zQ, 0);
280277
db_protect_pop();
281278
setup_incr_cfgcnt();
282279
admin_log("Set textarea_attribute %Q to: %.*s%s",
@@ -309,13 +306,12 @@
309306
const char *const *azChoice /* Choices in pairs (VAR value, Display) */
310307
){
311308
const char *z = db_get(zVar, zDflt);
312309
const char *zQ = P(zQP);
313310
int i;
314
- if( zQ && fossil_strcmp(zQ,z)!=0){
311
+ if( zQ && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){
315312
const int nZQ = (int)strlen(zQ);
316
- login_verify_csrf_secret();
317313
db_unprotect(PROTECT_ALL);
318314
db_set(zVar/*works-like:"x"*/, zQ, 0);
319315
setup_incr_cfgcnt();
320316
db_protect_pop();
321317
admin_log("Set multiple_choice_attribute %Q to: %.*s%s",
@@ -1822,19 +1818,18 @@
18221818
go = 1;
18231819
}else if( P("tablelist") ){
18241820
zQ = sqlite3_mprintf("SELECT*FROM pragma_table_list ORDER BY schema, name");
18251821
go = 1;
18261822
}
1827
- if( go ){
1823
+ if( go && cgi_csrf_safe(2) ){
18281824
sqlite3_stmt *pStmt;
18291825
int rc;
18301826
const char *zTail;
18311827
int nCol;
18321828
int nRow = 0;
18331829
int i;
18341830
@ <hr>
1835
- login_verify_csrf_secret();
18361831
sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0);
18371832
search_sql_setup(g.db);
18381833
rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail);
18391834
if( rc!=SQLITE_OK ){
18401835
@ <div class="generalError">%h(sqlite3_errmsg(g.db))</div>
@@ -1913,22 +1908,20 @@
19131908
style_header("Raw TH1 Commands");
19141909
@ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
19151910
@ run by this page. If Tcl integration was enabled at compile-time and
19161911
@ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
19171912
@
1918
- @ <form method="post" action="%R/admin_th1">
1919
- login_insert_csrf_secret();
1913
+ form_begin(0, "%R/admin_th1");
19201914
@ TH1:<br>
19211915
@ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br>
19221916
@ <input type="submit" name="go" value="Run TH1">
19231917
@ </form>
1924
- if( go ){
1918
+ if( go && cgi_csrf_safe(2) ){
19251919
const char *zR;
19261920
int rc;
19271921
int n;
19281922
@ <hr>
1929
- login_verify_csrf_secret();
19301923
rc = Th_Eval(g.interp, 0, zQ, -1);
19311924
zR = Th_GetResult(g.interp, &n);
19321925
if( rc==TH_OK ){
19331926
@ <pre class="th1result">%h(zR)</pre>
19341927
}else{
@@ -2173,17 +2166,16 @@
21732166
login_needed(0);
21742167
return;
21752168
}
21762169
style_set_current_feature("setup");
21772170
style_header("URL Alias Configuration");
2178
- if( P("submit")!=0 ){
2171
+ if( P("submit")!=0 && cgi_csrf_safe(2) ){
21792172
Blob token;
21802173
Blob sql;
21812174
const char *zNewName;
21822175
const char *zValue;
21832176
char zCnt[10];
2184
- login_verify_csrf_secret();
21852177
blob_init(&namelist, PD("namelist",""), -1);
21862178
blob_init(&sql, 0, 0);
21872179
while( blob_token(&namelist, &token) ){
21882180
const char *zOldName = blob_str(&token);
21892181
sqlite3_snprintf(sizeof(zCnt), zCnt, "n%d", cnt);
21902182
--- src/setup.c
+++ src/setup.c
@@ -202,12 +202,11 @@
202 if( zQ==0 && !disabled && P("submit") ){
203 zQ = "off";
204 }
205 if( zQ ){
206 int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ);
207 if( iQ!=iVal ){
208 login_verify_csrf_secret();
209 db_protect_only(PROTECT_NONE);
210 db_set(zVar/*works-like:"x"*/, iQ ? "1" : "0", 0);
211 db_protect_pop();
212 setup_incr_cfgcnt();
213 admin_log("Set option [%q] to [%q].",
@@ -237,13 +236,12 @@
237 const char *zDflt, /* Default value if CONFIG table entry does not exist */
238 int disabled /* 1 if disabled */
239 ){
240 const char *zVal = db_get(zVar, zDflt);
241 const char *zQ = P(zQParm);
242 if( zQ && fossil_strcmp(zQ,zVal)!=0 ){
243 const int nZQ = (int)strlen(zQ);
244 login_verify_csrf_secret();
245 setup_incr_cfgcnt();
246 db_protect_only(PROTECT_NONE);
247 db_set(zVar/*works-like:"x"*/, zQ, 0);
248 db_protect_pop();
249 admin_log("Set entry_attribute %Q to: %.*s%s",
@@ -270,13 +268,12 @@
270 const char *zDflt, /* Default value if CONFIG table entry does not exist */
271 int disabled /* 1 if the textarea should not be editable */
272 ){
273 const char *z = db_get(zVar, zDflt);
274 const char *zQ = P(zQP);
275 if( zQ && !disabled && fossil_strcmp(zQ,z)!=0){
276 const int nZQ = (int)strlen(zQ);
277 login_verify_csrf_secret();
278 db_protect_only(PROTECT_NONE);
279 db_set(zVar/*works-like:"x"*/, zQ, 0);
280 db_protect_pop();
281 setup_incr_cfgcnt();
282 admin_log("Set textarea_attribute %Q to: %.*s%s",
@@ -309,13 +306,12 @@
309 const char *const *azChoice /* Choices in pairs (VAR value, Display) */
310 ){
311 const char *z = db_get(zVar, zDflt);
312 const char *zQ = P(zQP);
313 int i;
314 if( zQ && fossil_strcmp(zQ,z)!=0){
315 const int nZQ = (int)strlen(zQ);
316 login_verify_csrf_secret();
317 db_unprotect(PROTECT_ALL);
318 db_set(zVar/*works-like:"x"*/, zQ, 0);
319 setup_incr_cfgcnt();
320 db_protect_pop();
321 admin_log("Set multiple_choice_attribute %Q to: %.*s%s",
@@ -1822,19 +1818,18 @@
1822 go = 1;
1823 }else if( P("tablelist") ){
1824 zQ = sqlite3_mprintf("SELECT*FROM pragma_table_list ORDER BY schema, name");
1825 go = 1;
1826 }
1827 if( go ){
1828 sqlite3_stmt *pStmt;
1829 int rc;
1830 const char *zTail;
1831 int nCol;
1832 int nRow = 0;
1833 int i;
1834 @ <hr>
1835 login_verify_csrf_secret();
1836 sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0);
1837 search_sql_setup(g.db);
1838 rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail);
1839 if( rc!=SQLITE_OK ){
1840 @ <div class="generalError">%h(sqlite3_errmsg(g.db))</div>
@@ -1913,22 +1908,20 @@
1913 style_header("Raw TH1 Commands");
1914 @ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
1915 @ run by this page. If Tcl integration was enabled at compile-time and
1916 @ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
1917 @
1918 @ <form method="post" action="%R/admin_th1">
1919 login_insert_csrf_secret();
1920 @ TH1:<br>
1921 @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br>
1922 @ <input type="submit" name="go" value="Run TH1">
1923 @ </form>
1924 if( go ){
1925 const char *zR;
1926 int rc;
1927 int n;
1928 @ <hr>
1929 login_verify_csrf_secret();
1930 rc = Th_Eval(g.interp, 0, zQ, -1);
1931 zR = Th_GetResult(g.interp, &n);
1932 if( rc==TH_OK ){
1933 @ <pre class="th1result">%h(zR)</pre>
1934 }else{
@@ -2173,17 +2166,16 @@
2173 login_needed(0);
2174 return;
2175 }
2176 style_set_current_feature("setup");
2177 style_header("URL Alias Configuration");
2178 if( P("submit")!=0 ){
2179 Blob token;
2180 Blob sql;
2181 const char *zNewName;
2182 const char *zValue;
2183 char zCnt[10];
2184 login_verify_csrf_secret();
2185 blob_init(&namelist, PD("namelist",""), -1);
2186 blob_init(&sql, 0, 0);
2187 while( blob_token(&namelist, &token) ){
2188 const char *zOldName = blob_str(&token);
2189 sqlite3_snprintf(sizeof(zCnt), zCnt, "n%d", cnt);
2190
--- src/setup.c
+++ src/setup.c
@@ -202,12 +202,11 @@
202 if( zQ==0 && !disabled && P("submit") ){
203 zQ = "off";
204 }
205 if( zQ ){
206 int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ);
207 if( iQ!=iVal && cgi_csrf_safe(2) ){
 
208 db_protect_only(PROTECT_NONE);
209 db_set(zVar/*works-like:"x"*/, iQ ? "1" : "0", 0);
210 db_protect_pop();
211 setup_incr_cfgcnt();
212 admin_log("Set option [%q] to [%q].",
@@ -237,13 +236,12 @@
236 const char *zDflt, /* Default value if CONFIG table entry does not exist */
237 int disabled /* 1 if disabled */
238 ){
239 const char *zVal = db_get(zVar, zDflt);
240 const char *zQ = P(zQParm);
241 if( zQ && fossil_strcmp(zQ,zVal)!=0 && cgi_csrf_safe(2) ){
242 const int nZQ = (int)strlen(zQ);
 
243 setup_incr_cfgcnt();
244 db_protect_only(PROTECT_NONE);
245 db_set(zVar/*works-like:"x"*/, zQ, 0);
246 db_protect_pop();
247 admin_log("Set entry_attribute %Q to: %.*s%s",
@@ -270,13 +268,12 @@
268 const char *zDflt, /* Default value if CONFIG table entry does not exist */
269 int disabled /* 1 if the textarea should not be editable */
270 ){
271 const char *z = db_get(zVar, zDflt);
272 const char *zQ = P(zQP);
273 if( zQ && !disabled && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){
274 const int nZQ = (int)strlen(zQ);
 
275 db_protect_only(PROTECT_NONE);
276 db_set(zVar/*works-like:"x"*/, zQ, 0);
277 db_protect_pop();
278 setup_incr_cfgcnt();
279 admin_log("Set textarea_attribute %Q to: %.*s%s",
@@ -309,13 +306,12 @@
306 const char *const *azChoice /* Choices in pairs (VAR value, Display) */
307 ){
308 const char *z = db_get(zVar, zDflt);
309 const char *zQ = P(zQP);
310 int i;
311 if( zQ && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){
312 const int nZQ = (int)strlen(zQ);
 
313 db_unprotect(PROTECT_ALL);
314 db_set(zVar/*works-like:"x"*/, zQ, 0);
315 setup_incr_cfgcnt();
316 db_protect_pop();
317 admin_log("Set multiple_choice_attribute %Q to: %.*s%s",
@@ -1822,19 +1818,18 @@
1818 go = 1;
1819 }else if( P("tablelist") ){
1820 zQ = sqlite3_mprintf("SELECT*FROM pragma_table_list ORDER BY schema, name");
1821 go = 1;
1822 }
1823 if( go && cgi_csrf_safe(2) ){
1824 sqlite3_stmt *pStmt;
1825 int rc;
1826 const char *zTail;
1827 int nCol;
1828 int nRow = 0;
1829 int i;
1830 @ <hr>
 
1831 sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0);
1832 search_sql_setup(g.db);
1833 rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail);
1834 if( rc!=SQLITE_OK ){
1835 @ <div class="generalError">%h(sqlite3_errmsg(g.db))</div>
@@ -1913,22 +1908,20 @@
1908 style_header("Raw TH1 Commands");
1909 @ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
1910 @ run by this page. If Tcl integration was enabled at compile-time and
1911 @ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
1912 @
1913 form_begin(0, "%R/admin_th1");
 
1914 @ TH1:<br>
1915 @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br>
1916 @ <input type="submit" name="go" value="Run TH1">
1917 @ </form>
1918 if( go && cgi_csrf_safe(2) ){
1919 const char *zR;
1920 int rc;
1921 int n;
1922 @ <hr>
 
1923 rc = Th_Eval(g.interp, 0, zQ, -1);
1924 zR = Th_GetResult(g.interp, &n);
1925 if( rc==TH_OK ){
1926 @ <pre class="th1result">%h(zR)</pre>
1927 }else{
@@ -2173,17 +2166,16 @@
2166 login_needed(0);
2167 return;
2168 }
2169 style_set_current_feature("setup");
2170 style_header("URL Alias Configuration");
2171 if( P("submit")!=0 && cgi_csrf_safe(2) ){
2172 Blob token;
2173 Blob sql;
2174 const char *zNewName;
2175 const char *zValue;
2176 char zCnt[10];
 
2177 blob_init(&namelist, PD("namelist",""), -1);
2178 blob_init(&sql, 0, 0);
2179 while( blob_token(&namelist, &token) ){
2180 const char *zOldName = blob_str(&token);
2181 sqlite3_snprintf(sizeof(zCnt), zCnt, "n%d", cnt);
2182
+2 -2
--- src/setupuser.c
+++ src/setupuser.c
@@ -386,11 +386,11 @@
386386
** more are missing, no-op */
387387
}else if( higherUser ){
388388
/* An Admin (a) user cannot edit a Superuser (s) */
389389
}else if( zDeleteVerify!=0 ){
390390
/* Need to verify a delete request */
391
- }else if( !cgi_csrf_safe(1) ){
391
+ }else if( !cgi_csrf_safe(2) ){
392392
/* This might be a cross-site request forgery, so ignore it */
393393
}else{
394394
/* We have all the information we need to make the change to the user */
395395
char c;
396396
char zCap[70], zNm[4];
@@ -440,11 +440,11 @@
440440
@ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
441441
@ [Bummer]</a></p>
442442
style_finish_page();
443443
return;
444444
}
445
- login_verify_csrf_secret();
445
+ cgi_csrf_verify();
446446
db_unprotect(PROTECT_USER);
447447
db_multi_exec(
448448
"REPLACE INTO user(uid,login,info,pw,cap,mtime) "
449449
"VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())",
450450
uid, zLogin, P("info"), zPw, zCap
451451
--- src/setupuser.c
+++ src/setupuser.c
@@ -386,11 +386,11 @@
386 ** more are missing, no-op */
387 }else if( higherUser ){
388 /* An Admin (a) user cannot edit a Superuser (s) */
389 }else if( zDeleteVerify!=0 ){
390 /* Need to verify a delete request */
391 }else if( !cgi_csrf_safe(1) ){
392 /* This might be a cross-site request forgery, so ignore it */
393 }else{
394 /* We have all the information we need to make the change to the user */
395 char c;
396 char zCap[70], zNm[4];
@@ -440,11 +440,11 @@
440 @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
441 @ [Bummer]</a></p>
442 style_finish_page();
443 return;
444 }
445 login_verify_csrf_secret();
446 db_unprotect(PROTECT_USER);
447 db_multi_exec(
448 "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
449 "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())",
450 uid, zLogin, P("info"), zPw, zCap
451
--- src/setupuser.c
+++ src/setupuser.c
@@ -386,11 +386,11 @@
386 ** more are missing, no-op */
387 }else if( higherUser ){
388 /* An Admin (a) user cannot edit a Superuser (s) */
389 }else if( zDeleteVerify!=0 ){
390 /* Need to verify a delete request */
391 }else if( !cgi_csrf_safe(2) ){
392 /* This might be a cross-site request forgery, so ignore it */
393 }else{
394 /* We have all the information we need to make the change to the user */
395 char c;
396 char zCap[70], zNm[4];
@@ -440,11 +440,11 @@
440 @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
441 @ [Bummer]</a></p>
442 style_finish_page();
443 return;
444 }
445 cgi_csrf_verify();
446 db_unprotect(PROTECT_USER);
447 db_multi_exec(
448 "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
449 "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())",
450 uid, zLogin, P("info"), zPw, zCap
451
+2 -4
--- src/shun.c
+++ src/shun.c
@@ -98,14 +98,13 @@
9898
}
9999
}
100100
zUuid = zCanonical;
101101
}
102102
style_header("Shunned Artifacts");
103
- if( zUuid && P("sub") ){
103
+ if( zUuid && P("sub") && cgi_csrf_safe(2) ){
104104
const char *p = zUuid;
105105
int allExist = 1;
106
- login_verify_csrf_secret();
107106
while( *p ){
108107
db_multi_exec("DELETE FROM shun WHERE uuid=%Q", p);
109108
if( !db_exists("SELECT 1 FROM blob WHERE uuid=%Q", p) ){
110109
allExist = 0;
111110
}
@@ -127,14 +126,13 @@
127126
@ It may be necessary to rebuild the repository using the
128127
@ <b>fossil rebuild</b> command-line before the artifact content
129128
@ can pulled in from other repositories.</p>
130129
}
131130
}
132
- if( zUuid && P("add") ){
131
+ if( zUuid && P("add") && cgi_csrf_safe(2) ){
133132
const char *p = zUuid;
134133
int rid, tagid;
135
- login_verify_csrf_secret();
136134
while( *p ){
137135
db_multi_exec(
138136
"INSERT OR IGNORE INTO shun(uuid,mtime)"
139137
" VALUES(%Q, now())", p);
140138
db_multi_exec("DELETE FROM attachment WHERE src=%Q", p);
141139
--- src/shun.c
+++ src/shun.c
@@ -98,14 +98,13 @@
98 }
99 }
100 zUuid = zCanonical;
101 }
102 style_header("Shunned Artifacts");
103 if( zUuid && P("sub") ){
104 const char *p = zUuid;
105 int allExist = 1;
106 login_verify_csrf_secret();
107 while( *p ){
108 db_multi_exec("DELETE FROM shun WHERE uuid=%Q", p);
109 if( !db_exists("SELECT 1 FROM blob WHERE uuid=%Q", p) ){
110 allExist = 0;
111 }
@@ -127,14 +126,13 @@
127 @ It may be necessary to rebuild the repository using the
128 @ <b>fossil rebuild</b> command-line before the artifact content
129 @ can pulled in from other repositories.</p>
130 }
131 }
132 if( zUuid && P("add") ){
133 const char *p = zUuid;
134 int rid, tagid;
135 login_verify_csrf_secret();
136 while( *p ){
137 db_multi_exec(
138 "INSERT OR IGNORE INTO shun(uuid,mtime)"
139 " VALUES(%Q, now())", p);
140 db_multi_exec("DELETE FROM attachment WHERE src=%Q", p);
141
--- src/shun.c
+++ src/shun.c
@@ -98,14 +98,13 @@
98 }
99 }
100 zUuid = zCanonical;
101 }
102 style_header("Shunned Artifacts");
103 if( zUuid && P("sub") && cgi_csrf_safe(2) ){
104 const char *p = zUuid;
105 int allExist = 1;
 
106 while( *p ){
107 db_multi_exec("DELETE FROM shun WHERE uuid=%Q", p);
108 if( !db_exists("SELECT 1 FROM blob WHERE uuid=%Q", p) ){
109 allExist = 0;
110 }
@@ -127,14 +126,13 @@
126 @ It may be necessary to rebuild the repository using the
127 @ <b>fossil rebuild</b> command-line before the artifact content
128 @ can pulled in from other repositories.</p>
129 }
130 }
131 if( zUuid && P("add") && cgi_csrf_safe(2) ){
132 const char *p = zUuid;
133 int rid, tagid;
 
134 while( *p ){
135 db_multi_exec(
136 "INSERT OR IGNORE INTO shun(uuid,mtime)"
137 " VALUES(%Q, now())", p);
138 db_multi_exec("DELETE FROM attachment WHERE src=%Q", p);
139
--- src/style.c
+++ src/style.c
@@ -259,10 +259,11 @@
259259
}else{
260260
needHrefJs = 1;
261261
@ <form method="POST" data-action='%s(zLink)' action='%R/login' \
262262
@ %s(zOtherArgs)>
263263
}
264
+ login_insert_csrf_secret();
264265
}
265266
266267
/*
267268
** Add a new element to the submenu
268269
*/
269270
--- src/style.c
+++ src/style.c
@@ -259,10 +259,11 @@
259 }else{
260 needHrefJs = 1;
261 @ <form method="POST" data-action='%s(zLink)' action='%R/login' \
262 @ %s(zOtherArgs)>
263 }
 
264 }
265
266 /*
267 ** Add a new element to the submenu
268 */
269
--- src/style.c
+++ src/style.c
@@ -259,10 +259,11 @@
259 }else{
260 needHrefJs = 1;
261 @ <form method="POST" data-action='%s(zLink)' action='%R/login' \
262 @ %s(zOtherArgs)>
263 }
264 login_insert_csrf_secret();
265 }
266
267 /*
268 ** Add a new element to the submenu
269 */
270
+3 -1
--- src/th_main.c
+++ src/th_main.c
@@ -605,11 +605,13 @@
605605
int *argl
606606
){
607607
if( argc!=1 ){
608608
return Th_WrongNumArgs(interp, "verifyCsrf");
609609
}
610
- login_verify_csrf_secret();
610
+ if( !cgi_csrf_safe(2) ){
611
+ fossil_fatal("possible CSRF attack");
612
+ }
611613
return TH_OK;
612614
}
613615
614616
/*
615617
** TH1 command: verifyLogin
616618
--- src/th_main.c
+++ src/th_main.c
@@ -605,11 +605,13 @@
605 int *argl
606 ){
607 if( argc!=1 ){
608 return Th_WrongNumArgs(interp, "verifyCsrf");
609 }
610 login_verify_csrf_secret();
 
 
611 return TH_OK;
612 }
613
614 /*
615 ** TH1 command: verifyLogin
616
--- src/th_main.c
+++ src/th_main.c
@@ -605,11 +605,13 @@
605 int *argl
606 ){
607 if( argc!=1 ){
608 return Th_WrongNumArgs(interp, "verifyCsrf");
609 }
610 if( !cgi_csrf_safe(2) ){
611 fossil_fatal("possible CSRF attack");
612 }
613 return TH_OK;
614 }
615
616 /*
617 ** TH1 command: verifyLogin
618
+4 -3
--- src/tkt.c
+++ src/tkt.c
@@ -900,11 +900,14 @@
900900
int i;
901901
int nJ = 0, rc = TH_OK;
902902
Blob tktchng, cksum;
903903
int needMod;
904904
905
- login_verify_csrf_secret();
905
+ if( !cgi_csrf_safe(2) ){
906
+ @ <p class="generalError">Error: Invalid CSRF token.</p>
907
+ return TH_OK;
908
+ }
906909
if( !captcha_is_correct(0) ){
907910
@ <p class="generalError">Error: Incorrect security code.</p>
908911
return TH_OK;
909912
}
910913
zUuid = (const char *)pUuid;
@@ -1015,11 +1018,10 @@
10151018
initializeVariablesFromCGI();
10161019
getAllTicketFields();
10171020
initializeVariablesFromDb();
10181021
if( g.zPath[0]=='d' ) showAllFields();
10191022
form_begin(0, "%R/%s", g.zPath);
1020
- login_insert_csrf_secret();
10211023
if( P("date_override") && g.perm.Setup ){
10221024
@ <input type="hidden" name="date_override" value="%h(P("date_override"))">
10231025
}
10241026
zScript = ticket_newpage_code();
10251027
if( g.zLogin && g.zLogin[0] ){
@@ -1105,11 +1107,10 @@
11051107
initializeVariablesFromCGI();
11061108
initializeVariablesFromDb();
11071109
if( g.zPath[0]=='d' ) showAllFields();
11081110
form_begin(0, "%R/%s", g.zPath);
11091111
@ <input type="hidden" name="name" value="%s(zName)">
1110
- login_insert_csrf_secret();
11111112
zScript = ticket_editpage_code();
11121113
Th_Store("login", login_name());
11131114
Th_Store("date", db_text(0, "SELECT datetime('now')"));
11141115
Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
11151116
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
11161117
--- src/tkt.c
+++ src/tkt.c
@@ -900,11 +900,14 @@
900 int i;
901 int nJ = 0, rc = TH_OK;
902 Blob tktchng, cksum;
903 int needMod;
904
905 login_verify_csrf_secret();
 
 
 
906 if( !captcha_is_correct(0) ){
907 @ <p class="generalError">Error: Incorrect security code.</p>
908 return TH_OK;
909 }
910 zUuid = (const char *)pUuid;
@@ -1015,11 +1018,10 @@
1015 initializeVariablesFromCGI();
1016 getAllTicketFields();
1017 initializeVariablesFromDb();
1018 if( g.zPath[0]=='d' ) showAllFields();
1019 form_begin(0, "%R/%s", g.zPath);
1020 login_insert_csrf_secret();
1021 if( P("date_override") && g.perm.Setup ){
1022 @ <input type="hidden" name="date_override" value="%h(P("date_override"))">
1023 }
1024 zScript = ticket_newpage_code();
1025 if( g.zLogin && g.zLogin[0] ){
@@ -1105,11 +1107,10 @@
1105 initializeVariablesFromCGI();
1106 initializeVariablesFromDb();
1107 if( g.zPath[0]=='d' ) showAllFields();
1108 form_begin(0, "%R/%s", g.zPath);
1109 @ <input type="hidden" name="name" value="%s(zName)">
1110 login_insert_csrf_secret();
1111 zScript = ticket_editpage_code();
1112 Th_Store("login", login_name());
1113 Th_Store("date", db_text(0, "SELECT datetime('now')"));
1114 Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
1115 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
1116
--- src/tkt.c
+++ src/tkt.c
@@ -900,11 +900,14 @@
900 int i;
901 int nJ = 0, rc = TH_OK;
902 Blob tktchng, cksum;
903 int needMod;
904
905 if( !cgi_csrf_safe(2) ){
906 @ <p class="generalError">Error: Invalid CSRF token.</p>
907 return TH_OK;
908 }
909 if( !captcha_is_correct(0) ){
910 @ <p class="generalError">Error: Incorrect security code.</p>
911 return TH_OK;
912 }
913 zUuid = (const char *)pUuid;
@@ -1015,11 +1018,10 @@
1018 initializeVariablesFromCGI();
1019 getAllTicketFields();
1020 initializeVariablesFromDb();
1021 if( g.zPath[0]=='d' ) showAllFields();
1022 form_begin(0, "%R/%s", g.zPath);
 
1023 if( P("date_override") && g.perm.Setup ){
1024 @ <input type="hidden" name="date_override" value="%h(P("date_override"))">
1025 }
1026 zScript = ticket_newpage_code();
1027 if( g.zLogin && g.zLogin[0] ){
@@ -1105,11 +1107,10 @@
1107 initializeVariablesFromCGI();
1108 initializeVariablesFromDb();
1109 if( g.zPath[0]=='d' ) showAllFields();
1110 form_begin(0, "%R/%s", g.zPath);
1111 @ <input type="hidden" name="name" value="%s(zName)">
 
1112 zScript = ticket_editpage_code();
1113 Th_Store("login", login_name());
1114 Th_Store("date", db_text(0, "SELECT datetime('now')"));
1115 Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
1116 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
1117
+2 -4
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -135,18 +135,16 @@
135135
if( z==0 ){
136136
z = db_get(zDbField, zDfltValue);
137137
}
138138
style_set_current_feature("tktsetup");
139139
style_header("Edit %s", zTitle);
140
- if( P("clear")!=0 ){
141
- login_verify_csrf_secret();
140
+ if( P("clear")!=0 && cgi_csrf_safe(2) ){
142141
db_unset(zDbField/*works-like:"x"*/, 0);
143142
if( xRebuild ) xRebuild();
144143
cgi_redirect("tktsetup");
145
- }else if( isSubmit ){
144
+ }else if( isSubmit && cgi_csrf_safe(2) ){
146145
char *zErr = 0;
147
- login_verify_csrf_secret();
148146
if( xText && (zErr = xText(z))!=0 ){
149147
@ <p class="tktsetupError">ERROR: %h(zErr)</p>
150148
}else{
151149
db_set(zDbField/*works-like:"x"*/, z, 0);
152150
if( xRebuild ) xRebuild();
153151
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -135,18 +135,16 @@
135 if( z==0 ){
136 z = db_get(zDbField, zDfltValue);
137 }
138 style_set_current_feature("tktsetup");
139 style_header("Edit %s", zTitle);
140 if( P("clear")!=0 ){
141 login_verify_csrf_secret();
142 db_unset(zDbField/*works-like:"x"*/, 0);
143 if( xRebuild ) xRebuild();
144 cgi_redirect("tktsetup");
145 }else if( isSubmit ){
146 char *zErr = 0;
147 login_verify_csrf_secret();
148 if( xText && (zErr = xText(z))!=0 ){
149 @ <p class="tktsetupError">ERROR: %h(zErr)</p>
150 }else{
151 db_set(zDbField/*works-like:"x"*/, z, 0);
152 if( xRebuild ) xRebuild();
153
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -135,18 +135,16 @@
135 if( z==0 ){
136 z = db_get(zDbField, zDfltValue);
137 }
138 style_set_current_feature("tktsetup");
139 style_header("Edit %s", zTitle);
140 if( P("clear")!=0 && cgi_csrf_safe(2) ){
 
141 db_unset(zDbField/*works-like:"x"*/, 0);
142 if( xRebuild ) xRebuild();
143 cgi_redirect("tktsetup");
144 }else if( isSubmit && cgi_csrf_safe(2) ){
145 char *zErr = 0;
 
146 if( xText && (zErr = xText(z))!=0 ){
147 @ <p class="tktsetupError">ERROR: %h(zErr)</p>
148 }else{
149 db_set(zDbField/*works-like:"x"*/, z, 0);
150 if( xRebuild ) xRebuild();
151
+1 -2
--- src/wiki.c
+++ src/wiki.c
@@ -1632,18 +1632,18 @@
16321632
zMimetype = wiki_filter_mimetypes(pWiki->zMimetype)
16331633
/* see https://fossil-scm.org/forum/forumpost/0acfdaac80 */;
16341634
}
16351635
if( !isSandbox && P("submit")!=0 && P("r")!=0 && P("u")!=0
16361636
&& (goodCaptcha = captcha_is_correct(0))
1637
+ && cgi_csrf_safe(2)
16371638
){
16381639
char *zDate;
16391640
Blob cksum;
16401641
Blob body;
16411642
Blob wiki;
16421643
16431644
blob_zero(&body);
1644
- login_verify_csrf_secret();
16451645
blob_append(&body, pWiki->zWiki, -1);
16461646
blob_zero(&wiki);
16471647
db_begin_transaction();
16481648
zDate = date_in_standard_format("now");
16491649
blob_appendf(&wiki, "D %s\n", zDate);
@@ -1695,11 +1695,10 @@
16951695
@ <hr>
16961696
blob_reset(&preview);
16971697
}
16981698
zUser = PD("u", g.zLogin);
16991699
form_begin(0, "%R/wikiappend");
1700
- login_insert_csrf_secret();
17011700
@ <input type="hidden" name="name" value="%h(zPageName)">
17021701
@ <input type="hidden" name="mimetype" value="%h(zMimetype)">
17031702
@ Your Name:
17041703
@ <input type="text" name="u" size="20" value="%h(zUser)"><br>
17051704
zFormat = mimetype_common_name(zMimetype);
17061705
--- src/wiki.c
+++ src/wiki.c
@@ -1632,18 +1632,18 @@
1632 zMimetype = wiki_filter_mimetypes(pWiki->zMimetype)
1633 /* see https://fossil-scm.org/forum/forumpost/0acfdaac80 */;
1634 }
1635 if( !isSandbox && P("submit")!=0 && P("r")!=0 && P("u")!=0
1636 && (goodCaptcha = captcha_is_correct(0))
 
1637 ){
1638 char *zDate;
1639 Blob cksum;
1640 Blob body;
1641 Blob wiki;
1642
1643 blob_zero(&body);
1644 login_verify_csrf_secret();
1645 blob_append(&body, pWiki->zWiki, -1);
1646 blob_zero(&wiki);
1647 db_begin_transaction();
1648 zDate = date_in_standard_format("now");
1649 blob_appendf(&wiki, "D %s\n", zDate);
@@ -1695,11 +1695,10 @@
1695 @ <hr>
1696 blob_reset(&preview);
1697 }
1698 zUser = PD("u", g.zLogin);
1699 form_begin(0, "%R/wikiappend");
1700 login_insert_csrf_secret();
1701 @ <input type="hidden" name="name" value="%h(zPageName)">
1702 @ <input type="hidden" name="mimetype" value="%h(zMimetype)">
1703 @ Your Name:
1704 @ <input type="text" name="u" size="20" value="%h(zUser)"><br>
1705 zFormat = mimetype_common_name(zMimetype);
1706
--- src/wiki.c
+++ src/wiki.c
@@ -1632,18 +1632,18 @@
1632 zMimetype = wiki_filter_mimetypes(pWiki->zMimetype)
1633 /* see https://fossil-scm.org/forum/forumpost/0acfdaac80 */;
1634 }
1635 if( !isSandbox && P("submit")!=0 && P("r")!=0 && P("u")!=0
1636 && (goodCaptcha = captcha_is_correct(0))
1637 && cgi_csrf_safe(2)
1638 ){
1639 char *zDate;
1640 Blob cksum;
1641 Blob body;
1642 Blob wiki;
1643
1644 blob_zero(&body);
 
1645 blob_append(&body, pWiki->zWiki, -1);
1646 blob_zero(&wiki);
1647 db_begin_transaction();
1648 zDate = date_in_standard_format("now");
1649 blob_appendf(&wiki, "D %s\n", zDate);
@@ -1695,11 +1695,10 @@
1695 @ <hr>
1696 blob_reset(&preview);
1697 }
1698 zUser = PD("u", g.zLogin);
1699 form_begin(0, "%R/wikiappend");
 
1700 @ <input type="hidden" name="name" value="%h(zPageName)">
1701 @ <input type="hidden" name="mimetype" value="%h(zMimetype)">
1702 @ Your Name:
1703 @ <input type="text" name="u" size="20" value="%h(zUser)"><br>
1704 zFormat = mimetype_common_name(zMimetype);
1705
+2 -4
--- src/xfersetup.c
+++ src/xfersetup.c
@@ -118,18 +118,16 @@
118118
if( z==0 ){
119119
z = db_get(zDbField, zDfltValue);
120120
}
121121
style_set_current_feature("xfersetup");
122122
style_header("Edit %s", zTitle);
123
- if( P("clear")!=0 ){
124
- login_verify_csrf_secret();
123
+ if( P("clear")!=0 && cgi_csrf_safe(2) ){
125124
db_unset(zDbField/*works-like:"x"*/, 0);
126125
if( xRebuild ) xRebuild();
127126
z = zDfltValue;
128
- }else if( isSubmit ){
127
+ }else if( isSubmit && cgi_csrf_safe(2) ){
129128
char *zErr = 0;
130
- login_verify_csrf_secret();
131129
if( xText && (zErr = xText(z))!=0 ){
132130
@ <p class="xfersetupError">ERROR: %h(zErr)</p>
133131
}else{
134132
db_set(zDbField/*works-like:"x"*/, z, 0);
135133
if( xRebuild ) xRebuild();
136134
--- src/xfersetup.c
+++ src/xfersetup.c
@@ -118,18 +118,16 @@
118 if( z==0 ){
119 z = db_get(zDbField, zDfltValue);
120 }
121 style_set_current_feature("xfersetup");
122 style_header("Edit %s", zTitle);
123 if( P("clear")!=0 ){
124 login_verify_csrf_secret();
125 db_unset(zDbField/*works-like:"x"*/, 0);
126 if( xRebuild ) xRebuild();
127 z = zDfltValue;
128 }else if( isSubmit ){
129 char *zErr = 0;
130 login_verify_csrf_secret();
131 if( xText && (zErr = xText(z))!=0 ){
132 @ <p class="xfersetupError">ERROR: %h(zErr)</p>
133 }else{
134 db_set(zDbField/*works-like:"x"*/, z, 0);
135 if( xRebuild ) xRebuild();
136
--- src/xfersetup.c
+++ src/xfersetup.c
@@ -118,18 +118,16 @@
118 if( z==0 ){
119 z = db_get(zDbField, zDfltValue);
120 }
121 style_set_current_feature("xfersetup");
122 style_header("Edit %s", zTitle);
123 if( P("clear")!=0 && cgi_csrf_safe(2) ){
 
124 db_unset(zDbField/*works-like:"x"*/, 0);
125 if( xRebuild ) xRebuild();
126 z = zDfltValue;
127 }else if( isSubmit && cgi_csrf_safe(2) ){
128 char *zErr = 0;
 
129 if( xText && (zErr = xText(z))!=0 ){
130 @ <p class="xfersetupError">ERROR: %h(zErr)</p>
131 }else{
132 db_set(zDbField/*works-like:"x"*/, z, 0);
133 if( xRebuild ) xRebuild();
134

Keyboard Shortcuts

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