Fossil SCM
Merge the CSRF-defense enhancements into trunk.
Commit
920ace17395fc150aaceedd7cbb416c693d06a4f134c54dacc5f762dc0a14b19
Parent
8ff63db2e66013a…
22 files changed
+7
-6
-8
+48
-26
+1
-2
+9
-5
+1
-3
+1
-1
+26
-22
+6
-2
+3
-5
+2
-1
+12
-20
+3
-3
+13
+2
-4
+10
-3
+21
-1
+3
-1
+4
-3
+2
-4
+1
-2
+2
-4
~
src/alerts.c
~
src/builtin.c
~
src/cgi.c
~
src/event.c
~
src/forum.c
~
src/info.c
~
src/interwiki.c
~
src/login.c
~
src/main.c
~
src/report.c
~
src/security_audit.c
~
src/setup.c
~
src/setupuser.c
~
src/sha1.c
~
src/shun.c
~
src/skins.c
~
src/style.c
~
src/th_main.c
~
src/tkt.c
~
src/tktsetup.c
~
src/wiki.c
~
src/xfersetup.c
+7
-6
| --- src/alerts.c | ||
| +++ src/alerts.c | ||
| @@ -1545,11 +1545,11 @@ | ||
| 1545 | 1545 | } |
| 1546 | 1546 | style_set_current_feature("alerts"); |
| 1547 | 1547 | alert_submenu_common(); |
| 1548 | 1548 | needCaptcha = !login_is_individual(); |
| 1549 | 1549 | if( P("submit") |
| 1550 | - && cgi_csrf_safe(1) | |
| 1550 | + && cgi_csrf_safe(2) | |
| 1551 | 1551 | && subscribe_error_check(&eErr,&zErr,needCaptcha) |
| 1552 | 1552 | ){ |
| 1553 | 1553 | /* A validated request for a new subscription has been received. */ |
| 1554 | 1554 | char ssub[20]; |
| 1555 | 1555 | const char *zEAddr = P("e"); |
| @@ -1856,11 +1856,11 @@ | ||
| 1856 | 1856 | db_commit_transaction(); |
| 1857 | 1857 | cgi_redirect("subscribe"); |
| 1858 | 1858 | /*NOTREACHED*/ |
| 1859 | 1859 | } |
| 1860 | 1860 | alert_submenu_common(); |
| 1861 | - if( P("submit")!=0 && cgi_csrf_safe(1) ){ | |
| 1861 | + if( P("submit")!=0 && cgi_csrf_safe(2) ){ | |
| 1862 | 1862 | char newSsub[10]; |
| 1863 | 1863 | int nsub = 0; |
| 1864 | 1864 | Blob update; |
| 1865 | 1865 | |
| 1866 | 1866 | sdonotcall = PB("sdonotcall"); |
| @@ -1918,11 +1918,11 @@ | ||
| 1918 | 1918 | "UPDATE subscriber SET lastContact=now()/86400" |
| 1919 | 1919 | " WHERE subscriberId=%d", sid |
| 1920 | 1920 | ); |
| 1921 | 1921 | db_protect_pop(); |
| 1922 | 1922 | } |
| 1923 | - if( P("delete")!=0 && cgi_csrf_safe(1) ){ | |
| 1923 | + if( P("delete")!=0 && cgi_csrf_safe(2) ){ | |
| 1924 | 1924 | if( !PB("dodelete") ){ |
| 1925 | 1925 | eErr = 9; |
| 1926 | 1926 | zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to" |
| 1927 | 1927 | " unsubscribe"); |
| 1928 | 1928 | }else{ |
| @@ -2269,11 +2269,11 @@ | ||
| 2269 | 2269 | |
| 2270 | 2270 | style_set_current_feature("alerts"); |
| 2271 | 2271 | |
| 2272 | 2272 | zEAddr = PD("e",""); |
| 2273 | 2273 | dx = atoi(PD("dx","0")); |
| 2274 | - bSubmit = P("submit")!=0 && P("e")!=0 && cgi_csrf_safe(1); | |
| 2274 | + bSubmit = P("submit")!=0 && P("e")!=0 && cgi_csrf_safe(2); | |
| 2275 | 2275 | if( bSubmit ){ |
| 2276 | 2276 | if( !captcha_is_correct(1) ){ |
| 2277 | 2277 | eErr = 2; |
| 2278 | 2278 | zErr = mprintf("enter the security code shown below"); |
| 2279 | 2279 | bSubmit = 0; |
| @@ -3300,11 +3300,11 @@ | ||
| 3300 | 3300 | } |
| 3301 | 3301 | if( P("submit")!=0 |
| 3302 | 3302 | && P("subject")!=0 |
| 3303 | 3303 | && P("msg")!=0 |
| 3304 | 3304 | && P("from")!=0 |
| 3305 | - && cgi_csrf_safe(1) | |
| 3305 | + && cgi_csrf_safe(2) | |
| 3306 | 3306 | && captcha_is_correct(0) |
| 3307 | 3307 | ){ |
| 3308 | 3308 | Blob hdr, body; |
| 3309 | 3309 | AlertSender *pSender = alert_sender_new(0,0); |
| 3310 | 3310 | blob_init(&hdr, 0, 0); |
| @@ -3477,11 +3477,11 @@ | ||
| 3477 | 3477 | /* Visit the /announce/test1 page to see the CGI variables */ |
| 3478 | 3478 | zAction = "announce/test1"; |
| 3479 | 3479 | @ <p style='border: 1px solid black; padding: 1ex;'> |
| 3480 | 3480 | cgi_print_all(0, 0, 0); |
| 3481 | 3481 | @ </p> |
| 3482 | - }else if( P("submit")!=0 && cgi_csrf_safe(1) ){ | |
| 3482 | + }else if( P("submit")!=0 && cgi_csrf_safe(2) ){ | |
| 3483 | 3483 | char *zErr = alert_send_announcement(); |
| 3484 | 3484 | style_header("Announcement Sent"); |
| 3485 | 3485 | if( zErr ){ |
| 3486 | 3486 | @ <h1>Internal Error</h1> |
| 3487 | 3487 | @ <p>The following error was reported by the system: |
| @@ -3502,10 +3502,11 @@ | ||
| 3502 | 3502 | return; |
| 3503 | 3503 | } |
| 3504 | 3504 | |
| 3505 | 3505 | style_header("Send Announcement"); |
| 3506 | 3506 | @ <form method="POST" action="%R/%s(zAction)"> |
| 3507 | + login_insert_csrf_secret(); | |
| 3507 | 3508 | @ <table class="subscribe"> |
| 3508 | 3509 | if( g.perm.Admin ){ |
| 3509 | 3510 | int aa = PB("aa"); |
| 3510 | 3511 | int all = PB("all"); |
| 3511 | 3512 | int aMod = PB("mods"); |
| 3512 | 3513 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -1545,11 +1545,11 @@ | |
| 1545 | } |
| 1546 | style_set_current_feature("alerts"); |
| 1547 | alert_submenu_common(); |
| 1548 | needCaptcha = !login_is_individual(); |
| 1549 | if( P("submit") |
| 1550 | && cgi_csrf_safe(1) |
| 1551 | && subscribe_error_check(&eErr,&zErr,needCaptcha) |
| 1552 | ){ |
| 1553 | /* A validated request for a new subscription has been received. */ |
| 1554 | char ssub[20]; |
| 1555 | const char *zEAddr = P("e"); |
| @@ -1856,11 +1856,11 @@ | |
| 1856 | db_commit_transaction(); |
| 1857 | cgi_redirect("subscribe"); |
| 1858 | /*NOTREACHED*/ |
| 1859 | } |
| 1860 | alert_submenu_common(); |
| 1861 | if( P("submit")!=0 && cgi_csrf_safe(1) ){ |
| 1862 | char newSsub[10]; |
| 1863 | int nsub = 0; |
| 1864 | Blob update; |
| 1865 | |
| 1866 | sdonotcall = PB("sdonotcall"); |
| @@ -1918,11 +1918,11 @@ | |
| 1918 | "UPDATE subscriber SET lastContact=now()/86400" |
| 1919 | " WHERE subscriberId=%d", sid |
| 1920 | ); |
| 1921 | db_protect_pop(); |
| 1922 | } |
| 1923 | if( P("delete")!=0 && cgi_csrf_safe(1) ){ |
| 1924 | if( !PB("dodelete") ){ |
| 1925 | eErr = 9; |
| 1926 | zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to" |
| 1927 | " unsubscribe"); |
| 1928 | }else{ |
| @@ -2269,11 +2269,11 @@ | |
| 2269 | |
| 2270 | style_set_current_feature("alerts"); |
| 2271 | |
| 2272 | zEAddr = PD("e",""); |
| 2273 | dx = atoi(PD("dx","0")); |
| 2274 | bSubmit = P("submit")!=0 && P("e")!=0 && cgi_csrf_safe(1); |
| 2275 | if( bSubmit ){ |
| 2276 | if( !captcha_is_correct(1) ){ |
| 2277 | eErr = 2; |
| 2278 | zErr = mprintf("enter the security code shown below"); |
| 2279 | bSubmit = 0; |
| @@ -3300,11 +3300,11 @@ | |
| 3300 | } |
| 3301 | if( P("submit")!=0 |
| 3302 | && P("subject")!=0 |
| 3303 | && P("msg")!=0 |
| 3304 | && P("from")!=0 |
| 3305 | && cgi_csrf_safe(1) |
| 3306 | && captcha_is_correct(0) |
| 3307 | ){ |
| 3308 | Blob hdr, body; |
| 3309 | AlertSender *pSender = alert_sender_new(0,0); |
| 3310 | blob_init(&hdr, 0, 0); |
| @@ -3477,11 +3477,11 @@ | |
| 3477 | /* Visit the /announce/test1 page to see the CGI variables */ |
| 3478 | zAction = "announce/test1"; |
| 3479 | @ <p style='border: 1px solid black; padding: 1ex;'> |
| 3480 | cgi_print_all(0, 0, 0); |
| 3481 | @ </p> |
| 3482 | }else if( P("submit")!=0 && cgi_csrf_safe(1) ){ |
| 3483 | char *zErr = alert_send_announcement(); |
| 3484 | style_header("Announcement Sent"); |
| 3485 | if( zErr ){ |
| 3486 | @ <h1>Internal Error</h1> |
| 3487 | @ <p>The following error was reported by the system: |
| @@ -3502,10 +3502,11 @@ | |
| 3502 | return; |
| 3503 | } |
| 3504 | |
| 3505 | style_header("Send Announcement"); |
| 3506 | @ <form method="POST" action="%R/%s(zAction)"> |
| 3507 | @ <table class="subscribe"> |
| 3508 | if( g.perm.Admin ){ |
| 3509 | int aa = PB("aa"); |
| 3510 | int all = PB("all"); |
| 3511 | int aMod = PB("mods"); |
| 3512 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -1545,11 +1545,11 @@ | |
| 1545 | } |
| 1546 | style_set_current_feature("alerts"); |
| 1547 | alert_submenu_common(); |
| 1548 | needCaptcha = !login_is_individual(); |
| 1549 | if( P("submit") |
| 1550 | && cgi_csrf_safe(2) |
| 1551 | && subscribe_error_check(&eErr,&zErr,needCaptcha) |
| 1552 | ){ |
| 1553 | /* A validated request for a new subscription has been received. */ |
| 1554 | char ssub[20]; |
| 1555 | const char *zEAddr = P("e"); |
| @@ -1856,11 +1856,11 @@ | |
| 1856 | db_commit_transaction(); |
| 1857 | cgi_redirect("subscribe"); |
| 1858 | /*NOTREACHED*/ |
| 1859 | } |
| 1860 | alert_submenu_common(); |
| 1861 | if( P("submit")!=0 && cgi_csrf_safe(2) ){ |
| 1862 | char newSsub[10]; |
| 1863 | int nsub = 0; |
| 1864 | Blob update; |
| 1865 | |
| 1866 | sdonotcall = PB("sdonotcall"); |
| @@ -1918,11 +1918,11 @@ | |
| 1918 | "UPDATE subscriber SET lastContact=now()/86400" |
| 1919 | " WHERE subscriberId=%d", sid |
| 1920 | ); |
| 1921 | db_protect_pop(); |
| 1922 | } |
| 1923 | if( P("delete")!=0 && cgi_csrf_safe(2) ){ |
| 1924 | if( !PB("dodelete") ){ |
| 1925 | eErr = 9; |
| 1926 | zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to" |
| 1927 | " unsubscribe"); |
| 1928 | }else{ |
| @@ -2269,11 +2269,11 @@ | |
| 2269 | |
| 2270 | style_set_current_feature("alerts"); |
| 2271 | |
| 2272 | zEAddr = PD("e",""); |
| 2273 | dx = atoi(PD("dx","0")); |
| 2274 | bSubmit = P("submit")!=0 && P("e")!=0 && cgi_csrf_safe(2); |
| 2275 | if( bSubmit ){ |
| 2276 | if( !captcha_is_correct(1) ){ |
| 2277 | eErr = 2; |
| 2278 | zErr = mprintf("enter the security code shown below"); |
| 2279 | bSubmit = 0; |
| @@ -3300,11 +3300,11 @@ | |
| 3300 | } |
| 3301 | if( P("submit")!=0 |
| 3302 | && P("subject")!=0 |
| 3303 | && P("msg")!=0 |
| 3304 | && P("from")!=0 |
| 3305 | && cgi_csrf_safe(2) |
| 3306 | && captcha_is_correct(0) |
| 3307 | ){ |
| 3308 | Blob hdr, body; |
| 3309 | AlertSender *pSender = alert_sender_new(0,0); |
| 3310 | blob_init(&hdr, 0, 0); |
| @@ -3477,11 +3477,11 @@ | |
| 3477 | /* Visit the /announce/test1 page to see the CGI variables */ |
| 3478 | zAction = "announce/test1"; |
| 3479 | @ <p style='border: 1px solid black; padding: 1ex;'> |
| 3480 | cgi_print_all(0, 0, 0); |
| 3481 | @ </p> |
| 3482 | }else if( P("submit")!=0 && cgi_csrf_safe(2) ){ |
| 3483 | char *zErr = alert_send_announcement(); |
| 3484 | style_header("Announcement Sent"); |
| 3485 | if( zErr ){ |
| 3486 | @ <h1>Internal Error</h1> |
| 3487 | @ <p>The following error was reported by the system: |
| @@ -3502,10 +3502,11 @@ | |
| 3502 | return; |
| 3503 | } |
| 3504 | |
| 3505 | style_header("Send Announcement"); |
| 3506 | @ <form method="POST" action="%R/%s(zAction)"> |
| 3507 | login_insert_csrf_secret(); |
| 3508 | @ <table class="subscribe"> |
| 3509 | if( g.perm.Admin ){ |
| 3510 | int aa = PB("aa"); |
| 3511 | int all = PB("all"); |
| 3512 | int aMod = PB("mods"); |
| 3513 |
-8
| --- src/builtin.c | ||
| +++ src/builtin.c | ||
| @@ -666,18 +666,10 @@ | ||
| 666 | 666 | CX("name: %!j,", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest"); |
| 667 | 667 | CX("isAdmin: %s", (g.perm.Admin || g.perm.Setup) ? "true" : "false"); |
| 668 | 668 | CX("};\n"/*fossil.user*/); |
| 669 | 669 | CX("if(fossil.config.skin.isDark) " |
| 670 | 670 | "document.body.classList.add('fossil-dark-style');\n"); |
| 671 | -#if 0 | |
| 672 | - /* Is it safe to emit the CSRF token here? Some pages add it | |
| 673 | - ** as a hidden form field. */ | |
| 674 | - if(g.zCsrfToken[0]!=0){ | |
| 675 | - CX("window.fossil.csrfToken = %!j;\n", | |
| 676 | - g.zCsrfToken); | |
| 677 | - } | |
| 678 | -#endif | |
| 679 | 671 | /* |
| 680 | 672 | ** fossil.page holds info about the current page. This is also |
| 681 | 673 | ** where the current page "should" store any of its own |
| 682 | 674 | ** page-specific state, and it is reserved for that purpose. |
| 683 | 675 | */ |
| 684 | 676 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -666,18 +666,10 @@ | |
| 666 | CX("name: %!j,", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest"); |
| 667 | CX("isAdmin: %s", (g.perm.Admin || g.perm.Setup) ? "true" : "false"); |
| 668 | CX("};\n"/*fossil.user*/); |
| 669 | CX("if(fossil.config.skin.isDark) " |
| 670 | "document.body.classList.add('fossil-dark-style');\n"); |
| 671 | #if 0 |
| 672 | /* Is it safe to emit the CSRF token here? Some pages add it |
| 673 | ** as a hidden form field. */ |
| 674 | if(g.zCsrfToken[0]!=0){ |
| 675 | CX("window.fossil.csrfToken = %!j;\n", |
| 676 | g.zCsrfToken); |
| 677 | } |
| 678 | #endif |
| 679 | /* |
| 680 | ** fossil.page holds info about the current page. This is also |
| 681 | ** where the current page "should" store any of its own |
| 682 | ** page-specific state, and it is reserved for that purpose. |
| 683 | */ |
| 684 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -666,18 +666,10 @@ | |
| 666 | CX("name: %!j,", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest"); |
| 667 | CX("isAdmin: %s", (g.perm.Admin || g.perm.Setup) ? "true" : "false"); |
| 668 | CX("};\n"/*fossil.user*/); |
| 669 | CX("if(fossil.config.skin.isDark) " |
| 670 | "document.body.classList.add('fossil-dark-style');\n"); |
| 671 | /* |
| 672 | ** fossil.page holds info about the current page. This is also |
| 673 | ** where the current page "should" store any of its own |
| 674 | ** page-specific state, and it is reserved for that purpose. |
| 675 | */ |
| 676 |
+48
-26
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -314,17 +314,15 @@ | ||
| 314 | 314 | if( g.zBaseURL!=0 && fossil_strncmp(g.zBaseURL, "https:", 6)==0 ){ |
| 315 | 315 | zSecure = " secure;"; |
| 316 | 316 | } |
| 317 | 317 | if( lifetime!=0 ){ |
| 318 | 318 | blob_appendf(&extraHeader, |
| 319 | - "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly; " | |
| 320 | - "%s Version=1\r\n", | |
| 319 | + "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly; %s\r\n", | |
| 321 | 320 | zName, lifetime>0 ? zValue : "null", zPath, lifetime, zSecure); |
| 322 | 321 | }else{ |
| 323 | 322 | blob_appendf(&extraHeader, |
| 324 | - "Set-Cookie: %s=%t; Path=%s; HttpOnly; " | |
| 325 | - "%s Version=1\r\n", | |
| 323 | + "Set-Cookie: %s=%t; Path=%s; HttpOnly; %s\r\n", | |
| 326 | 324 | zName, zValue, zPath, zSecure); |
| 327 | 325 | } |
| 328 | 326 | } |
| 329 | 327 | |
| 330 | 328 | |
| @@ -700,42 +698,66 @@ | ||
| 700 | 698 | nBase = (int)strlen(g.zBaseURL); |
| 701 | 699 | if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0; |
| 702 | 700 | if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0; |
| 703 | 701 | return 1; |
| 704 | 702 | } |
| 703 | + | |
| 704 | +/* | |
| 705 | +** Return true if the current CGI request is a POST request | |
| 706 | +*/ | |
| 707 | +static int cgi_is_post_request(void){ | |
| 708 | + const char *zMethod = P("REQUEST_METHOD"); | |
| 709 | + if( zMethod==0 ) return 0; | |
| 710 | + if( strcmp(zMethod,"POST")!=0 ) return 0; | |
| 711 | + return 1; | |
| 712 | +} | |
| 705 | 713 | |
| 706 | 714 | /* |
| 707 | 715 | ** 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: | |
| 716 | +** Cross-Site Request Forgery (CSRF) attack. The level of checking | |
| 717 | +** is determined by the parameter. The higher the number, the more | |
| 718 | +** secure we are: | |
| 719 | +** | |
| 720 | +** 0: Request must come from the same origin | |
| 721 | +** 1: Same origin and must be a POST request | |
| 722 | +** 2: All of the above plus must have a valid CSRF token | |
| 723 | +** | |
| 724 | +** Results are cached in the g.okCsrf variable. The g.okCsrf value | |
| 725 | +** has meaning as follows: | |
| 710 | 726 | ** |
| 711 | -** * The HTTP_REFERER must have the same origin | |
| 712 | -** * The REQUEST_METHOD must be POST - or requirePost==0 | |
| 727 | +** -1: Not a secure request | |
| 728 | +** 0: Status unknown | |
| 729 | +** 1: Request comes from the same origin | |
| 730 | +** 2: (1) plus it is a POST request | |
| 731 | +** 3: (2) plus there is a valid "csrf" token in the request | |
| 713 | 732 | */ |
| 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; | |
| 733 | +int cgi_csrf_safe(int securityLevel){ | |
| 734 | + if( g.okCsrf<0 ) return 0; | |
| 735 | + if( g.okCsrf==0 ){ | |
| 736 | + if( !cgi_same_origin() ){ | |
| 737 | + g.okCsrf = -1; | |
| 738 | + }else{ | |
| 739 | + g.okCsrf = 1; | |
| 740 | + if( cgi_is_post_request() ){ | |
| 741 | + g.okCsrf = 2; | |
| 742 | + if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){ | |
| 743 | + g.okCsrf = 3; | |
| 744 | + } | |
| 745 | + } | |
| 746 | + } | |
| 719 | 747 | } |
| 720 | - return cgi_same_origin(); | |
| 748 | + return g.okCsrf >= (securityLevel+1); | |
| 721 | 749 | } |
| 722 | 750 | |
| 723 | 751 | /* |
| 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. | |
| 752 | +** Verify that CSRF defenses are maximal - that the request comes from | |
| 753 | +** the same origin, that it is a POST request, and that there is a valid | |
| 754 | +** "csrf" token. If this is not the case, fail immediately. | |
| 730 | 755 | */ |
| 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"); | |
| 756 | +void cgi_csrf_verify(void){ | |
| 757 | + if( !cgi_csrf_safe(2) ){ | |
| 758 | + fossil_fatal("Cross-site Request Forgery detected"); | |
| 737 | 759 | } |
| 738 | 760 | } |
| 739 | 761 | |
| 740 | 762 | /* |
| 741 | 763 | ** Information about all query parameters, post parameter, cookies and |
| 742 | 764 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -314,17 +314,15 @@ | |
| 314 | if( g.zBaseURL!=0 && fossil_strncmp(g.zBaseURL, "https:", 6)==0 ){ |
| 315 | zSecure = " secure;"; |
| 316 | } |
| 317 | if( lifetime!=0 ){ |
| 318 | blob_appendf(&extraHeader, |
| 319 | "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly; " |
| 320 | "%s Version=1\r\n", |
| 321 | zName, lifetime>0 ? zValue : "null", zPath, lifetime, zSecure); |
| 322 | }else{ |
| 323 | blob_appendf(&extraHeader, |
| 324 | "Set-Cookie: %s=%t; Path=%s; HttpOnly; " |
| 325 | "%s Version=1\r\n", |
| 326 | zName, zValue, zPath, zSecure); |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | |
| @@ -700,42 +698,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 | |
| @@ -314,17 +314,15 @@ | |
| 314 | if( g.zBaseURL!=0 && fossil_strncmp(g.zBaseURL, "https:", 6)==0 ){ |
| 315 | zSecure = " secure;"; |
| 316 | } |
| 317 | if( lifetime!=0 ){ |
| 318 | blob_appendf(&extraHeader, |
| 319 | "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly; %s\r\n", |
| 320 | zName, lifetime>0 ? zValue : "null", zPath, lifetime, zSecure); |
| 321 | }else{ |
| 322 | blob_appendf(&extraHeader, |
| 323 | "Set-Cookie: %s=%t; Path=%s; HttpOnly; %s\r\n", |
| 324 | zName, zValue, zPath, zSecure); |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | |
| @@ -700,42 +698,66 @@ | |
| 698 | nBase = (int)strlen(g.zBaseURL); |
| 699 | if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0; |
| 700 | if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0; |
| 701 | return 1; |
| 702 | } |
| 703 | |
| 704 | /* |
| 705 | ** Return true if the current CGI request is a POST request |
| 706 | */ |
| 707 | static int cgi_is_post_request(void){ |
| 708 | const char *zMethod = P("REQUEST_METHOD"); |
| 709 | if( zMethod==0 ) return 0; |
| 710 | if( strcmp(zMethod,"POST")!=0 ) return 0; |
| 711 | return 1; |
| 712 | } |
| 713 | |
| 714 | /* |
| 715 | ** Return true if the current request appears to be safe from a |
| 716 | ** Cross-Site Request Forgery (CSRF) attack. The level of checking |
| 717 | ** is determined by the parameter. The higher the number, the more |
| 718 | ** secure we are: |
| 719 | ** |
| 720 | ** 0: Request must come from the same origin |
| 721 | ** 1: Same origin and must be a POST request |
| 722 | ** 2: All of the above plus must have a valid CSRF token |
| 723 | ** |
| 724 | ** Results are cached in the g.okCsrf variable. The g.okCsrf value |
| 725 | ** has meaning as follows: |
| 726 | ** |
| 727 | ** -1: Not a secure request |
| 728 | ** 0: Status unknown |
| 729 | ** 1: Request comes from the same origin |
| 730 | ** 2: (1) plus it is a POST request |
| 731 | ** 3: (2) plus there is a valid "csrf" token in the request |
| 732 | */ |
| 733 | int cgi_csrf_safe(int securityLevel){ |
| 734 | if( g.okCsrf<0 ) return 0; |
| 735 | if( g.okCsrf==0 ){ |
| 736 | if( !cgi_same_origin() ){ |
| 737 | g.okCsrf = -1; |
| 738 | }else{ |
| 739 | g.okCsrf = 1; |
| 740 | if( cgi_is_post_request() ){ |
| 741 | g.okCsrf = 2; |
| 742 | if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){ |
| 743 | g.okCsrf = 3; |
| 744 | } |
| 745 | } |
| 746 | } |
| 747 | } |
| 748 | return g.okCsrf >= (securityLevel+1); |
| 749 | } |
| 750 | |
| 751 | /* |
| 752 | ** Verify that CSRF defenses are maximal - that the request comes from |
| 753 | ** the same origin, that it is a POST request, and that there is a valid |
| 754 | ** "csrf" token. If this is not the case, fail immediately. |
| 755 | */ |
| 756 | void cgi_csrf_verify(void){ |
| 757 | if( !cgi_csrf_safe(2) ){ |
| 758 | fossil_fatal("Cross-site Request Forgery detected"); |
| 759 | } |
| 760 | } |
| 761 | |
| 762 | /* |
| 763 | ** Information about all query parameters, post parameter, cookies and |
| 764 |
+1
-2
| --- src/event.c | ||
| +++ src/event.c | ||
| @@ -468,12 +468,11 @@ | ||
| 468 | 468 | rid |
| 469 | 469 | ); |
| 470 | 470 | } |
| 471 | 471 | } |
| 472 | 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(); | |
| 473 | + if( P("submit")!=0 && (zBody!=0 && zComment!=0) && cgi_csrf_safe(2) ){ | |
| 475 | 474 | if ( !event_commit_common(rid, zId, zBody, zETime, |
| 476 | 475 | zMimetype, zComment, zTags, |
| 477 | 476 | zClrFlag[0] ? zClr : 0) ){ |
| 478 | 477 | style_header("Error"); |
| 479 | 478 | @ Internal error: Fossil tried to make an invalid artifact for |
| 480 | 479 |
| --- 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 |
+9
-5
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -897,10 +897,11 @@ | ||
| 897 | 897 | } |
| 898 | 898 | }else if( bSameUser ){ |
| 899 | 899 | /* Allow users to delete (reject) their own pending posts. */ |
| 900 | 900 | @ <input type="submit" name="reject" value="Delete"> |
| 901 | 901 | } |
| 902 | + login_insert_csrf_secret(); | |
| 902 | 903 | @ </form> |
| 903 | 904 | if( bSelect && forumpost_may_close() && iClosed>=0 ){ |
| 904 | 905 | int iHead = forumpost_head_rid(p->fpid); |
| 905 | 906 | @ <form method="post" \ |
| 906 | 907 | @ action='%R/forumpost_%s(iClosed > 0 ? "reopen" : "close")'> |
| @@ -1423,11 +1424,11 @@ | ||
| 1423 | 1424 | login_check_credentials(); |
| 1424 | 1425 | if( forumpost_may_close()==0 ){ |
| 1425 | 1426 | login_needed(g.anon.Admin); |
| 1426 | 1427 | return; |
| 1427 | 1428 | } |
| 1428 | - cgi_csrf_verify(1); | |
| 1429 | + cgi_csrf_verify(); | |
| 1429 | 1430 | fpid = symbolic_name_to_rid(zFpid, "f"); |
| 1430 | 1431 | if( fpid<=0 ){ |
| 1431 | 1432 | webpage_error("Missing or invalid fpid query parameter"); |
| 1432 | 1433 | } |
| 1433 | 1434 | fClose = sqlite3_strglob("*_close*", g.zPath)==0; |
| @@ -1539,11 +1540,11 @@ | ||
| 1539 | 1540 | login_check_credentials(); |
| 1540 | 1541 | if( !g.perm.WrForum ){ |
| 1541 | 1542 | login_needed(g.anon.WrForum); |
| 1542 | 1543 | return; |
| 1543 | 1544 | } |
| 1544 | - if( P("submit") && cgi_csrf_safe(1) ){ | |
| 1545 | + if( P("submit") && cgi_csrf_safe(2) ){ | |
| 1545 | 1546 | if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent, |
| 1546 | 1547 | forum_post_flags()) ) return; |
| 1547 | 1548 | } |
| 1548 | 1549 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1549 | 1550 | @ <h1>Preview:</h1> |
| @@ -1560,10 +1561,11 @@ | ||
| 1560 | 1561 | @ <input type="submit" name="submit" value="Submit"> |
| 1561 | 1562 | }else{ |
| 1562 | 1563 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1563 | 1564 | } |
| 1564 | 1565 | forum_render_debug_options(); |
| 1566 | + login_insert_csrf_secret(); | |
| 1565 | 1567 | @ </form> |
| 1566 | 1568 | forum_emit_js(); |
| 1567 | 1569 | style_finish_page(); |
| 1568 | 1570 | } |
| 1569 | 1571 | |
| @@ -1611,11 +1613,11 @@ | ||
| 1611 | 1613 | return; |
| 1612 | 1614 | } |
| 1613 | 1615 | bPreview = P("preview")!=0; |
| 1614 | 1616 | bReply = P("reply")!=0; |
| 1615 | 1617 | iClosed = forum_rid_is_closed(fpid, 1); |
| 1616 | - isCsrfSafe = cgi_csrf_safe(1); | |
| 1618 | + isCsrfSafe = cgi_csrf_safe(2); | |
| 1617 | 1619 | bPrivate = content_is_private(fpid); |
| 1618 | 1620 | bSameUser = login_is_individual() |
| 1619 | 1621 | && fossil_strcmp(pPost->zUser, g.zLogin)==0; |
| 1620 | 1622 | if( isCsrfSafe && (g.perm.ModForum || (bPrivate && bSameUser)) ){ |
| 1621 | 1623 | if( g.perm.ModForum && P("approve") ){ |
| @@ -1679,10 +1681,11 @@ | ||
| 1679 | 1681 | forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki, |
| 1680 | 1682 | "forumEdit", 1); |
| 1681 | 1683 | @ <h1>Change Into:</h1> |
| 1682 | 1684 | forum_render(zTitle, zMimetype, zContent,"forumEdit", 1); |
| 1683 | 1685 | @ <form action="%R/forume2" method="POST"> |
| 1686 | + login_insert_csrf_secret(); | |
| 1684 | 1687 | @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> |
| 1685 | 1688 | @ <input type="hidden" name="nullout" value="1"> |
| 1686 | 1689 | @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> |
| 1687 | 1690 | @ <input type="hidden" name="content" value="%h(zContent)"> |
| 1688 | 1691 | if( zTitle ){ |
| @@ -1706,10 +1709,11 @@ | ||
| 1706 | 1709 | @ <h2>Preview of Edited Post:</h2> |
| 1707 | 1710 | forum_render(zTitle, zMimetype, zContent,"forumEdit", 1); |
| 1708 | 1711 | } |
| 1709 | 1712 | @ <h2>Revised Message:</h2> |
| 1710 | 1713 | @ <form action="%R/forume2" method="POST"> |
| 1714 | + login_insert_csrf_secret(); | |
| 1711 | 1715 | @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> |
| 1712 | 1716 | @ <input type="hidden" name="edit" value="1"> |
| 1713 | 1717 | forum_from_line(); |
| 1714 | 1718 | forum_post_widget(zTitle, zMimetype, zContent); |
| 1715 | 1719 | }else{ |
| @@ -1750,10 +1754,11 @@ | ||
| 1750 | 1754 | if( !iClosed || g.perm.Admin ) { |
| 1751 | 1755 | @ <input type="submit" name="submit" value="Submit"> |
| 1752 | 1756 | } |
| 1753 | 1757 | } |
| 1754 | 1758 | forum_render_debug_options(); |
| 1759 | + login_insert_csrf_secret(); | |
| 1755 | 1760 | @ </form> |
| 1756 | 1761 | forum_emit_js(); |
| 1757 | 1762 | style_finish_page(); |
| 1758 | 1763 | } |
| 1759 | 1764 | |
| @@ -1842,14 +1847,13 @@ | ||
| 1842 | 1847 | } |
| 1843 | 1848 | } |
| 1844 | 1849 | |
| 1845 | 1850 | @ <h2>Settings</h2> |
| 1846 | 1851 | @ <p>Configuration settings specific to the forum.</p> |
| 1847 | - if( P("submit") && cgi_csrf_safe(1) ){ | |
| 1852 | + if( P("submit") && cgi_csrf_safe(2) ){ | |
| 1848 | 1853 | int i = 0; |
| 1849 | 1854 | const char *zSetting; |
| 1850 | - login_verify_csrf_secret(); | |
| 1851 | 1855 | db_begin_transaction(); |
| 1852 | 1856 | while( (zSetting = zSettingsBool[i++]) ){ |
| 1853 | 1857 | const char *z = P(zSetting); |
| 1854 | 1858 | if( !z || !z[0] ) z = "off"; |
| 1855 | 1859 | db_set(zSetting/*works-like:"x"*/, z, 0); |
| 1856 | 1860 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -897,10 +897,11 @@ | |
| 897 | } |
| 898 | }else if( bSameUser ){ |
| 899 | /* Allow users to delete (reject) their own pending posts. */ |
| 900 | @ <input type="submit" name="reject" value="Delete"> |
| 901 | } |
| 902 | @ </form> |
| 903 | if( bSelect && forumpost_may_close() && iClosed>=0 ){ |
| 904 | int iHead = forumpost_head_rid(p->fpid); |
| 905 | @ <form method="post" \ |
| 906 | @ action='%R/forumpost_%s(iClosed > 0 ? "reopen" : "close")'> |
| @@ -1423,11 +1424,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; |
| @@ -1539,11 +1540,11 @@ | |
| 1539 | login_check_credentials(); |
| 1540 | if( !g.perm.WrForum ){ |
| 1541 | login_needed(g.anon.WrForum); |
| 1542 | return; |
| 1543 | } |
| 1544 | if( P("submit") && cgi_csrf_safe(1) ){ |
| 1545 | if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent, |
| 1546 | forum_post_flags()) ) return; |
| 1547 | } |
| 1548 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1549 | @ <h1>Preview:</h1> |
| @@ -1560,10 +1561,11 @@ | |
| 1560 | @ <input type="submit" name="submit" value="Submit"> |
| 1561 | }else{ |
| 1562 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1563 | } |
| 1564 | forum_render_debug_options(); |
| 1565 | @ </form> |
| 1566 | forum_emit_js(); |
| 1567 | style_finish_page(); |
| 1568 | } |
| 1569 | |
| @@ -1611,11 +1613,11 @@ | |
| 1611 | return; |
| 1612 | } |
| 1613 | bPreview = P("preview")!=0; |
| 1614 | bReply = P("reply")!=0; |
| 1615 | iClosed = forum_rid_is_closed(fpid, 1); |
| 1616 | isCsrfSafe = cgi_csrf_safe(1); |
| 1617 | bPrivate = content_is_private(fpid); |
| 1618 | bSameUser = login_is_individual() |
| 1619 | && fossil_strcmp(pPost->zUser, g.zLogin)==0; |
| 1620 | if( isCsrfSafe && (g.perm.ModForum || (bPrivate && bSameUser)) ){ |
| 1621 | if( g.perm.ModForum && P("approve") ){ |
| @@ -1679,10 +1681,11 @@ | |
| 1679 | forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki, |
| 1680 | "forumEdit", 1); |
| 1681 | @ <h1>Change Into:</h1> |
| 1682 | forum_render(zTitle, zMimetype, zContent,"forumEdit", 1); |
| 1683 | @ <form action="%R/forume2" method="POST"> |
| 1684 | @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> |
| 1685 | @ <input type="hidden" name="nullout" value="1"> |
| 1686 | @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> |
| 1687 | @ <input type="hidden" name="content" value="%h(zContent)"> |
| 1688 | if( zTitle ){ |
| @@ -1706,10 +1709,11 @@ | |
| 1706 | @ <h2>Preview of Edited Post:</h2> |
| 1707 | forum_render(zTitle, zMimetype, zContent,"forumEdit", 1); |
| 1708 | } |
| 1709 | @ <h2>Revised Message:</h2> |
| 1710 | @ <form action="%R/forume2" method="POST"> |
| 1711 | @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> |
| 1712 | @ <input type="hidden" name="edit" value="1"> |
| 1713 | forum_from_line(); |
| 1714 | forum_post_widget(zTitle, zMimetype, zContent); |
| 1715 | }else{ |
| @@ -1750,10 +1754,11 @@ | |
| 1750 | if( !iClosed || g.perm.Admin ) { |
| 1751 | @ <input type="submit" name="submit" value="Submit"> |
| 1752 | } |
| 1753 | } |
| 1754 | forum_render_debug_options(); |
| 1755 | @ </form> |
| 1756 | forum_emit_js(); |
| 1757 | style_finish_page(); |
| 1758 | } |
| 1759 | |
| @@ -1842,14 +1847,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 | |
| @@ -897,10 +897,11 @@ | |
| 897 | } |
| 898 | }else if( bSameUser ){ |
| 899 | /* Allow users to delete (reject) their own pending posts. */ |
| 900 | @ <input type="submit" name="reject" value="Delete"> |
| 901 | } |
| 902 | login_insert_csrf_secret(); |
| 903 | @ </form> |
| 904 | if( bSelect && forumpost_may_close() && iClosed>=0 ){ |
| 905 | int iHead = forumpost_head_rid(p->fpid); |
| 906 | @ <form method="post" \ |
| 907 | @ action='%R/forumpost_%s(iClosed > 0 ? "reopen" : "close")'> |
| @@ -1423,11 +1424,11 @@ | |
| 1424 | login_check_credentials(); |
| 1425 | if( forumpost_may_close()==0 ){ |
| 1426 | login_needed(g.anon.Admin); |
| 1427 | return; |
| 1428 | } |
| 1429 | cgi_csrf_verify(); |
| 1430 | fpid = symbolic_name_to_rid(zFpid, "f"); |
| 1431 | if( fpid<=0 ){ |
| 1432 | webpage_error("Missing or invalid fpid query parameter"); |
| 1433 | } |
| 1434 | fClose = sqlite3_strglob("*_close*", g.zPath)==0; |
| @@ -1539,11 +1540,11 @@ | |
| 1540 | login_check_credentials(); |
| 1541 | if( !g.perm.WrForum ){ |
| 1542 | login_needed(g.anon.WrForum); |
| 1543 | return; |
| 1544 | } |
| 1545 | if( P("submit") && cgi_csrf_safe(2) ){ |
| 1546 | if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent, |
| 1547 | forum_post_flags()) ) return; |
| 1548 | } |
| 1549 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1550 | @ <h1>Preview:</h1> |
| @@ -1560,10 +1561,11 @@ | |
| 1561 | @ <input type="submit" name="submit" value="Submit"> |
| 1562 | }else{ |
| 1563 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1564 | } |
| 1565 | forum_render_debug_options(); |
| 1566 | login_insert_csrf_secret(); |
| 1567 | @ </form> |
| 1568 | forum_emit_js(); |
| 1569 | style_finish_page(); |
| 1570 | } |
| 1571 | |
| @@ -1611,11 +1613,11 @@ | |
| 1613 | return; |
| 1614 | } |
| 1615 | bPreview = P("preview")!=0; |
| 1616 | bReply = P("reply")!=0; |
| 1617 | iClosed = forum_rid_is_closed(fpid, 1); |
| 1618 | isCsrfSafe = cgi_csrf_safe(2); |
| 1619 | bPrivate = content_is_private(fpid); |
| 1620 | bSameUser = login_is_individual() |
| 1621 | && fossil_strcmp(pPost->zUser, g.zLogin)==0; |
| 1622 | if( isCsrfSafe && (g.perm.ModForum || (bPrivate && bSameUser)) ){ |
| 1623 | if( g.perm.ModForum && P("approve") ){ |
| @@ -1679,10 +1681,11 @@ | |
| 1681 | forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki, |
| 1682 | "forumEdit", 1); |
| 1683 | @ <h1>Change Into:</h1> |
| 1684 | forum_render(zTitle, zMimetype, zContent,"forumEdit", 1); |
| 1685 | @ <form action="%R/forume2" method="POST"> |
| 1686 | login_insert_csrf_secret(); |
| 1687 | @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> |
| 1688 | @ <input type="hidden" name="nullout" value="1"> |
| 1689 | @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> |
| 1690 | @ <input type="hidden" name="content" value="%h(zContent)"> |
| 1691 | if( zTitle ){ |
| @@ -1706,10 +1709,11 @@ | |
| 1709 | @ <h2>Preview of Edited Post:</h2> |
| 1710 | forum_render(zTitle, zMimetype, zContent,"forumEdit", 1); |
| 1711 | } |
| 1712 | @ <h2>Revised Message:</h2> |
| 1713 | @ <form action="%R/forume2" method="POST"> |
| 1714 | login_insert_csrf_secret(); |
| 1715 | @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> |
| 1716 | @ <input type="hidden" name="edit" value="1"> |
| 1717 | forum_from_line(); |
| 1718 | forum_post_widget(zTitle, zMimetype, zContent); |
| 1719 | }else{ |
| @@ -1750,10 +1754,11 @@ | |
| 1754 | if( !iClosed || g.perm.Admin ) { |
| 1755 | @ <input type="submit" name="submit" value="Submit"> |
| 1756 | } |
| 1757 | } |
| 1758 | forum_render_debug_options(); |
| 1759 | login_insert_csrf_secret(); |
| 1760 | @ </form> |
| 1761 | forum_emit_js(); |
| 1762 | style_finish_page(); |
| 1763 | } |
| 1764 | |
| @@ -1842,14 +1847,13 @@ | |
| 1847 | } |
| 1848 | } |
| 1849 | |
| 1850 | @ <h2>Settings</h2> |
| 1851 | @ <p>Configuration settings specific to the forum.</p> |
| 1852 | if( P("submit") && cgi_csrf_safe(2) ){ |
| 1853 | int i = 0; |
| 1854 | const char *zSetting; |
| 1855 | db_begin_transaction(); |
| 1856 | while( (zSetting = zSettingsBool[i++]) ){ |
| 1857 | const char *z = P(zSetting); |
| 1858 | if( !z || !z[0] ) z = "off"; |
| 1859 | db_set(zSetting/*works-like:"x"*/, z, 0); |
| 1860 |
+1
-3
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -3213,15 +3213,14 @@ | ||
| 3213 | 3213 | zNewTag = PDT("tagname",""); |
| 3214 | 3214 | zNewBrFlag = P("newbr") ? " checked" : ""; |
| 3215 | 3215 | zNewBranch = PDT("brname",""); |
| 3216 | 3216 | zCloseFlag = P("close") ? " checked" : ""; |
| 3217 | 3217 | zHideFlag = P("hide") ? " checked" : ""; |
| 3218 | - if( P("apply") && cgi_csrf_safe(1) ){ | |
| 3218 | + if( P("apply") && cgi_csrf_safe(2) ){ | |
| 3219 | 3219 | Blob ctrl; |
| 3220 | 3220 | char *zNow; |
| 3221 | 3221 | |
| 3222 | - login_verify_csrf_secret(); | |
| 3223 | 3222 | blob_zero(&ctrl); |
| 3224 | 3223 | zNow = date_in_standard_format(zChngTime ? zChngTime : "now"); |
| 3225 | 3224 | blob_appendf(&ctrl, "D %s\n", zNow); |
| 3226 | 3225 | init_newtags(); |
| 3227 | 3226 | if( zNewColorFlag[0] |
| @@ -3300,11 +3299,10 @@ | ||
| 3300 | 3299 | blob_reset(&suffix); |
| 3301 | 3300 | } |
| 3302 | 3301 | @ <p>Make changes to attributes of check-in |
| 3303 | 3302 | @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p> |
| 3304 | 3303 | form_begin(0, "%R/ci_edit"); |
| 3305 | - login_insert_csrf_secret(); | |
| 3306 | 3304 | @ <div><input type="hidden" name="r" value="%s(zUuid)"> |
| 3307 | 3305 | @ <table border="0" cellspacing="10"> |
| 3308 | 3306 | |
| 3309 | 3307 | @ <tr><th align="right" valign="top">User:</th> |
| 3310 | 3308 | @ <td valign="top"> |
| 3311 | 3309 |
| --- 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 |
+1
-1
| --- src/interwiki.c | ||
| +++ src/interwiki.c | ||
| @@ -314,11 +314,11 @@ | ||
| 314 | 314 | login_check_credentials(); |
| 315 | 315 | if( !g.perm.Read && !g.perm.RdWiki && ~g.perm.RdTkt ){ |
| 316 | 316 | login_needed(0); |
| 317 | 317 | return; |
| 318 | 318 | } |
| 319 | - if( g.perm.Setup && P("submit")!=0 && cgi_csrf_safe(1) ){ | |
| 319 | + if( g.perm.Setup && P("submit")!=0 && cgi_csrf_safe(2) ){ | |
| 320 | 320 | zTag = PT("tag"); |
| 321 | 321 | zBase = PT("base"); |
| 322 | 322 | zHash = PT("hash"); |
| 323 | 323 | zWiki = PT("wiki"); |
| 324 | 324 | if( zTag==0 || zTag[0]==0 || !interwiki_valid_name(zTag) ){ |
| 325 | 325 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -314,11 +314,11 @@ | |
| 314 | login_check_credentials(); |
| 315 | if( !g.perm.Read && !g.perm.RdWiki && ~g.perm.RdTkt ){ |
| 316 | login_needed(0); |
| 317 | return; |
| 318 | } |
| 319 | if( g.perm.Setup && P("submit")!=0 && cgi_csrf_safe(1) ){ |
| 320 | zTag = PT("tag"); |
| 321 | zBase = PT("base"); |
| 322 | zHash = PT("hash"); |
| 323 | zWiki = PT("wiki"); |
| 324 | if( zTag==0 || zTag[0]==0 || !interwiki_valid_name(zTag) ){ |
| 325 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -314,11 +314,11 @@ | |
| 314 | login_check_credentials(); |
| 315 | if( !g.perm.Read && !g.perm.RdWiki && ~g.perm.RdTkt ){ |
| 316 | login_needed(0); |
| 317 | return; |
| 318 | } |
| 319 | if( g.perm.Setup && P("submit")!=0 && cgi_csrf_safe(2) ){ |
| 320 | zTag = PT("tag"); |
| 321 | zBase = PT("base"); |
| 322 | zHash = PT("hash"); |
| 323 | zWiki = PT("wiki"); |
| 324 | if( zTag==0 || zTag[0]==0 || !interwiki_valid_name(zTag) ){ |
| 325 |
+26
-22
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -49,10 +49,25 @@ | ||
| 49 | 49 | # define sleep Sleep /* windows does not have sleep, but Sleep */ |
| 50 | 50 | # endif |
| 51 | 51 | #endif |
| 52 | 52 | #include <time.h> |
| 53 | 53 | |
| 54 | +/* | |
| 55 | +** Compute an appropriate Anti-CSRF token into g.zCsrfToken[]. | |
| 56 | +*/ | |
| 57 | +static void login_create_csrf_secret(const char *zSeed){ | |
| 58 | + unsigned char zResult[20]; | |
| 59 | + int i; | |
| 60 | + | |
| 61 | + sha1sum_binary(zSeed, zResult); | |
| 62 | + for(i=0; i<sizeof(g.zCsrfToken)-1; i++){ | |
| 63 | + g.zCsrfToken[i] = "abcdefghijklmnopqrstuvwxyz" | |
| 64 | + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
| 65 | + "0123456789-/"[zResult[i]%64]; | |
| 66 | + } | |
| 67 | + g.zCsrfToken[i] = 0; | |
| 68 | +} | |
| 54 | 69 | |
| 55 | 70 | /* |
| 56 | 71 | ** Return the login-group name. Or return 0 if this repository is |
| 57 | 72 | ** not a member of a login-group. |
| 58 | 73 | */ |
| @@ -583,11 +598,11 @@ | ||
| 583 | 598 | constant_time_cmp_function, 0, 0); |
| 584 | 599 | zUsername = P("u"); |
| 585 | 600 | zPasswd = P("p"); |
| 586 | 601 | anonFlag = g.zLogin==0 && PB("anon"); |
| 587 | 602 | /* Handle log-out requests */ |
| 588 | - if( P("out") ){ | |
| 603 | + if( P("out") && cgi_csrf_safe(2) ){ | |
| 589 | 604 | login_clear_login_data(); |
| 590 | 605 | redirect_to_g(); |
| 591 | 606 | return; |
| 592 | 607 | } |
| 593 | 608 | |
| @@ -598,10 +613,11 @@ | ||
| 598 | 613 | } |
| 599 | 614 | |
| 600 | 615 | /* Deal with password-change requests */ |
| 601 | 616 | if( g.perm.Password && zPasswd |
| 602 | 617 | && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 |
| 618 | + && cgi_csrf_safe(2) | |
| 603 | 619 | ){ |
| 604 | 620 | /* If there is not a "real" login, we cannot change any password. */ |
| 605 | 621 | if( g.zLogin ){ |
| 606 | 622 | /* The user requests a password change */ |
| 607 | 623 | zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); |
| @@ -1288,10 +1304,11 @@ | ||
| 1288 | 1304 | || (g.fSshClient & CGI_SSH_CLIENT)!=0 ) |
| 1289 | 1305 | && g.useLocalauth |
| 1290 | 1306 | && db_get_int("localauth",0)==0 |
| 1291 | 1307 | && P("HTTPS")==0 |
| 1292 | 1308 | ){ |
| 1309 | + char *zSeed; | |
| 1293 | 1310 | if( g.localOpen ) zLogin = db_lget("default-user",0); |
| 1294 | 1311 | if( zLogin!=0 ){ |
| 1295 | 1312 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin); |
| 1296 | 1313 | }else{ |
| 1297 | 1314 | uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); |
| @@ -1298,11 +1315,14 @@ | ||
| 1298 | 1315 | } |
| 1299 | 1316 | g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); |
| 1300 | 1317 | zCap = "sxy"; |
| 1301 | 1318 | g.noPswd = 1; |
| 1302 | 1319 | g.isHuman = 1; |
| 1303 | - sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost"); | |
| 1320 | + zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)" | |
| 1321 | + " FROM user WHERE uid=%d", uid); | |
| 1322 | + login_create_csrf_secret(zSeed); | |
| 1323 | + fossil_free(zSeed); | |
| 1304 | 1324 | } |
| 1305 | 1325 | |
| 1306 | 1326 | /* Check the login cookie to see if it matches a known valid user. |
| 1307 | 1327 | */ |
| 1308 | 1328 | if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){ |
| @@ -1353,11 +1373,11 @@ | ||
| 1353 | 1373 | if( uid==0 && login_transfer_credentials(zUser,zArg,zHash) ){ |
| 1354 | 1374 | uid = login_find_user(zUser, zHash); |
| 1355 | 1375 | if( uid ) record_login_attempt(zUser, zIpAddr, 1); |
| 1356 | 1376 | } |
| 1357 | 1377 | } |
| 1358 | - sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash); | |
| 1378 | + login_create_csrf_secret(zHash); | |
| 1359 | 1379 | } |
| 1360 | 1380 | |
| 1361 | 1381 | /* If no user found and the REMOTE_USER environment variable is set, |
| 1362 | 1382 | ** then accept the value of REMOTE_USER as the user. |
| 1363 | 1383 | */ |
| @@ -1403,11 +1423,11 @@ | ||
| 1403 | 1423 | if( uid==0 ){ |
| 1404 | 1424 | /* If there is no user "nobody", then make one up - with no privileges */ |
| 1405 | 1425 | uid = -1; |
| 1406 | 1426 | zCap = ""; |
| 1407 | 1427 | } |
| 1408 | - sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "none"); | |
| 1428 | + login_create_csrf_secret("none"); | |
| 1409 | 1429 | } |
| 1410 | 1430 | |
| 1411 | 1431 | login_set_uid(uid, zCap); |
| 1412 | 1432 | } |
| 1413 | 1433 | |
| @@ -1805,26 +1825,10 @@ | ||
| 1805 | 1825 | */ |
| 1806 | 1826 | void login_insert_csrf_secret(void){ |
| 1807 | 1827 | @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)"> |
| 1808 | 1828 | } |
| 1809 | 1829 | |
| 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 | 1830 | /* |
| 1827 | 1831 | ** Check to see if the candidate username zUserID is already used. |
| 1828 | 1832 | ** Return 1 if it is already in use. Return 0 if the name is |
| 1829 | 1833 | ** available for a self-registeration. |
| 1830 | 1834 | */ |
| @@ -1978,11 +1982,11 @@ | ||
| 1978 | 1982 | zConfirm = PDT("cp",""); |
| 1979 | 1983 | zEAddr = PDT("ea",""); |
| 1980 | 1984 | zDName = PDT("dn",""); |
| 1981 | 1985 | |
| 1982 | 1986 | /* Verify user imputs */ |
| 1983 | - if( P("new")==0 || !cgi_csrf_safe(1) ){ | |
| 1987 | + if( P("new")==0 || !cgi_csrf_safe(2) ){ | |
| 1984 | 1988 | /* This is not a valid form submission. Fall through into |
| 1985 | 1989 | ** the form display */ |
| 1986 | 1990 | }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ |
| 1987 | 1991 | iErrLine = 6; |
| 1988 | 1992 | zErr = "Incorrect CAPTCHA"; |
| @@ -2256,11 +2260,11 @@ | ||
| 2256 | 2260 | return; |
| 2257 | 2261 | } |
| 2258 | 2262 | zEAddr = PDT("ea",""); |
| 2259 | 2263 | |
| 2260 | 2264 | /* Verify user imputs */ |
| 2261 | - if( !cgi_csrf_safe(1) || P("reqpwreset")==0 ){ | |
| 2265 | + if( !cgi_csrf_safe(2) || P("reqpwreset")==0 ){ | |
| 2262 | 2266 | /* This is the initial display of the form. No processing or error |
| 2263 | 2267 | ** checking is to be done. Fall through into the form display |
| 2264 | 2268 | */ |
| 2265 | 2269 | }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ |
| 2266 | 2270 | iErrLine = 2; |
| 2267 | 2271 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -49,10 +49,25 @@ | |
| 49 | # define sleep Sleep /* windows does not have sleep, but Sleep */ |
| 50 | # endif |
| 51 | #endif |
| 52 | #include <time.h> |
| 53 | |
| 54 | |
| 55 | /* |
| 56 | ** Return the login-group name. Or return 0 if this repository is |
| 57 | ** not a member of a login-group. |
| 58 | */ |
| @@ -583,11 +598,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 +613,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); |
| @@ -1288,10 +1304,11 @@ | |
| 1288 | || (g.fSshClient & CGI_SSH_CLIENT)!=0 ) |
| 1289 | && g.useLocalauth |
| 1290 | && db_get_int("localauth",0)==0 |
| 1291 | && P("HTTPS")==0 |
| 1292 | ){ |
| 1293 | if( g.localOpen ) zLogin = db_lget("default-user",0); |
| 1294 | if( zLogin!=0 ){ |
| 1295 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin); |
| 1296 | }else{ |
| 1297 | uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); |
| @@ -1298,11 +1315,14 @@ | |
| 1298 | } |
| 1299 | g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); |
| 1300 | zCap = "sxy"; |
| 1301 | g.noPswd = 1; |
| 1302 | g.isHuman = 1; |
| 1303 | sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost"); |
| 1304 | } |
| 1305 | |
| 1306 | /* Check the login cookie to see if it matches a known valid user. |
| 1307 | */ |
| 1308 | if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){ |
| @@ -1353,11 +1373,11 @@ | |
| 1353 | if( uid==0 && login_transfer_credentials(zUser,zArg,zHash) ){ |
| 1354 | uid = login_find_user(zUser, zHash); |
| 1355 | if( uid ) record_login_attempt(zUser, zIpAddr, 1); |
| 1356 | } |
| 1357 | } |
| 1358 | sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash); |
| 1359 | } |
| 1360 | |
| 1361 | /* If no user found and the REMOTE_USER environment variable is set, |
| 1362 | ** then accept the value of REMOTE_USER as the user. |
| 1363 | */ |
| @@ -1403,11 +1423,11 @@ | |
| 1403 | if( uid==0 ){ |
| 1404 | /* If there is no user "nobody", then make one up - with no privileges */ |
| 1405 | uid = -1; |
| 1406 | zCap = ""; |
| 1407 | } |
| 1408 | sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "none"); |
| 1409 | } |
| 1410 | |
| 1411 | login_set_uid(uid, zCap); |
| 1412 | } |
| 1413 | |
| @@ -1805,26 +1825,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 | */ |
| @@ -1978,11 +1982,11 @@ | |
| 1978 | zConfirm = PDT("cp",""); |
| 1979 | zEAddr = PDT("ea",""); |
| 1980 | zDName = PDT("dn",""); |
| 1981 | |
| 1982 | /* Verify user imputs */ |
| 1983 | if( P("new")==0 || !cgi_csrf_safe(1) ){ |
| 1984 | /* This is not a valid form submission. Fall through into |
| 1985 | ** the form display */ |
| 1986 | }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ |
| 1987 | iErrLine = 6; |
| 1988 | zErr = "Incorrect CAPTCHA"; |
| @@ -2256,11 +2260,11 @@ | |
| 2256 | return; |
| 2257 | } |
| 2258 | zEAddr = PDT("ea",""); |
| 2259 | |
| 2260 | /* Verify user imputs */ |
| 2261 | if( !cgi_csrf_safe(1) || P("reqpwreset")==0 ){ |
| 2262 | /* This is the initial display of the form. No processing or error |
| 2263 | ** checking is to be done. Fall through into the form display |
| 2264 | */ |
| 2265 | }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ |
| 2266 | iErrLine = 2; |
| 2267 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -49,10 +49,25 @@ | |
| 49 | # define sleep Sleep /* windows does not have sleep, but Sleep */ |
| 50 | # endif |
| 51 | #endif |
| 52 | #include <time.h> |
| 53 | |
| 54 | /* |
| 55 | ** Compute an appropriate Anti-CSRF token into g.zCsrfToken[]. |
| 56 | */ |
| 57 | static void login_create_csrf_secret(const char *zSeed){ |
| 58 | unsigned char zResult[20]; |
| 59 | int i; |
| 60 | |
| 61 | sha1sum_binary(zSeed, zResult); |
| 62 | for(i=0; i<sizeof(g.zCsrfToken)-1; i++){ |
| 63 | g.zCsrfToken[i] = "abcdefghijklmnopqrstuvwxyz" |
| 64 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| 65 | "0123456789-/"[zResult[i]%64]; |
| 66 | } |
| 67 | g.zCsrfToken[i] = 0; |
| 68 | } |
| 69 | |
| 70 | /* |
| 71 | ** Return the login-group name. Or return 0 if this repository is |
| 72 | ** not a member of a login-group. |
| 73 | */ |
| @@ -583,11 +598,11 @@ | |
| 598 | constant_time_cmp_function, 0, 0); |
| 599 | zUsername = P("u"); |
| 600 | zPasswd = P("p"); |
| 601 | anonFlag = g.zLogin==0 && PB("anon"); |
| 602 | /* Handle log-out requests */ |
| 603 | if( P("out") && cgi_csrf_safe(2) ){ |
| 604 | login_clear_login_data(); |
| 605 | redirect_to_g(); |
| 606 | return; |
| 607 | } |
| 608 | |
| @@ -598,10 +613,11 @@ | |
| 613 | } |
| 614 | |
| 615 | /* Deal with password-change requests */ |
| 616 | if( g.perm.Password && zPasswd |
| 617 | && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 |
| 618 | && cgi_csrf_safe(2) |
| 619 | ){ |
| 620 | /* If there is not a "real" login, we cannot change any password. */ |
| 621 | if( g.zLogin ){ |
| 622 | /* The user requests a password change */ |
| 623 | zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); |
| @@ -1288,10 +1304,11 @@ | |
| 1304 | || (g.fSshClient & CGI_SSH_CLIENT)!=0 ) |
| 1305 | && g.useLocalauth |
| 1306 | && db_get_int("localauth",0)==0 |
| 1307 | && P("HTTPS")==0 |
| 1308 | ){ |
| 1309 | char *zSeed; |
| 1310 | if( g.localOpen ) zLogin = db_lget("default-user",0); |
| 1311 | if( zLogin!=0 ){ |
| 1312 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin); |
| 1313 | }else{ |
| 1314 | uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); |
| @@ -1298,11 +1315,14 @@ | |
| 1315 | } |
| 1316 | g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); |
| 1317 | zCap = "sxy"; |
| 1318 | g.noPswd = 1; |
| 1319 | g.isHuman = 1; |
| 1320 | zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)" |
| 1321 | " FROM user WHERE uid=%d", uid); |
| 1322 | login_create_csrf_secret(zSeed); |
| 1323 | fossil_free(zSeed); |
| 1324 | } |
| 1325 | |
| 1326 | /* Check the login cookie to see if it matches a known valid user. |
| 1327 | */ |
| 1328 | if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){ |
| @@ -1353,11 +1373,11 @@ | |
| 1373 | if( uid==0 && login_transfer_credentials(zUser,zArg,zHash) ){ |
| 1374 | uid = login_find_user(zUser, zHash); |
| 1375 | if( uid ) record_login_attempt(zUser, zIpAddr, 1); |
| 1376 | } |
| 1377 | } |
| 1378 | login_create_csrf_secret(zHash); |
| 1379 | } |
| 1380 | |
| 1381 | /* If no user found and the REMOTE_USER environment variable is set, |
| 1382 | ** then accept the value of REMOTE_USER as the user. |
| 1383 | */ |
| @@ -1403,11 +1423,11 @@ | |
| 1423 | if( uid==0 ){ |
| 1424 | /* If there is no user "nobody", then make one up - with no privileges */ |
| 1425 | uid = -1; |
| 1426 | zCap = ""; |
| 1427 | } |
| 1428 | login_create_csrf_secret("none"); |
| 1429 | } |
| 1430 | |
| 1431 | login_set_uid(uid, zCap); |
| 1432 | } |
| 1433 | |
| @@ -1805,26 +1825,10 @@ | |
| 1825 | */ |
| 1826 | void login_insert_csrf_secret(void){ |
| 1827 | @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)"> |
| 1828 | } |
| 1829 | |
| 1830 | /* |
| 1831 | ** Check to see if the candidate username zUserID is already used. |
| 1832 | ** Return 1 if it is already in use. Return 0 if the name is |
| 1833 | ** available for a self-registeration. |
| 1834 | */ |
| @@ -1978,11 +1982,11 @@ | |
| 1982 | zConfirm = PDT("cp",""); |
| 1983 | zEAddr = PDT("ea",""); |
| 1984 | zDName = PDT("dn",""); |
| 1985 | |
| 1986 | /* Verify user imputs */ |
| 1987 | if( P("new")==0 || !cgi_csrf_safe(2) ){ |
| 1988 | /* This is not a valid form submission. Fall through into |
| 1989 | ** the form display */ |
| 1990 | }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ |
| 1991 | iErrLine = 6; |
| 1992 | zErr = "Incorrect CAPTCHA"; |
| @@ -2256,11 +2260,11 @@ | |
| 2260 | return; |
| 2261 | } |
| 2262 | zEAddr = PDT("ea",""); |
| 2263 | |
| 2264 | /* Verify user imputs */ |
| 2265 | if( !cgi_csrf_safe(2) || P("reqpwreset")==0 ){ |
| 2266 | /* This is the initial display of the form. No processing or error |
| 2267 | ** checking is to be done. Fall through into the form display |
| 2268 | */ |
| 2269 | }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ |
| 2270 | iErrLine = 2; |
| 2271 |
+6
-2
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -256,12 +256,16 @@ | ||
| 256 | 256 | /* all Tcl related context necessary for integration */ |
| 257 | 257 | struct TclContext tcl; |
| 258 | 258 | #endif |
| 259 | 259 | |
| 260 | 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 */ | |
| 261 | + char zCsrfToken[16]; /* 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 */ | |
| 263 | 267 | |
| 264 | 268 | int parseCnt[10]; /* Counts of artifacts parsed */ |
| 265 | 269 | FILE *fDebug; /* Write debug information here, if the file exists */ |
| 266 | 270 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 267 | 271 | int fNoThHook; /* Disable all TH1 command/webpage hooks */ |
| 268 | 272 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -256,12 +256,16 @@ | |
| 256 | /* all Tcl related context necessary for integration */ |
| 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 | |
| @@ -256,12 +256,16 @@ | |
| 256 | /* all Tcl related context necessary for integration */ |
| 257 | struct TclContext tcl; |
| 258 | #endif |
| 259 | |
| 260 | /* For defense against Cross-site Request Forgery attacks */ |
| 261 | char zCsrfToken[16]; /* 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 @@ | ||
| 471 | 471 | zClrKey = trim_string(PD("k","")); |
| 472 | 472 | zDesc = trim_string(PD("d","")); |
| 473 | 473 | zMimetype = P("m"); |
| 474 | 474 | zTag = P("x"); |
| 475 | 475 | 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) ){ | |
| 478 | 477 | db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn); |
| 479 | 478 | cgi_redirect("reportlist"); |
| 480 | 479 | return; |
| 481 | - }else if( rn>0 && P("del1") ){ | |
| 480 | + }else if( rn>0 && P("del1") && cgi_csrf_safe(2) ){ | |
| 482 | 481 | zTitle = db_text(0, "SELECT title FROM reportfmt " |
| 483 | 482 | "WHERE rn=%d", rn); |
| 484 | 483 | if( zTitle==0 ) cgi_redirect("reportlist"); |
| 485 | 484 | |
| 486 | 485 | style_header("Are You Sure?"); |
| @@ -514,12 +513,11 @@ | ||
| 514 | 513 | && db_exists("SELECT 1 FROM reportfmt WHERE title=%Q and rn<>%d", |
| 515 | 514 | zTitle, rn) |
| 516 | 515 | ){ |
| 517 | 516 | zErr = mprintf("There is already another report named \"%h\"", zTitle); |
| 518 | 517 | } |
| 519 | - if( zErr==0 ){ | |
| 520 | - login_verify_csrf_secret(); | |
| 518 | + if( zErr==0 && cgi_csrf_safe(2) ){ | |
| 521 | 519 | if( zTag && zTag[0]==0 ) zTag = 0; |
| 522 | 520 | if( zDesc && zDesc[0]==0 ){ zDesc = 0; zMimetype = 0; } |
| 523 | 521 | if( zMimetype && zMimetype[0]==0 ){ zDesc = 0; zMimetype = 0; } |
| 524 | 522 | if( rn>0 ){ |
| 525 | 523 | db_multi_exec( |
| 526 | 524 |
| --- 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 |
+2
-1
| --- src/security_audit.c | ||
| +++ src/security_audit.c | ||
| @@ -795,11 +795,11 @@ | ||
| 795 | 795 | @ command. |
| 796 | 796 | @ </ol> |
| 797 | 797 | style_finish_page(); |
| 798 | 798 | return; |
| 799 | 799 | } |
| 800 | - if( P("truncate1") && cgi_csrf_safe(1) ){ | |
| 800 | + if( P("truncate1") && cgi_csrf_safe(2) ){ | |
| 801 | 801 | fclose(fopen(g.zErrlog,"w")); |
| 802 | 802 | } |
| 803 | 803 | if( P("download") ){ |
| 804 | 804 | Blob log; |
| 805 | 805 | blob_read_from_file(&log, g.zErrlog, ExtFILE); |
| @@ -808,10 +808,11 @@ | ||
| 808 | 808 | return; |
| 809 | 809 | } |
| 810 | 810 | szFile = file_size(g.zErrlog, ExtFILE); |
| 811 | 811 | if( P("truncate") ){ |
| 812 | 812 | @ <form action="%R/errorlog" method="POST"> |
| 813 | + login_insert_csrf_secret(); | |
| 813 | 814 | @ <p>Confirm that you want to truncate the %,lld(szFile)-byte error log: |
| 814 | 815 | @ <input type="submit" name="truncate1" value="Confirm"> |
| 815 | 816 | @ <input type="submit" name="cancel" value="Cancel"> |
| 816 | 817 | @ </form> |
| 817 | 818 | style_finish_page(); |
| 818 | 819 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -795,11 +795,11 @@ | |
| 795 | @ command. |
| 796 | @ </ol> |
| 797 | style_finish_page(); |
| 798 | return; |
| 799 | } |
| 800 | if( P("truncate1") && cgi_csrf_safe(1) ){ |
| 801 | fclose(fopen(g.zErrlog,"w")); |
| 802 | } |
| 803 | if( P("download") ){ |
| 804 | Blob log; |
| 805 | blob_read_from_file(&log, g.zErrlog, ExtFILE); |
| @@ -808,10 +808,11 @@ | |
| 808 | return; |
| 809 | } |
| 810 | szFile = file_size(g.zErrlog, ExtFILE); |
| 811 | if( P("truncate") ){ |
| 812 | @ <form action="%R/errorlog" method="POST"> |
| 813 | @ <p>Confirm that you want to truncate the %,lld(szFile)-byte error log: |
| 814 | @ <input type="submit" name="truncate1" value="Confirm"> |
| 815 | @ <input type="submit" name="cancel" value="Cancel"> |
| 816 | @ </form> |
| 817 | style_finish_page(); |
| 818 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -795,11 +795,11 @@ | |
| 795 | @ command. |
| 796 | @ </ol> |
| 797 | style_finish_page(); |
| 798 | return; |
| 799 | } |
| 800 | if( P("truncate1") && cgi_csrf_safe(2) ){ |
| 801 | fclose(fopen(g.zErrlog,"w")); |
| 802 | } |
| 803 | if( P("download") ){ |
| 804 | Blob log; |
| 805 | blob_read_from_file(&log, g.zErrlog, ExtFILE); |
| @@ -808,10 +808,11 @@ | |
| 808 | return; |
| 809 | } |
| 810 | szFile = file_size(g.zErrlog, ExtFILE); |
| 811 | if( P("truncate") ){ |
| 812 | @ <form action="%R/errorlog" method="POST"> |
| 813 | login_insert_csrf_secret(); |
| 814 | @ <p>Confirm that you want to truncate the %,lld(szFile)-byte error log: |
| 815 | @ <input type="submit" name="truncate1" value="Confirm"> |
| 816 | @ <input type="submit" name="cancel" value="Cancel"> |
| 817 | @ </form> |
| 818 | style_finish_page(); |
| 819 |
+12
-20
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -202,12 +202,11 @@ | ||
| 202 | 202 | if( zQ==0 && !disabled && P("submit") ){ |
| 203 | 203 | zQ = "off"; |
| 204 | 204 | } |
| 205 | 205 | if( zQ ){ |
| 206 | 206 | 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) ){ | |
| 209 | 208 | db_protect_only(PROTECT_NONE); |
| 210 | 209 | db_set(zVar/*works-like:"x"*/, iQ ? "1" : "0", 0); |
| 211 | 210 | db_protect_pop(); |
| 212 | 211 | setup_incr_cfgcnt(); |
| 213 | 212 | admin_log("Set option [%q] to [%q].", |
| @@ -237,13 +236,12 @@ | ||
| 237 | 236 | const char *zDflt, /* Default value if CONFIG table entry does not exist */ |
| 238 | 237 | int disabled /* 1 if disabled */ |
| 239 | 238 | ){ |
| 240 | 239 | const char *zVal = db_get(zVar, zDflt); |
| 241 | 240 | 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) ){ | |
| 243 | 242 | const int nZQ = (int)strlen(zQ); |
| 244 | - login_verify_csrf_secret(); | |
| 245 | 243 | setup_incr_cfgcnt(); |
| 246 | 244 | db_protect_only(PROTECT_NONE); |
| 247 | 245 | db_set(zVar/*works-like:"x"*/, zQ, 0); |
| 248 | 246 | db_protect_pop(); |
| 249 | 247 | admin_log("Set entry_attribute %Q to: %.*s%s", |
| @@ -270,13 +268,12 @@ | ||
| 270 | 268 | const char *zDflt, /* Default value if CONFIG table entry does not exist */ |
| 271 | 269 | int disabled /* 1 if the textarea should not be editable */ |
| 272 | 270 | ){ |
| 273 | 271 | const char *z = db_get(zVar, zDflt); |
| 274 | 272 | 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) ){ | |
| 276 | 274 | const int nZQ = (int)strlen(zQ); |
| 277 | - login_verify_csrf_secret(); | |
| 278 | 275 | db_protect_only(PROTECT_NONE); |
| 279 | 276 | db_set(zVar/*works-like:"x"*/, zQ, 0); |
| 280 | 277 | db_protect_pop(); |
| 281 | 278 | setup_incr_cfgcnt(); |
| 282 | 279 | admin_log("Set textarea_attribute %Q to: %.*s%s", |
| @@ -309,13 +306,12 @@ | ||
| 309 | 306 | const char *const *azChoice /* Choices in pairs (VAR value, Display) */ |
| 310 | 307 | ){ |
| 311 | 308 | const char *z = db_get(zVar, zDflt); |
| 312 | 309 | const char *zQ = P(zQP); |
| 313 | 310 | int i; |
| 314 | - if( zQ && fossil_strcmp(zQ,z)!=0){ | |
| 311 | + if( zQ && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){ | |
| 315 | 312 | const int nZQ = (int)strlen(zQ); |
| 316 | - login_verify_csrf_secret(); | |
| 317 | 313 | db_unprotect(PROTECT_ALL); |
| 318 | 314 | db_set(zVar/*works-like:"x"*/, zQ, 0); |
| 319 | 315 | setup_incr_cfgcnt(); |
| 320 | 316 | db_protect_pop(); |
| 321 | 317 | admin_log("Set multiple_choice_attribute %Q to: %.*s%s", |
| @@ -1458,11 +1454,11 @@ | ||
| 1458 | 1454 | if( !g.perm.Admin ){ |
| 1459 | 1455 | login_needed(0); |
| 1460 | 1456 | return; |
| 1461 | 1457 | } |
| 1462 | 1458 | db_begin_transaction(); |
| 1463 | - if( P("clear")!=0 && cgi_csrf_safe(1) ){ | |
| 1459 | + if( P("clear")!=0 && cgi_csrf_safe(2) ){ | |
| 1464 | 1460 | db_unprotect(PROTECT_CONFIG); |
| 1465 | 1461 | db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'"); |
| 1466 | 1462 | db_protect_pop(); |
| 1467 | 1463 | cgi_replace_parameter("adunit",""); |
| 1468 | 1464 | cgi_replace_parameter("adright",""); |
| @@ -1560,11 +1556,11 @@ | ||
| 1560 | 1556 | if( !g.perm.Admin ){ |
| 1561 | 1557 | login_needed(0); |
| 1562 | 1558 | return; |
| 1563 | 1559 | } |
| 1564 | 1560 | db_begin_transaction(); |
| 1565 | - if( !cgi_csrf_safe(1) ){ | |
| 1561 | + if( !cgi_csrf_safe(2) ){ | |
| 1566 | 1562 | /* Allow no state changes if not safe from CSRF */ |
| 1567 | 1563 | }else if( P("setlogo")!=0 && zLogoMime && zLogoMime[0] && szLogoImg>0 ){ |
| 1568 | 1564 | Blob img; |
| 1569 | 1565 | Stmt ins; |
| 1570 | 1566 | blob_init(&img, aLogoImg, szLogoImg); |
| @@ -1769,11 +1765,11 @@ | ||
| 1769 | 1765 | if( !g.perm.Setup ){ |
| 1770 | 1766 | login_needed(0); |
| 1771 | 1767 | return; |
| 1772 | 1768 | } |
| 1773 | 1769 | add_content_sql_commands(g.db); |
| 1774 | - zQ = cgi_csrf_safe(1) ? P("q") : 0; | |
| 1770 | + zQ = cgi_csrf_safe(2) ? P("q") : 0; | |
| 1775 | 1771 | style_set_current_feature("setup"); |
| 1776 | 1772 | style_header("Raw SQL Commands"); |
| 1777 | 1773 | @ <p><b>Caution:</b> There are no restrictions on the SQL that can be |
| 1778 | 1774 | @ run by this page. You can do serious and irrepairable damage to the |
| 1779 | 1775 | @ repository. Proceed with extreme caution.</p> |
| @@ -1822,19 +1818,18 @@ | ||
| 1822 | 1818 | go = 1; |
| 1823 | 1819 | }else if( P("tablelist") ){ |
| 1824 | 1820 | zQ = sqlite3_mprintf("SELECT*FROM pragma_table_list ORDER BY schema, name"); |
| 1825 | 1821 | go = 1; |
| 1826 | 1822 | } |
| 1827 | - if( go ){ | |
| 1823 | + if( go && cgi_csrf_safe(2) ){ | |
| 1828 | 1824 | sqlite3_stmt *pStmt; |
| 1829 | 1825 | int rc; |
| 1830 | 1826 | const char *zTail; |
| 1831 | 1827 | int nCol; |
| 1832 | 1828 | int nRow = 0; |
| 1833 | 1829 | int i; |
| 1834 | 1830 | @ <hr> |
| 1835 | - login_verify_csrf_secret(); | |
| 1836 | 1831 | sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0); |
| 1837 | 1832 | search_sql_setup(g.db); |
| 1838 | 1833 | rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail); |
| 1839 | 1834 | if( rc!=SQLITE_OK ){ |
| 1840 | 1835 | @ <div class="generalError">%h(sqlite3_errmsg(g.db))</div> |
| @@ -1913,22 +1908,20 @@ | ||
| 1913 | 1908 | style_header("Raw TH1 Commands"); |
| 1914 | 1909 | @ <p><b>Caution:</b> There are no restrictions on the TH1 that can be |
| 1915 | 1910 | @ run by this page. If Tcl integration was enabled at compile-time and |
| 1916 | 1911 | @ the "tcl" setting is enabled, Tcl commands may be run as well.</p> |
| 1917 | 1912 | @ |
| 1918 | - @ <form method="post" action="%R/admin_th1"> | |
| 1919 | - login_insert_csrf_secret(); | |
| 1913 | + form_begin(0, "%R/admin_th1"); | |
| 1920 | 1914 | @ TH1:<br> |
| 1921 | 1915 | @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br> |
| 1922 | 1916 | @ <input type="submit" name="go" value="Run TH1"> |
| 1923 | 1917 | @ </form> |
| 1924 | - if( go ){ | |
| 1918 | + if( go && cgi_csrf_safe(2) ){ | |
| 1925 | 1919 | const char *zR; |
| 1926 | 1920 | int rc; |
| 1927 | 1921 | int n; |
| 1928 | 1922 | @ <hr> |
| 1929 | - login_verify_csrf_secret(); | |
| 1930 | 1923 | rc = Th_Eval(g.interp, 0, zQ, -1); |
| 1931 | 1924 | zR = Th_GetResult(g.interp, &n); |
| 1932 | 1925 | if( rc==TH_OK ){ |
| 1933 | 1926 | @ <pre class="th1result">%h(zR)</pre> |
| 1934 | 1927 | }else{ |
| @@ -2129,11 +2122,11 @@ | ||
| 2129 | 2122 | Blob *pSql, |
| 2130 | 2123 | const char *zOldName, |
| 2131 | 2124 | const char *zNewName, |
| 2132 | 2125 | const char *zValue |
| 2133 | 2126 | ){ |
| 2134 | - if( !cgi_csrf_safe(1) ) return; | |
| 2127 | + if( !cgi_csrf_safe(2) ) return; | |
| 2135 | 2128 | if( zNewName[0]==0 || zValue[0]==0 ){ |
| 2136 | 2129 | if( zOldName[0] ){ |
| 2137 | 2130 | blob_append_sql(pSql, |
| 2138 | 2131 | "DELETE FROM config WHERE name='walias:%q';\n", |
| 2139 | 2132 | zOldName); |
| @@ -2173,17 +2166,16 @@ | ||
| 2173 | 2166 | login_needed(0); |
| 2174 | 2167 | return; |
| 2175 | 2168 | } |
| 2176 | 2169 | style_set_current_feature("setup"); |
| 2177 | 2170 | style_header("URL Alias Configuration"); |
| 2178 | - if( P("submit")!=0 ){ | |
| 2171 | + if( P("submit")!=0 && cgi_csrf_safe(2) ){ | |
| 2179 | 2172 | Blob token; |
| 2180 | 2173 | Blob sql; |
| 2181 | 2174 | const char *zNewName; |
| 2182 | 2175 | const char *zValue; |
| 2183 | 2176 | char zCnt[10]; |
| 2184 | - login_verify_csrf_secret(); | |
| 2185 | 2177 | blob_init(&namelist, PD("namelist",""), -1); |
| 2186 | 2178 | blob_init(&sql, 0, 0); |
| 2187 | 2179 | while( blob_token(&namelist, &token) ){ |
| 2188 | 2180 | const char *zOldName = blob_str(&token); |
| 2189 | 2181 | sqlite3_snprintf(sizeof(zCnt), zCnt, "n%d", cnt); |
| 2190 | 2182 |
| --- 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", |
| @@ -1458,11 +1454,11 @@ | |
| 1458 | if( !g.perm.Admin ){ |
| 1459 | login_needed(0); |
| 1460 | return; |
| 1461 | } |
| 1462 | db_begin_transaction(); |
| 1463 | if( P("clear")!=0 && cgi_csrf_safe(1) ){ |
| 1464 | db_unprotect(PROTECT_CONFIG); |
| 1465 | db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'"); |
| 1466 | db_protect_pop(); |
| 1467 | cgi_replace_parameter("adunit",""); |
| 1468 | cgi_replace_parameter("adright",""); |
| @@ -1560,11 +1556,11 @@ | |
| 1560 | if( !g.perm.Admin ){ |
| 1561 | login_needed(0); |
| 1562 | return; |
| 1563 | } |
| 1564 | db_begin_transaction(); |
| 1565 | if( !cgi_csrf_safe(1) ){ |
| 1566 | /* Allow no state changes if not safe from CSRF */ |
| 1567 | }else if( P("setlogo")!=0 && zLogoMime && zLogoMime[0] && szLogoImg>0 ){ |
| 1568 | Blob img; |
| 1569 | Stmt ins; |
| 1570 | blob_init(&img, aLogoImg, szLogoImg); |
| @@ -1769,11 +1765,11 @@ | |
| 1769 | if( !g.perm.Setup ){ |
| 1770 | login_needed(0); |
| 1771 | return; |
| 1772 | } |
| 1773 | add_content_sql_commands(g.db); |
| 1774 | zQ = cgi_csrf_safe(1) ? P("q") : 0; |
| 1775 | style_set_current_feature("setup"); |
| 1776 | style_header("Raw SQL Commands"); |
| 1777 | @ <p><b>Caution:</b> There are no restrictions on the SQL that can be |
| 1778 | @ run by this page. You can do serious and irrepairable damage to the |
| 1779 | @ repository. Proceed with extreme caution.</p> |
| @@ -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{ |
| @@ -2129,11 +2122,11 @@ | |
| 2129 | Blob *pSql, |
| 2130 | const char *zOldName, |
| 2131 | const char *zNewName, |
| 2132 | const char *zValue |
| 2133 | ){ |
| 2134 | if( !cgi_csrf_safe(1) ) return; |
| 2135 | if( zNewName[0]==0 || zValue[0]==0 ){ |
| 2136 | if( zOldName[0] ){ |
| 2137 | blob_append_sql(pSql, |
| 2138 | "DELETE FROM config WHERE name='walias:%q';\n", |
| 2139 | zOldName); |
| @@ -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", |
| @@ -1458,11 +1454,11 @@ | |
| 1454 | if( !g.perm.Admin ){ |
| 1455 | login_needed(0); |
| 1456 | return; |
| 1457 | } |
| 1458 | db_begin_transaction(); |
| 1459 | if( P("clear")!=0 && cgi_csrf_safe(2) ){ |
| 1460 | db_unprotect(PROTECT_CONFIG); |
| 1461 | db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'"); |
| 1462 | db_protect_pop(); |
| 1463 | cgi_replace_parameter("adunit",""); |
| 1464 | cgi_replace_parameter("adright",""); |
| @@ -1560,11 +1556,11 @@ | |
| 1556 | if( !g.perm.Admin ){ |
| 1557 | login_needed(0); |
| 1558 | return; |
| 1559 | } |
| 1560 | db_begin_transaction(); |
| 1561 | if( !cgi_csrf_safe(2) ){ |
| 1562 | /* Allow no state changes if not safe from CSRF */ |
| 1563 | }else if( P("setlogo")!=0 && zLogoMime && zLogoMime[0] && szLogoImg>0 ){ |
| 1564 | Blob img; |
| 1565 | Stmt ins; |
| 1566 | blob_init(&img, aLogoImg, szLogoImg); |
| @@ -1769,11 +1765,11 @@ | |
| 1765 | if( !g.perm.Setup ){ |
| 1766 | login_needed(0); |
| 1767 | return; |
| 1768 | } |
| 1769 | add_content_sql_commands(g.db); |
| 1770 | zQ = cgi_csrf_safe(2) ? P("q") : 0; |
| 1771 | style_set_current_feature("setup"); |
| 1772 | style_header("Raw SQL Commands"); |
| 1773 | @ <p><b>Caution:</b> There are no restrictions on the SQL that can be |
| 1774 | @ run by this page. You can do serious and irrepairable damage to the |
| 1775 | @ repository. Proceed with extreme caution.</p> |
| @@ -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{ |
| @@ -2129,11 +2122,11 @@ | |
| 2122 | Blob *pSql, |
| 2123 | const char *zOldName, |
| 2124 | const char *zNewName, |
| 2125 | const char *zValue |
| 2126 | ){ |
| 2127 | if( !cgi_csrf_safe(2) ) return; |
| 2128 | if( zNewName[0]==0 || zValue[0]==0 ){ |
| 2129 | if( zOldName[0] ){ |
| 2130 | blob_append_sql(pSql, |
| 2131 | "DELETE FROM config WHERE name='walias:%q';\n", |
| 2132 | zOldName); |
| @@ -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 |
+3
-3
| --- src/setupuser.c | ||
| +++ src/setupuser.c | ||
| @@ -342,11 +342,11 @@ | ||
| 342 | 342 | cgi_redirect(cgi_referer("setup_ulist")); |
| 343 | 343 | return; |
| 344 | 344 | } |
| 345 | 345 | |
| 346 | 346 | /* Check for requests to delete the user */ |
| 347 | - if( P("delete") && cgi_csrf_safe(1) ){ | |
| 347 | + if( P("delete") && cgi_csrf_safe(2) ){ | |
| 348 | 348 | int n; |
| 349 | 349 | if( P("verifydelete") ){ |
| 350 | 350 | /* Verified delete user request */ |
| 351 | 351 | db_unprotect(PROTECT_USER); |
| 352 | 352 | if( alert_tables_exist() ){ |
| @@ -386,11 +386,11 @@ | ||
| 386 | 386 | ** more are missing, no-op */ |
| 387 | 387 | }else if( higherUser ){ |
| 388 | 388 | /* An Admin (a) user cannot edit a Superuser (s) */ |
| 389 | 389 | }else if( zDeleteVerify!=0 ){ |
| 390 | 390 | /* Need to verify a delete request */ |
| 391 | - }else if( !cgi_csrf_safe(1) ){ | |
| 391 | + }else if( !cgi_csrf_safe(2) ){ | |
| 392 | 392 | /* This might be a cross-site request forgery, so ignore it */ |
| 393 | 393 | }else{ |
| 394 | 394 | /* We have all the information we need to make the change to the user */ |
| 395 | 395 | char c; |
| 396 | 396 | char zCap[70], zNm[4]; |
| @@ -440,11 +440,11 @@ | ||
| 440 | 440 | @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> |
| 441 | 441 | @ [Bummer]</a></p> |
| 442 | 442 | style_finish_page(); |
| 443 | 443 | return; |
| 444 | 444 | } |
| 445 | - login_verify_csrf_secret(); | |
| 445 | + cgi_csrf_verify(); | |
| 446 | 446 | db_unprotect(PROTECT_USER); |
| 447 | 447 | db_multi_exec( |
| 448 | 448 | "REPLACE INTO user(uid,login,info,pw,cap,mtime) " |
| 449 | 449 | "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())", |
| 450 | 450 | uid, zLogin, P("info"), zPw, zCap |
| 451 | 451 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -342,11 +342,11 @@ | |
| 342 | cgi_redirect(cgi_referer("setup_ulist")); |
| 343 | return; |
| 344 | } |
| 345 | |
| 346 | /* Check for requests to delete the user */ |
| 347 | if( P("delete") && cgi_csrf_safe(1) ){ |
| 348 | int n; |
| 349 | if( P("verifydelete") ){ |
| 350 | /* Verified delete user request */ |
| 351 | db_unprotect(PROTECT_USER); |
| 352 | if( alert_tables_exist() ){ |
| @@ -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 | |
| @@ -342,11 +342,11 @@ | |
| 342 | cgi_redirect(cgi_referer("setup_ulist")); |
| 343 | return; |
| 344 | } |
| 345 | |
| 346 | /* Check for requests to delete the user */ |
| 347 | if( P("delete") && cgi_csrf_safe(2) ){ |
| 348 | int n; |
| 349 | if( P("verifydelete") ){ |
| 350 | /* Verified delete user request */ |
| 351 | db_unprotect(PROTECT_USER); |
| 352 | if( alert_tables_exist() ){ |
| @@ -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 |
+13
| --- src/sha1.c | ||
| +++ src/sha1.c | ||
| @@ -392,10 +392,23 @@ | ||
| 392 | 392 | blob_resize(pCksum, 40); |
| 393 | 393 | SHA1Final(zResult, &ctx); |
| 394 | 394 | DigestToBase16(zResult, blob_buffer(pCksum)); |
| 395 | 395 | return 0; |
| 396 | 396 | } |
| 397 | + | |
| 398 | +/* | |
| 399 | +** Compute a binary SHA1 checksum of a zero-terminated string. The | |
| 400 | +** result is stored in zOut, which is a buffer that must be at least | |
| 401 | +** 20 bytes in size. | |
| 402 | +*/ | |
| 403 | +void sha1sum_binary(const char *zIn, unsigned char *zOut){ | |
| 404 | + SHA1Context ctx; | |
| 405 | + | |
| 406 | + SHA1Init(&ctx); | |
| 407 | + SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); | |
| 408 | + SHA1Final(zOut, &ctx); | |
| 409 | +} | |
| 397 | 410 | |
| 398 | 411 | /* |
| 399 | 412 | ** Compute the SHA1 checksum of a zero-terminated string. The |
| 400 | 413 | ** result is held in memory obtained from mprintf(). |
| 401 | 414 | */ |
| 402 | 415 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -392,10 +392,23 @@ | |
| 392 | blob_resize(pCksum, 40); |
| 393 | SHA1Final(zResult, &ctx); |
| 394 | DigestToBase16(zResult, blob_buffer(pCksum)); |
| 395 | return 0; |
| 396 | } |
| 397 | |
| 398 | /* |
| 399 | ** Compute the SHA1 checksum of a zero-terminated string. The |
| 400 | ** result is held in memory obtained from mprintf(). |
| 401 | */ |
| 402 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -392,10 +392,23 @@ | |
| 392 | blob_resize(pCksum, 40); |
| 393 | SHA1Final(zResult, &ctx); |
| 394 | DigestToBase16(zResult, blob_buffer(pCksum)); |
| 395 | return 0; |
| 396 | } |
| 397 | |
| 398 | /* |
| 399 | ** Compute a binary SHA1 checksum of a zero-terminated string. The |
| 400 | ** result is stored in zOut, which is a buffer that must be at least |
| 401 | ** 20 bytes in size. |
| 402 | */ |
| 403 | void sha1sum_binary(const char *zIn, unsigned char *zOut){ |
| 404 | SHA1Context ctx; |
| 405 | |
| 406 | SHA1Init(&ctx); |
| 407 | SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); |
| 408 | SHA1Final(zOut, &ctx); |
| 409 | } |
| 410 | |
| 411 | /* |
| 412 | ** Compute the SHA1 checksum of a zero-terminated string. The |
| 413 | ** result is held in memory obtained from mprintf(). |
| 414 | */ |
| 415 |
+2
-4
| --- src/shun.c | ||
| +++ src/shun.c | ||
| @@ -98,14 +98,13 @@ | ||
| 98 | 98 | } |
| 99 | 99 | } |
| 100 | 100 | zUuid = zCanonical; |
| 101 | 101 | } |
| 102 | 102 | style_header("Shunned Artifacts"); |
| 103 | - if( zUuid && P("sub") ){ | |
| 103 | + if( zUuid && P("sub") && cgi_csrf_safe(2) ){ | |
| 104 | 104 | const char *p = zUuid; |
| 105 | 105 | int allExist = 1; |
| 106 | - login_verify_csrf_secret(); | |
| 107 | 106 | while( *p ){ |
| 108 | 107 | db_multi_exec("DELETE FROM shun WHERE uuid=%Q", p); |
| 109 | 108 | if( !db_exists("SELECT 1 FROM blob WHERE uuid=%Q", p) ){ |
| 110 | 109 | allExist = 0; |
| 111 | 110 | } |
| @@ -127,14 +126,13 @@ | ||
| 127 | 126 | @ It may be necessary to rebuild the repository using the |
| 128 | 127 | @ <b>fossil rebuild</b> command-line before the artifact content |
| 129 | 128 | @ can pulled in from other repositories.</p> |
| 130 | 129 | } |
| 131 | 130 | } |
| 132 | - if( zUuid && P("add") ){ | |
| 131 | + if( zUuid && P("add") && cgi_csrf_safe(2) ){ | |
| 133 | 132 | const char *p = zUuid; |
| 134 | 133 | int rid, tagid; |
| 135 | - login_verify_csrf_secret(); | |
| 136 | 134 | while( *p ){ |
| 137 | 135 | db_multi_exec( |
| 138 | 136 | "INSERT OR IGNORE INTO shun(uuid,mtime)" |
| 139 | 137 | " VALUES(%Q, now())", p); |
| 140 | 138 | db_multi_exec("DELETE FROM attachment WHERE src=%Q", p); |
| 141 | 139 |
| --- 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 |
+10
-3
| --- src/skins.c | ||
| +++ src/skins.c | ||
| @@ -531,11 +531,11 @@ | ||
| 531 | 531 | aBuiltinSkin[i].zSQL = getSkin(aBuiltinSkin[i].zLabel); |
| 532 | 532 | } |
| 533 | 533 | |
| 534 | 534 | style_set_current_feature("skins"); |
| 535 | 535 | |
| 536 | - if( cgi_csrf_safe(1) ){ | |
| 536 | + if( cgi_csrf_safe(2) ){ | |
| 537 | 537 | /* Process requests to delete a user-defined skin */ |
| 538 | 538 | if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 539 | 539 | style_header("Confirm Custom Skin Delete"); |
| 540 | 540 | @ <form action="%R/setup_skin_admin" method="post"><div> |
| 541 | 541 | @ <p>Deletion of a custom skin is a permanent action that cannot |
| @@ -628,10 +628,11 @@ | ||
| 628 | 628 | seenCurrent = 1; |
| 629 | 629 | }else{ |
| 630 | 630 | @ <form action="%R/setup_skin_admin" method="post"> |
| 631 | 631 | @ <input type="hidden" name="sn" value="%h(z)"> |
| 632 | 632 | @ <input type="submit" name="load" value="Install"> |
| 633 | + login_insert_csrf_secret(); | |
| 633 | 634 | if( pAltSkin==&aBuiltinSkin[i] ){ |
| 634 | 635 | @ (Current override) |
| 635 | 636 | } |
| 636 | 637 | @ </form> |
| 637 | 638 | } |
| @@ -652,10 +653,11 @@ | ||
| 652 | 653 | @ <tr><td colspan=4><h2>Skins saved as "skin:*' entries \ |
| 653 | 654 | @ in the CONFIG table:</h2></td></tr> |
| 654 | 655 | } |
| 655 | 656 | @ <tr><td>%d(i).<td>%h(zN)<td> <td> |
| 656 | 657 | @ <form action="%R/setup_skin_admin" method="post"> |
| 658 | + login_insert_csrf_secret(); | |
| 657 | 659 | if( fossil_strcmp(zV, zCurrent)==0 ){ |
| 658 | 660 | @ (Currently In Use) |
| 659 | 661 | seenCurrent = 1; |
| 660 | 662 | }else{ |
| 661 | 663 | @ <input type="submit" name="load" value="Install"> |
| @@ -671,10 +673,11 @@ | ||
| 671 | 673 | @ <tr><td colspan=4><h2>Current skin in css/header/footer/details entries \ |
| 672 | 674 | @ in the CONFIG table:</h2></td></tr> |
| 673 | 675 | @ <tr><td>%d(i).<td><i>Current</i><td> <td> |
| 674 | 676 | @ <form action="%R/setup_skin_admin" method="post"> |
| 675 | 677 | @ <input type="submit" name="save" value="Backup"> |
| 678 | + login_insert_csrf_secret(); | |
| 676 | 679 | @ </form> |
| 677 | 680 | } |
| 678 | 681 | db_prepare(&q, |
| 679 | 682 | "SELECT DISTINCT substr(name, 1, 6) FROM config" |
| 680 | 683 | " WHERE name GLOB 'draft[1-9]-*'" |
| @@ -689,10 +692,11 @@ | ||
| 689 | 692 | @ <tr><td colspan=4><h2>Draft skins stored as "draft[1-9]-*' entries \ |
| 690 | 693 | @ in the CONFIG table:</h2></td></tr> |
| 691 | 694 | } |
| 692 | 695 | @ <tr><td>%d(i).<td>%h(zN)<td> <td> |
| 693 | 696 | @ <form action="%R/setup_skin_admin" method="post"> |
| 697 | + login_insert_csrf_secret(); | |
| 694 | 698 | @ <input type="submit" name="draftdel" value="Delete"> |
| 695 | 699 | @ <input type="hidden" name="name" value="%h(zN)"> |
| 696 | 700 | @ </form></tr> |
| 697 | 701 | } |
| 698 | 702 | db_finalize(&q); |
| @@ -836,11 +840,11 @@ | ||
| 836 | 840 | zTitle = mprintf("%s for Draft%d", aSkinAttr[ii].zTitle, iSkin); |
| 837 | 841 | zBasis = PD("basis","current"); |
| 838 | 842 | zDflt = skin_file_content(zBasis, zFile); |
| 839 | 843 | zOrig = db_get_mprintf(zDflt, "draft%d-%s",iSkin,zFile); |
| 840 | 844 | zContent = PD(zFile,zOrig); |
| 841 | - if( P("revert")!=0 && cgi_csrf_safe(0) ){ | |
| 845 | + if( P("revert")!=0 && cgi_csrf_safe(2) ){ | |
| 842 | 846 | zContent = zDflt; |
| 843 | 847 | isRevert = 1; |
| 844 | 848 | } |
| 845 | 849 | |
| 846 | 850 | db_begin_transaction(); |
| @@ -853,11 +857,13 @@ | ||
| 853 | 857 | @ <form action="%R/setup_skinedit" method="post"><div> |
| 854 | 858 | login_insert_csrf_secret(); |
| 855 | 859 | @ <input type='hidden' name='w' value='%d(ii)'> |
| 856 | 860 | @ <input type='hidden' name='sk' value='%d(iSkin)'> |
| 857 | 861 | @ <h2>Edit %s(zTitle):</h2> |
| 858 | - if( P("submit") && cgi_csrf_safe(0) && (zOrig==0 || strcmp(zOrig,zContent)!=0) ){ | |
| 862 | + if( P("submit") && cgi_csrf_safe(2) | |
| 863 | + && (zOrig==0 || strcmp(zOrig,zContent)!=0) | |
| 864 | + ){ | |
| 859 | 865 | db_set_mprintf(zContent, 0, "draft%d-%s",iSkin,zFile); |
| 860 | 866 | } |
| 861 | 867 | @ <textarea name="%s(zFile)" rows="10" cols="80">\ |
| 862 | 868 | @ %h(zContent)</textarea> |
| 863 | 869 | @ <br> |
| @@ -1042,10 +1048,11 @@ | ||
| 1042 | 1048 | @ <option value='%d(i)'>draft%d(i)</option> |
| 1043 | 1049 | } |
| 1044 | 1050 | } |
| 1045 | 1051 | @ </select> |
| 1046 | 1052 | @ </p> |
| 1053 | + @ </form> | |
| 1047 | 1054 | @ |
| 1048 | 1055 | @ <a name='step2'></a> |
| 1049 | 1056 | @ <h1>Step 2: Authenticate</h1> |
| 1050 | 1057 | @ |
| 1051 | 1058 | if( isSetup ){ |
| 1052 | 1059 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -531,11 +531,11 @@ | |
| 531 | aBuiltinSkin[i].zSQL = getSkin(aBuiltinSkin[i].zLabel); |
| 532 | } |
| 533 | |
| 534 | style_set_current_feature("skins"); |
| 535 | |
| 536 | if( cgi_csrf_safe(1) ){ |
| 537 | /* Process requests to delete a user-defined skin */ |
| 538 | if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 539 | style_header("Confirm Custom Skin Delete"); |
| 540 | @ <form action="%R/setup_skin_admin" method="post"><div> |
| 541 | @ <p>Deletion of a custom skin is a permanent action that cannot |
| @@ -628,10 +628,11 @@ | |
| 628 | seenCurrent = 1; |
| 629 | }else{ |
| 630 | @ <form action="%R/setup_skin_admin" method="post"> |
| 631 | @ <input type="hidden" name="sn" value="%h(z)"> |
| 632 | @ <input type="submit" name="load" value="Install"> |
| 633 | if( pAltSkin==&aBuiltinSkin[i] ){ |
| 634 | @ (Current override) |
| 635 | } |
| 636 | @ </form> |
| 637 | } |
| @@ -652,10 +653,11 @@ | |
| 652 | @ <tr><td colspan=4><h2>Skins saved as "skin:*' entries \ |
| 653 | @ in the CONFIG table:</h2></td></tr> |
| 654 | } |
| 655 | @ <tr><td>%d(i).<td>%h(zN)<td> <td> |
| 656 | @ <form action="%R/setup_skin_admin" method="post"> |
| 657 | if( fossil_strcmp(zV, zCurrent)==0 ){ |
| 658 | @ (Currently In Use) |
| 659 | seenCurrent = 1; |
| 660 | }else{ |
| 661 | @ <input type="submit" name="load" value="Install"> |
| @@ -671,10 +673,11 @@ | |
| 671 | @ <tr><td colspan=4><h2>Current skin in css/header/footer/details entries \ |
| 672 | @ in the CONFIG table:</h2></td></tr> |
| 673 | @ <tr><td>%d(i).<td><i>Current</i><td> <td> |
| 674 | @ <form action="%R/setup_skin_admin" method="post"> |
| 675 | @ <input type="submit" name="save" value="Backup"> |
| 676 | @ </form> |
| 677 | } |
| 678 | db_prepare(&q, |
| 679 | "SELECT DISTINCT substr(name, 1, 6) FROM config" |
| 680 | " WHERE name GLOB 'draft[1-9]-*'" |
| @@ -689,10 +692,11 @@ | |
| 689 | @ <tr><td colspan=4><h2>Draft skins stored as "draft[1-9]-*' entries \ |
| 690 | @ in the CONFIG table:</h2></td></tr> |
| 691 | } |
| 692 | @ <tr><td>%d(i).<td>%h(zN)<td> <td> |
| 693 | @ <form action="%R/setup_skin_admin" method="post"> |
| 694 | @ <input type="submit" name="draftdel" value="Delete"> |
| 695 | @ <input type="hidden" name="name" value="%h(zN)"> |
| 696 | @ </form></tr> |
| 697 | } |
| 698 | db_finalize(&q); |
| @@ -836,11 +840,11 @@ | |
| 836 | zTitle = mprintf("%s for Draft%d", aSkinAttr[ii].zTitle, iSkin); |
| 837 | zBasis = PD("basis","current"); |
| 838 | zDflt = skin_file_content(zBasis, zFile); |
| 839 | zOrig = db_get_mprintf(zDflt, "draft%d-%s",iSkin,zFile); |
| 840 | zContent = PD(zFile,zOrig); |
| 841 | if( P("revert")!=0 && cgi_csrf_safe(0) ){ |
| 842 | zContent = zDflt; |
| 843 | isRevert = 1; |
| 844 | } |
| 845 | |
| 846 | db_begin_transaction(); |
| @@ -853,11 +857,13 @@ | |
| 853 | @ <form action="%R/setup_skinedit" method="post"><div> |
| 854 | login_insert_csrf_secret(); |
| 855 | @ <input type='hidden' name='w' value='%d(ii)'> |
| 856 | @ <input type='hidden' name='sk' value='%d(iSkin)'> |
| 857 | @ <h2>Edit %s(zTitle):</h2> |
| 858 | if( P("submit") && cgi_csrf_safe(0) && (zOrig==0 || strcmp(zOrig,zContent)!=0) ){ |
| 859 | db_set_mprintf(zContent, 0, "draft%d-%s",iSkin,zFile); |
| 860 | } |
| 861 | @ <textarea name="%s(zFile)" rows="10" cols="80">\ |
| 862 | @ %h(zContent)</textarea> |
| 863 | @ <br> |
| @@ -1042,10 +1048,11 @@ | |
| 1042 | @ <option value='%d(i)'>draft%d(i)</option> |
| 1043 | } |
| 1044 | } |
| 1045 | @ </select> |
| 1046 | @ </p> |
| 1047 | @ |
| 1048 | @ <a name='step2'></a> |
| 1049 | @ <h1>Step 2: Authenticate</h1> |
| 1050 | @ |
| 1051 | if( isSetup ){ |
| 1052 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -531,11 +531,11 @@ | |
| 531 | aBuiltinSkin[i].zSQL = getSkin(aBuiltinSkin[i].zLabel); |
| 532 | } |
| 533 | |
| 534 | style_set_current_feature("skins"); |
| 535 | |
| 536 | if( cgi_csrf_safe(2) ){ |
| 537 | /* Process requests to delete a user-defined skin */ |
| 538 | if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 539 | style_header("Confirm Custom Skin Delete"); |
| 540 | @ <form action="%R/setup_skin_admin" method="post"><div> |
| 541 | @ <p>Deletion of a custom skin is a permanent action that cannot |
| @@ -628,10 +628,11 @@ | |
| 628 | seenCurrent = 1; |
| 629 | }else{ |
| 630 | @ <form action="%R/setup_skin_admin" method="post"> |
| 631 | @ <input type="hidden" name="sn" value="%h(z)"> |
| 632 | @ <input type="submit" name="load" value="Install"> |
| 633 | login_insert_csrf_secret(); |
| 634 | if( pAltSkin==&aBuiltinSkin[i] ){ |
| 635 | @ (Current override) |
| 636 | } |
| 637 | @ </form> |
| 638 | } |
| @@ -652,10 +653,11 @@ | |
| 653 | @ <tr><td colspan=4><h2>Skins saved as "skin:*' entries \ |
| 654 | @ in the CONFIG table:</h2></td></tr> |
| 655 | } |
| 656 | @ <tr><td>%d(i).<td>%h(zN)<td> <td> |
| 657 | @ <form action="%R/setup_skin_admin" method="post"> |
| 658 | login_insert_csrf_secret(); |
| 659 | if( fossil_strcmp(zV, zCurrent)==0 ){ |
| 660 | @ (Currently In Use) |
| 661 | seenCurrent = 1; |
| 662 | }else{ |
| 663 | @ <input type="submit" name="load" value="Install"> |
| @@ -671,10 +673,11 @@ | |
| 673 | @ <tr><td colspan=4><h2>Current skin in css/header/footer/details entries \ |
| 674 | @ in the CONFIG table:</h2></td></tr> |
| 675 | @ <tr><td>%d(i).<td><i>Current</i><td> <td> |
| 676 | @ <form action="%R/setup_skin_admin" method="post"> |
| 677 | @ <input type="submit" name="save" value="Backup"> |
| 678 | login_insert_csrf_secret(); |
| 679 | @ </form> |
| 680 | } |
| 681 | db_prepare(&q, |
| 682 | "SELECT DISTINCT substr(name, 1, 6) FROM config" |
| 683 | " WHERE name GLOB 'draft[1-9]-*'" |
| @@ -689,10 +692,11 @@ | |
| 692 | @ <tr><td colspan=4><h2>Draft skins stored as "draft[1-9]-*' entries \ |
| 693 | @ in the CONFIG table:</h2></td></tr> |
| 694 | } |
| 695 | @ <tr><td>%d(i).<td>%h(zN)<td> <td> |
| 696 | @ <form action="%R/setup_skin_admin" method="post"> |
| 697 | login_insert_csrf_secret(); |
| 698 | @ <input type="submit" name="draftdel" value="Delete"> |
| 699 | @ <input type="hidden" name="name" value="%h(zN)"> |
| 700 | @ </form></tr> |
| 701 | } |
| 702 | db_finalize(&q); |
| @@ -836,11 +840,11 @@ | |
| 840 | zTitle = mprintf("%s for Draft%d", aSkinAttr[ii].zTitle, iSkin); |
| 841 | zBasis = PD("basis","current"); |
| 842 | zDflt = skin_file_content(zBasis, zFile); |
| 843 | zOrig = db_get_mprintf(zDflt, "draft%d-%s",iSkin,zFile); |
| 844 | zContent = PD(zFile,zOrig); |
| 845 | if( P("revert")!=0 && cgi_csrf_safe(2) ){ |
| 846 | zContent = zDflt; |
| 847 | isRevert = 1; |
| 848 | } |
| 849 | |
| 850 | db_begin_transaction(); |
| @@ -853,11 +857,13 @@ | |
| 857 | @ <form action="%R/setup_skinedit" method="post"><div> |
| 858 | login_insert_csrf_secret(); |
| 859 | @ <input type='hidden' name='w' value='%d(ii)'> |
| 860 | @ <input type='hidden' name='sk' value='%d(iSkin)'> |
| 861 | @ <h2>Edit %s(zTitle):</h2> |
| 862 | if( P("submit") && cgi_csrf_safe(2) |
| 863 | && (zOrig==0 || strcmp(zOrig,zContent)!=0) |
| 864 | ){ |
| 865 | db_set_mprintf(zContent, 0, "draft%d-%s",iSkin,zFile); |
| 866 | } |
| 867 | @ <textarea name="%s(zFile)" rows="10" cols="80">\ |
| 868 | @ %h(zContent)</textarea> |
| 869 | @ <br> |
| @@ -1042,10 +1048,11 @@ | |
| 1048 | @ <option value='%d(i)'>draft%d(i)</option> |
| 1049 | } |
| 1050 | } |
| 1051 | @ </select> |
| 1052 | @ </p> |
| 1053 | @ </form> |
| 1054 | @ |
| 1055 | @ <a name='step2'></a> |
| 1056 | @ <h1>Step 2: Authenticate</h1> |
| 1057 | @ |
| 1058 | if( isSetup ){ |
| 1059 |
+21
-1
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -259,10 +259,11 @@ | ||
| 259 | 259 | }else{ |
| 260 | 260 | needHrefJs = 1; |
| 261 | 261 | @ <form method="POST" data-action='%s(zLink)' action='%R/login' \ |
| 262 | 262 | @ %s(zOtherArgs)> |
| 263 | 263 | } |
| 264 | + login_insert_csrf_secret(); | |
| 264 | 265 | } |
| 265 | 266 | |
| 266 | 267 | /* |
| 267 | 268 | ** Add a new element to the submenu |
| 268 | 269 | */ |
| @@ -1459,11 +1460,30 @@ | ||
| 1459 | 1460 | @ g.zRepositoryName = %h(g.zRepositoryName)<br> |
| 1460 | 1461 | @ load_average() = %f(load_average())<br> |
| 1461 | 1462 | #ifndef _WIN32 |
| 1462 | 1463 | @ RSS = %.2f(fossil_rss()/1000000.0) MB</br> |
| 1463 | 1464 | #endif |
| 1464 | - @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br> | |
| 1465 | + (void)cgi_csrf_safe(2); | |
| 1466 | + switch( g.okCsrf ){ | |
| 1467 | + case 1: { | |
| 1468 | + @ CSRF safety = Same origin<br> | |
| 1469 | + break; | |
| 1470 | + } | |
| 1471 | + case 2: { | |
| 1472 | + @ CSRF safety = Same origin, POST<br> | |
| 1473 | + break; | |
| 1474 | + } | |
| 1475 | + case 3: { | |
| 1476 | + @ CSRF safety = Same origin, POST, CSRF token<br> | |
| 1477 | + break; | |
| 1478 | + } | |
| 1479 | + default: { | |
| 1480 | + @ CSRF safety = unsafe<br> | |
| 1481 | + break; | |
| 1482 | + } | |
| 1483 | + } | |
| 1484 | + | |
| 1465 | 1485 | @ fossil_exe_id() = %h(fossil_exe_id())<br> |
| 1466 | 1486 | if( g.perm.Admin ){ |
| 1467 | 1487 | int k; |
| 1468 | 1488 | for(k=0; g.argvOrig[k]; k++){ |
| 1469 | 1489 | Blob t; |
| 1470 | 1490 |
| --- 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 | */ |
| @@ -1459,11 +1460,30 @@ | |
| 1459 | @ g.zRepositoryName = %h(g.zRepositoryName)<br> |
| 1460 | @ load_average() = %f(load_average())<br> |
| 1461 | #ifndef _WIN32 |
| 1462 | @ RSS = %.2f(fossil_rss()/1000000.0) MB</br> |
| 1463 | #endif |
| 1464 | @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br> |
| 1465 | @ fossil_exe_id() = %h(fossil_exe_id())<br> |
| 1466 | if( g.perm.Admin ){ |
| 1467 | int k; |
| 1468 | for(k=0; g.argvOrig[k]; k++){ |
| 1469 | Blob t; |
| 1470 |
| --- 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 | */ |
| @@ -1459,11 +1460,30 @@ | |
| 1460 | @ g.zRepositoryName = %h(g.zRepositoryName)<br> |
| 1461 | @ load_average() = %f(load_average())<br> |
| 1462 | #ifndef _WIN32 |
| 1463 | @ RSS = %.2f(fossil_rss()/1000000.0) MB</br> |
| 1464 | #endif |
| 1465 | (void)cgi_csrf_safe(2); |
| 1466 | switch( g.okCsrf ){ |
| 1467 | case 1: { |
| 1468 | @ CSRF safety = Same origin<br> |
| 1469 | break; |
| 1470 | } |
| 1471 | case 2: { |
| 1472 | @ CSRF safety = Same origin, POST<br> |
| 1473 | break; |
| 1474 | } |
| 1475 | case 3: { |
| 1476 | @ CSRF safety = Same origin, POST, CSRF token<br> |
| 1477 | break; |
| 1478 | } |
| 1479 | default: { |
| 1480 | @ CSRF safety = unsafe<br> |
| 1481 | break; |
| 1482 | } |
| 1483 | } |
| 1484 | |
| 1485 | @ fossil_exe_id() = %h(fossil_exe_id())<br> |
| 1486 | if( g.perm.Admin ){ |
| 1487 | int k; |
| 1488 | for(k=0; g.argvOrig[k]; k++){ |
| 1489 | Blob t; |
| 1490 |
+3
-1
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -605,11 +605,13 @@ | ||
| 605 | 605 | int *argl |
| 606 | 606 | ){ |
| 607 | 607 | if( argc!=1 ){ |
| 608 | 608 | return Th_WrongNumArgs(interp, "verifyCsrf"); |
| 609 | 609 | } |
| 610 | - login_verify_csrf_secret(); | |
| 610 | + if( !cgi_csrf_safe(2) ){ | |
| 611 | + fossil_fatal("possible CSRF attack"); | |
| 612 | + } | |
| 611 | 613 | return TH_OK; |
| 612 | 614 | } |
| 613 | 615 | |
| 614 | 616 | /* |
| 615 | 617 | ** TH1 command: verifyLogin |
| 616 | 618 |
| --- 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 @@ | ||
| 900 | 900 | int i; |
| 901 | 901 | int nJ = 0, rc = TH_OK; |
| 902 | 902 | Blob tktchng, cksum; |
| 903 | 903 | int needMod; |
| 904 | 904 | |
| 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 | + } | |
| 906 | 909 | if( !captcha_is_correct(0) ){ |
| 907 | 910 | @ <p class="generalError">Error: Incorrect security code.</p> |
| 908 | 911 | return TH_OK; |
| 909 | 912 | } |
| 910 | 913 | zUuid = (const char *)pUuid; |
| @@ -1015,11 +1018,10 @@ | ||
| 1015 | 1018 | initializeVariablesFromCGI(); |
| 1016 | 1019 | getAllTicketFields(); |
| 1017 | 1020 | initializeVariablesFromDb(); |
| 1018 | 1021 | if( g.zPath[0]=='d' ) showAllFields(); |
| 1019 | 1022 | form_begin(0, "%R/%s", g.zPath); |
| 1020 | - login_insert_csrf_secret(); | |
| 1021 | 1023 | if( P("date_override") && g.perm.Setup ){ |
| 1022 | 1024 | @ <input type="hidden" name="date_override" value="%h(P("date_override"))"> |
| 1023 | 1025 | } |
| 1024 | 1026 | zScript = ticket_newpage_code(); |
| 1025 | 1027 | if( g.zLogin && g.zLogin[0] ){ |
| @@ -1105,11 +1107,10 @@ | ||
| 1105 | 1107 | initializeVariablesFromCGI(); |
| 1106 | 1108 | initializeVariablesFromDb(); |
| 1107 | 1109 | if( g.zPath[0]=='d' ) showAllFields(); |
| 1108 | 1110 | form_begin(0, "%R/%s", g.zPath); |
| 1109 | 1111 | @ <input type="hidden" name="name" value="%s(zName)"> |
| 1110 | - login_insert_csrf_secret(); | |
| 1111 | 1112 | zScript = ticket_editpage_code(); |
| 1112 | 1113 | Th_Store("login", login_name()); |
| 1113 | 1114 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1114 | 1115 | Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); |
| 1115 | 1116 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); |
| 1116 | 1117 |
| --- 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 @@ | ||
| 135 | 135 | if( z==0 ){ |
| 136 | 136 | z = db_get(zDbField, zDfltValue); |
| 137 | 137 | } |
| 138 | 138 | style_set_current_feature("tktsetup"); |
| 139 | 139 | style_header("Edit %s", zTitle); |
| 140 | - if( P("clear")!=0 ){ | |
| 141 | - login_verify_csrf_secret(); | |
| 140 | + if( P("clear")!=0 && cgi_csrf_safe(2) ){ | |
| 142 | 141 | db_unset(zDbField/*works-like:"x"*/, 0); |
| 143 | 142 | if( xRebuild ) xRebuild(); |
| 144 | 143 | cgi_redirect("tktsetup"); |
| 145 | - }else if( isSubmit ){ | |
| 144 | + }else if( isSubmit && cgi_csrf_safe(2) ){ | |
| 146 | 145 | char *zErr = 0; |
| 147 | - login_verify_csrf_secret(); | |
| 148 | 146 | if( xText && (zErr = xText(z))!=0 ){ |
| 149 | 147 | @ <p class="tktsetupError">ERROR: %h(zErr)</p> |
| 150 | 148 | }else{ |
| 151 | 149 | db_set(zDbField/*works-like:"x"*/, z, 0); |
| 152 | 150 | if( xRebuild ) xRebuild(); |
| 153 | 151 |
| --- 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 @@ | ||
| 1632 | 1632 | zMimetype = wiki_filter_mimetypes(pWiki->zMimetype) |
| 1633 | 1633 | /* see https://fossil-scm.org/forum/forumpost/0acfdaac80 */; |
| 1634 | 1634 | } |
| 1635 | 1635 | if( !isSandbox && P("submit")!=0 && P("r")!=0 && P("u")!=0 |
| 1636 | 1636 | && (goodCaptcha = captcha_is_correct(0)) |
| 1637 | + && cgi_csrf_safe(2) | |
| 1637 | 1638 | ){ |
| 1638 | 1639 | char *zDate; |
| 1639 | 1640 | Blob cksum; |
| 1640 | 1641 | Blob body; |
| 1641 | 1642 | Blob wiki; |
| 1642 | 1643 | |
| 1643 | 1644 | blob_zero(&body); |
| 1644 | - login_verify_csrf_secret(); | |
| 1645 | 1645 | blob_append(&body, pWiki->zWiki, -1); |
| 1646 | 1646 | blob_zero(&wiki); |
| 1647 | 1647 | db_begin_transaction(); |
| 1648 | 1648 | zDate = date_in_standard_format("now"); |
| 1649 | 1649 | blob_appendf(&wiki, "D %s\n", zDate); |
| @@ -1695,11 +1695,10 @@ | ||
| 1695 | 1695 | @ <hr> |
| 1696 | 1696 | blob_reset(&preview); |
| 1697 | 1697 | } |
| 1698 | 1698 | zUser = PD("u", g.zLogin); |
| 1699 | 1699 | form_begin(0, "%R/wikiappend"); |
| 1700 | - login_insert_csrf_secret(); | |
| 1701 | 1700 | @ <input type="hidden" name="name" value="%h(zPageName)"> |
| 1702 | 1701 | @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> |
| 1703 | 1702 | @ Your Name: |
| 1704 | 1703 | @ <input type="text" name="u" size="20" value="%h(zUser)"><br> |
| 1705 | 1704 | zFormat = mimetype_common_name(zMimetype); |
| 1706 | 1705 |
| --- 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 @@ | ||
| 118 | 118 | if( z==0 ){ |
| 119 | 119 | z = db_get(zDbField, zDfltValue); |
| 120 | 120 | } |
| 121 | 121 | style_set_current_feature("xfersetup"); |
| 122 | 122 | style_header("Edit %s", zTitle); |
| 123 | - if( P("clear")!=0 ){ | |
| 124 | - login_verify_csrf_secret(); | |
| 123 | + if( P("clear")!=0 && cgi_csrf_safe(2) ){ | |
| 125 | 124 | db_unset(zDbField/*works-like:"x"*/, 0); |
| 126 | 125 | if( xRebuild ) xRebuild(); |
| 127 | 126 | z = zDfltValue; |
| 128 | - }else if( isSubmit ){ | |
| 127 | + }else if( isSubmit && cgi_csrf_safe(2) ){ | |
| 129 | 128 | char *zErr = 0; |
| 130 | - login_verify_csrf_secret(); | |
| 131 | 129 | if( xText && (zErr = xText(z))!=0 ){ |
| 132 | 130 | @ <p class="xfersetupError">ERROR: %h(zErr)</p> |
| 133 | 131 | }else{ |
| 134 | 132 | db_set(zDbField/*works-like:"x"*/, z, 0); |
| 135 | 133 | if( xRebuild ) xRebuild(); |
| 136 | 134 |
| --- 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 |