Fossil SCM
Eliminate IP prefix tracking as part of the login cookie.
Commit
7d18c40b8339eed6db748459e649ec731859406b7b213b21d3a0bc65893027ef
Parent
6ec931a13db1b0f…
2 files changed
+20
-65
-12
+20
-65
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -111,36 +111,10 @@ | ||
| 111 | 111 | }else{ |
| 112 | 112 | fossil_redirect_home(); |
| 113 | 113 | } |
| 114 | 114 | } |
| 115 | 115 | |
| 116 | -/* | |
| 117 | -** The IP address of the client is stored as part of login cookies. | |
| 118 | -** But some clients are behind firewalls that shift the IP address | |
| 119 | -** with each HTTP request. To allow such (broken) clients to log in, | |
| 120 | -** extract just a prefix of the IP address. | |
| 121 | -*/ | |
| 122 | -static char *ipPrefix(const char *zIP){ | |
| 123 | - int i, j; | |
| 124 | - static int ip_prefix_terms = -1; | |
| 125 | - if( ip_prefix_terms<0 ){ | |
| 126 | - if( db_get_int("redirect-to-https",0)>=2 ){ | |
| 127 | - ip_prefix_terms = 0; | |
| 128 | - }else{ | |
| 129 | - ip_prefix_terms = db_get_int("ip-prefix-terms",2); | |
| 130 | - } | |
| 131 | - } | |
| 132 | - if( ip_prefix_terms==0 ) return mprintf("0"); | |
| 133 | - for(i=j=0; zIP[i]; i++){ | |
| 134 | - if( zIP[i]=='.' ){ | |
| 135 | - j++; | |
| 136 | - if( j==ip_prefix_terms ) break; | |
| 137 | - } | |
| 138 | - } | |
| 139 | - return mprintf("%.*s", i, zIP); | |
| 140 | -} | |
| 141 | - | |
| 142 | 116 | /* |
| 143 | 117 | ** Return an abbreviated project code. The abbreviation is the first |
| 144 | 118 | ** 16 characters of the project code. |
| 145 | 119 | ** |
| 146 | 120 | ** Memory is obtained from malloc. |
| @@ -299,30 +273,27 @@ | ||
| 299 | 273 | const char *zExpire = db_get("cookie-expire","8766"); |
| 300 | 274 | int expires = atoi(zExpire)*3600; |
| 301 | 275 | char *zHash; |
| 302 | 276 | char *zCookie; |
| 303 | 277 | const char *zIpAddr = PD("REMOTE_ADDR","nil"); /* IP address of user */ |
| 304 | - char *zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */ | |
| 305 | 278 | |
| 306 | 279 | assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data."); |
| 307 | 280 | zHash = db_text(0, |
| 308 | 281 | "SELECT cookie FROM user" |
| 309 | 282 | " WHERE uid=%d" |
| 310 | - " AND ipaddr=%Q" | |
| 311 | 283 | " AND cexpire>julianday('now')" |
| 312 | 284 | " AND length(cookie)>30", |
| 313 | - uid, zRemoteAddr); | |
| 285 | + uid); | |
| 314 | 286 | if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))"); |
| 315 | 287 | zCookie = login_gen_user_cookie_value(zUsername, zHash); |
| 316 | 288 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 317 | 289 | record_login_attempt(zUsername, zIpAddr, 1); |
| 318 | 290 | db_multi_exec( |
| 319 | - "UPDATE user SET cookie=%Q, ipaddr=%Q, " | |
| 291 | + "UPDATE user SET cookie=%Q," | |
| 320 | 292 | " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", |
| 321 | - zHash, zRemoteAddr, expires, uid | |
| 293 | + zHash, expires, uid | |
| 322 | 294 | ); |
| 323 | - free(zRemoteAddr); | |
| 324 | 295 | free(zHash); |
| 325 | 296 | if( zDest ){ |
| 326 | 297 | *zDest = zCookie; |
| 327 | 298 | }else{ |
| 328 | 299 | free(zCookie); |
| @@ -331,34 +302,25 @@ | ||
| 331 | 302 | |
| 332 | 303 | /* Sets a cookie for an anonymous user login, which looks like this: |
| 333 | 304 | ** |
| 334 | 305 | ** HASH/TIME/anonymous |
| 335 | 306 | ** |
| 336 | -** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR | |
| 337 | -** is the abbreviated IP address and SECRET is captcha-secret. | |
| 338 | -** | |
| 339 | -** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR | |
| 340 | -** is used. | |
| 307 | +** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret. | |
| 341 | 308 | ** |
| 342 | 309 | ** If zCookieDest is not NULL then the generated cookie is assigned to |
| 343 | 310 | ** *zCookieDest and the caller must eventually free() it. |
| 344 | 311 | */ |
| 345 | 312 | void login_set_anon_cookie(const char *zIpAddr, char **zCookieDest ){ |
| 346 | 313 | const char *zNow; /* Current time (julian day number) */ |
| 347 | 314 | char *zCookie; /* The login cookie */ |
| 348 | 315 | const char *zCookieName; /* Name of the login cookie */ |
| 349 | 316 | Blob b; /* Blob used during cookie construction */ |
| 350 | - char *zRemoteAddr; /* Abbreviated IP address */ | |
| 351 | - if(!zIpAddr){ | |
| 352 | - zIpAddr = PD("REMOTE_ADDR","nil"); | |
| 353 | - } | |
| 354 | - zRemoteAddr = ipPrefix(zIpAddr); | |
| 355 | 317 | zCookieName = login_cookie_name(); |
| 356 | 318 | zNow = db_text("0", "SELECT julianday('now')"); |
| 357 | - assert( zCookieName && zRemoteAddr && zIpAddr && zNow ); | |
| 319 | + assert( zCookieName && zNow ); | |
| 358 | 320 | blob_init(&b, zNow, -1); |
| 359 | - blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret","")); | |
| 321 | + blob_appendf(&b, "/%s", db_get("captcha-secret","")); | |
| 360 | 322 | sha1sum_blob(&b, &b); |
| 361 | 323 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 362 | 324 | blob_reset(&b); |
| 363 | 325 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600); |
| 364 | 326 | if( zCookieDest ){ |
| @@ -813,12 +775,11 @@ | ||
| 813 | 775 | ** Return true if a transfer was made and false if not. |
| 814 | 776 | */ |
| 815 | 777 | static int login_transfer_credentials( |
| 816 | 778 | const char *zLogin, /* Login we are looking for */ |
| 817 | 779 | const char *zCode, /* Project code of peer repository */ |
| 818 | - const char *zHash, /* HASH from login cookie HASH/CODE/LOGIN */ | |
| 819 | - const char *zRemoteAddr /* Request comes from here */ | |
| 780 | + const char *zHash /* HASH from login cookie HASH/CODE/LOGIN */ | |
| 820 | 781 | ){ |
| 821 | 782 | sqlite3 *pOther = 0; /* The other repository */ |
| 822 | 783 | sqlite3_stmt *pStmt; /* Query against the other repository */ |
| 823 | 784 | char *zSQL; /* SQL of the query against other repo */ |
| 824 | 785 | char *zOtherRepo; /* Filename of the other repository */ |
| @@ -842,24 +803,23 @@ | ||
| 842 | 803 | constant_time_cmp_function, 0, 0); |
| 843 | 804 | sqlite3_busy_timeout(pOther, 5000); |
| 844 | 805 | zSQL = mprintf( |
| 845 | 806 | "SELECT cexpire FROM user" |
| 846 | 807 | " WHERE login=%Q" |
| 847 | - " AND ipaddr=%Q" | |
| 848 | 808 | " AND length(cap)>0" |
| 849 | 809 | " AND length(pw)>0" |
| 850 | 810 | " AND cexpire>julianday('now')" |
| 851 | 811 | " AND constant_time_cmp(cookie,%Q)=0", |
| 852 | - zLogin, zRemoteAddr, zHash | |
| 812 | + zLogin, zHash | |
| 853 | 813 | ); |
| 854 | 814 | pStmt = 0; |
| 855 | 815 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 856 | 816 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 857 | 817 | db_multi_exec( |
| 858 | - "UPDATE user SET cookie=%Q, ipaddr=%Q, cexpire=%.17g" | |
| 818 | + "UPDATE user SET cookie=%Q, cexpire=%.17g" | |
| 859 | 819 | " WHERE login=%Q", |
| 860 | - zHash, zRemoteAddr, | |
| 820 | + zHash, | |
| 861 | 821 | sqlite3_column_double(pStmt, 0), zLogin |
| 862 | 822 | ); |
| 863 | 823 | nXfer++; |
| 864 | 824 | } |
| 865 | 825 | sqlite3_finalize(pStmt); |
| @@ -879,33 +839,30 @@ | ||
| 879 | 839 | if( fossil_strcmp(zLogin, "reader")==0 ) return 1; |
| 880 | 840 | return 0; |
| 881 | 841 | } |
| 882 | 842 | |
| 883 | 843 | /* |
| 884 | -** Lookup the uid for a non-built-in user with zLogin and zCookie and | |
| 885 | -** zRemoteAddr. Return 0 if not found. | |
| 844 | +** Lookup the uid for a non-built-in user with zLogin and zCookie. | |
| 845 | +** Return 0 if not found. | |
| 886 | 846 | ** |
| 887 | 847 | ** Note that this only searches for logged-in entries with matching |
| 888 | -** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr) | |
| 889 | -** entries. | |
| 848 | +** zCookie (db: user.cookie) entries. | |
| 890 | 849 | */ |
| 891 | 850 | static int login_find_user( |
| 892 | 851 | const char *zLogin, /* User name */ |
| 893 | - const char *zCookie, /* Login cookie value */ | |
| 894 | - const char *zRemoteAddr /* Abbreviated IP address for valid login */ | |
| 852 | + const char *zCookie /* Login cookie value */ | |
| 895 | 853 | ){ |
| 896 | 854 | int uid; |
| 897 | 855 | if( login_is_special(zLogin) ) return 0; |
| 898 | 856 | uid = db_int(0, |
| 899 | 857 | "SELECT uid FROM user" |
| 900 | 858 | " WHERE login=%Q" |
| 901 | - " AND ipaddr=%Q" | |
| 902 | 859 | " AND cexpire>julianday('now')" |
| 903 | 860 | " AND length(cap)>0" |
| 904 | 861 | " AND length(pw)>0" |
| 905 | 862 | " AND constant_time_cmp(cookie,%Q)=0", |
| 906 | - zLogin, zRemoteAddr, zCookie | |
| 863 | + zLogin, zCookie | |
| 907 | 864 | ); |
| 908 | 865 | return uid; |
| 909 | 866 | } |
| 910 | 867 | |
| 911 | 868 | /* |
| @@ -974,11 +931,10 @@ | ||
| 974 | 931 | */ |
| 975 | 932 | void login_check_credentials(void){ |
| 976 | 933 | int uid = 0; /* User id */ |
| 977 | 934 | const char *zCookie; /* Text of the login cookie */ |
| 978 | 935 | const char *zIpAddr; /* Raw IP address of the requestor */ |
| 979 | - char *zRemoteAddr; /* Abbreviated IP address of the requestor */ | |
| 980 | 936 | const char *zCap = 0; /* Capability string */ |
| 981 | 937 | const char *zPublicPages = 0; /* GLOB patterns of public pages */ |
| 982 | 938 | const char *zLogin = 0; /* Login user for credentials */ |
| 983 | 939 | |
| 984 | 940 | /* Only run this check once. */ |
| @@ -992,11 +948,11 @@ | ||
| 992 | 948 | ** then there is no need to check user credentials. |
| 993 | 949 | ** |
| 994 | 950 | ** This feature allows the "fossil ui" command to give the user |
| 995 | 951 | ** full access rights without having to log in. |
| 996 | 952 | */ |
| 997 | - zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil")); | |
| 953 | + zIpAddr = PD("REMOTE_ADDR","nil"); | |
| 998 | 954 | if( ( cgi_is_loopback(zIpAddr) |
| 999 | 955 | || (g.fSshClient & CGI_SSH_CLIENT)!=0 ) |
| 1000 | 956 | && g.useLocalauth |
| 1001 | 957 | && db_get_int("localauth",0)==0 |
| 1002 | 958 | && P("HTTPS")==0 |
| @@ -1041,12 +997,11 @@ | ||
| 1041 | 997 | ** SECRET is the "captcha-secret" value in the repository. |
| 1042 | 998 | */ |
| 1043 | 999 | double rTime = atof(zArg); |
| 1044 | 1000 | Blob b; |
| 1045 | 1001 | blob_zero(&b); |
| 1046 | - blob_appendf(&b, "%s/%s/%s", | |
| 1047 | - zArg, zRemoteAddr, db_get("captcha-secret","")); | |
| 1002 | + blob_appendf(&b, "%s/%s", zArg, db_get("captcha-secret","")); | |
| 1048 | 1003 | sha1sum_blob(&b, &b); |
| 1049 | 1004 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1050 | 1005 | uid = db_int(0, |
| 1051 | 1006 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1052 | 1007 | " AND length(cap)>0" |
| @@ -1059,13 +1014,13 @@ | ||
| 1059 | 1014 | }else{ |
| 1060 | 1015 | /* Cookies of the form "HASH/CODE/USER". Search first in the |
| 1061 | 1016 | ** local user table, then the user table for project CODE if we |
| 1062 | 1017 | ** are part of a login-group. |
| 1063 | 1018 | */ |
| 1064 | - uid = login_find_user(zUser, zHash, zRemoteAddr); | |
| 1065 | - if( uid==0 && login_transfer_credentials(zUser,zArg,zHash,zRemoteAddr) ){ | |
| 1066 | - uid = login_find_user(zUser, zHash, zRemoteAddr); | |
| 1019 | + uid = login_find_user(zUser, zHash); | |
| 1020 | + if( uid==0 && login_transfer_credentials(zUser,zArg,zHash) ){ | |
| 1021 | + uid = login_find_user(zUser, zHash); | |
| 1067 | 1022 | if( uid ) record_login_attempt(zUser, zIpAddr, 1); |
| 1068 | 1023 | } |
| 1069 | 1024 | } |
| 1070 | 1025 | sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash); |
| 1071 | 1026 | } |
| 1072 | 1027 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -111,36 +111,10 @@ | |
| 111 | }else{ |
| 112 | fossil_redirect_home(); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | /* |
| 117 | ** The IP address of the client is stored as part of login cookies. |
| 118 | ** But some clients are behind firewalls that shift the IP address |
| 119 | ** with each HTTP request. To allow such (broken) clients to log in, |
| 120 | ** extract just a prefix of the IP address. |
| 121 | */ |
| 122 | static char *ipPrefix(const char *zIP){ |
| 123 | int i, j; |
| 124 | static int ip_prefix_terms = -1; |
| 125 | if( ip_prefix_terms<0 ){ |
| 126 | if( db_get_int("redirect-to-https",0)>=2 ){ |
| 127 | ip_prefix_terms = 0; |
| 128 | }else{ |
| 129 | ip_prefix_terms = db_get_int("ip-prefix-terms",2); |
| 130 | } |
| 131 | } |
| 132 | if( ip_prefix_terms==0 ) return mprintf("0"); |
| 133 | for(i=j=0; zIP[i]; i++){ |
| 134 | if( zIP[i]=='.' ){ |
| 135 | j++; |
| 136 | if( j==ip_prefix_terms ) break; |
| 137 | } |
| 138 | } |
| 139 | return mprintf("%.*s", i, zIP); |
| 140 | } |
| 141 | |
| 142 | /* |
| 143 | ** Return an abbreviated project code. The abbreviation is the first |
| 144 | ** 16 characters of the project code. |
| 145 | ** |
| 146 | ** Memory is obtained from malloc. |
| @@ -299,30 +273,27 @@ | |
| 299 | const char *zExpire = db_get("cookie-expire","8766"); |
| 300 | int expires = atoi(zExpire)*3600; |
| 301 | char *zHash; |
| 302 | char *zCookie; |
| 303 | const char *zIpAddr = PD("REMOTE_ADDR","nil"); /* IP address of user */ |
| 304 | char *zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */ |
| 305 | |
| 306 | assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data."); |
| 307 | zHash = db_text(0, |
| 308 | "SELECT cookie FROM user" |
| 309 | " WHERE uid=%d" |
| 310 | " AND ipaddr=%Q" |
| 311 | " AND cexpire>julianday('now')" |
| 312 | " AND length(cookie)>30", |
| 313 | uid, zRemoteAddr); |
| 314 | if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))"); |
| 315 | zCookie = login_gen_user_cookie_value(zUsername, zHash); |
| 316 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 317 | record_login_attempt(zUsername, zIpAddr, 1); |
| 318 | db_multi_exec( |
| 319 | "UPDATE user SET cookie=%Q, ipaddr=%Q, " |
| 320 | " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", |
| 321 | zHash, zRemoteAddr, expires, uid |
| 322 | ); |
| 323 | free(zRemoteAddr); |
| 324 | free(zHash); |
| 325 | if( zDest ){ |
| 326 | *zDest = zCookie; |
| 327 | }else{ |
| 328 | free(zCookie); |
| @@ -331,34 +302,25 @@ | |
| 331 | |
| 332 | /* Sets a cookie for an anonymous user login, which looks like this: |
| 333 | ** |
| 334 | ** HASH/TIME/anonymous |
| 335 | ** |
| 336 | ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR |
| 337 | ** is the abbreviated IP address and SECRET is captcha-secret. |
| 338 | ** |
| 339 | ** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR |
| 340 | ** is used. |
| 341 | ** |
| 342 | ** If zCookieDest is not NULL then the generated cookie is assigned to |
| 343 | ** *zCookieDest and the caller must eventually free() it. |
| 344 | */ |
| 345 | void login_set_anon_cookie(const char *zIpAddr, char **zCookieDest ){ |
| 346 | const char *zNow; /* Current time (julian day number) */ |
| 347 | char *zCookie; /* The login cookie */ |
| 348 | const char *zCookieName; /* Name of the login cookie */ |
| 349 | Blob b; /* Blob used during cookie construction */ |
| 350 | char *zRemoteAddr; /* Abbreviated IP address */ |
| 351 | if(!zIpAddr){ |
| 352 | zIpAddr = PD("REMOTE_ADDR","nil"); |
| 353 | } |
| 354 | zRemoteAddr = ipPrefix(zIpAddr); |
| 355 | zCookieName = login_cookie_name(); |
| 356 | zNow = db_text("0", "SELECT julianday('now')"); |
| 357 | assert( zCookieName && zRemoteAddr && zIpAddr && zNow ); |
| 358 | blob_init(&b, zNow, -1); |
| 359 | blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret","")); |
| 360 | sha1sum_blob(&b, &b); |
| 361 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 362 | blob_reset(&b); |
| 363 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600); |
| 364 | if( zCookieDest ){ |
| @@ -813,12 +775,11 @@ | |
| 813 | ** Return true if a transfer was made and false if not. |
| 814 | */ |
| 815 | static int login_transfer_credentials( |
| 816 | const char *zLogin, /* Login we are looking for */ |
| 817 | const char *zCode, /* Project code of peer repository */ |
| 818 | const char *zHash, /* HASH from login cookie HASH/CODE/LOGIN */ |
| 819 | const char *zRemoteAddr /* Request comes from here */ |
| 820 | ){ |
| 821 | sqlite3 *pOther = 0; /* The other repository */ |
| 822 | sqlite3_stmt *pStmt; /* Query against the other repository */ |
| 823 | char *zSQL; /* SQL of the query against other repo */ |
| 824 | char *zOtherRepo; /* Filename of the other repository */ |
| @@ -842,24 +803,23 @@ | |
| 842 | constant_time_cmp_function, 0, 0); |
| 843 | sqlite3_busy_timeout(pOther, 5000); |
| 844 | zSQL = mprintf( |
| 845 | "SELECT cexpire FROM user" |
| 846 | " WHERE login=%Q" |
| 847 | " AND ipaddr=%Q" |
| 848 | " AND length(cap)>0" |
| 849 | " AND length(pw)>0" |
| 850 | " AND cexpire>julianday('now')" |
| 851 | " AND constant_time_cmp(cookie,%Q)=0", |
| 852 | zLogin, zRemoteAddr, zHash |
| 853 | ); |
| 854 | pStmt = 0; |
| 855 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 856 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 857 | db_multi_exec( |
| 858 | "UPDATE user SET cookie=%Q, ipaddr=%Q, cexpire=%.17g" |
| 859 | " WHERE login=%Q", |
| 860 | zHash, zRemoteAddr, |
| 861 | sqlite3_column_double(pStmt, 0), zLogin |
| 862 | ); |
| 863 | nXfer++; |
| 864 | } |
| 865 | sqlite3_finalize(pStmt); |
| @@ -879,33 +839,30 @@ | |
| 879 | if( fossil_strcmp(zLogin, "reader")==0 ) return 1; |
| 880 | return 0; |
| 881 | } |
| 882 | |
| 883 | /* |
| 884 | ** Lookup the uid for a non-built-in user with zLogin and zCookie and |
| 885 | ** zRemoteAddr. Return 0 if not found. |
| 886 | ** |
| 887 | ** Note that this only searches for logged-in entries with matching |
| 888 | ** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr) |
| 889 | ** entries. |
| 890 | */ |
| 891 | static int login_find_user( |
| 892 | const char *zLogin, /* User name */ |
| 893 | const char *zCookie, /* Login cookie value */ |
| 894 | const char *zRemoteAddr /* Abbreviated IP address for valid login */ |
| 895 | ){ |
| 896 | int uid; |
| 897 | if( login_is_special(zLogin) ) return 0; |
| 898 | uid = db_int(0, |
| 899 | "SELECT uid FROM user" |
| 900 | " WHERE login=%Q" |
| 901 | " AND ipaddr=%Q" |
| 902 | " AND cexpire>julianday('now')" |
| 903 | " AND length(cap)>0" |
| 904 | " AND length(pw)>0" |
| 905 | " AND constant_time_cmp(cookie,%Q)=0", |
| 906 | zLogin, zRemoteAddr, zCookie |
| 907 | ); |
| 908 | return uid; |
| 909 | } |
| 910 | |
| 911 | /* |
| @@ -974,11 +931,10 @@ | |
| 974 | */ |
| 975 | void login_check_credentials(void){ |
| 976 | int uid = 0; /* User id */ |
| 977 | const char *zCookie; /* Text of the login cookie */ |
| 978 | const char *zIpAddr; /* Raw IP address of the requestor */ |
| 979 | char *zRemoteAddr; /* Abbreviated IP address of the requestor */ |
| 980 | const char *zCap = 0; /* Capability string */ |
| 981 | const char *zPublicPages = 0; /* GLOB patterns of public pages */ |
| 982 | const char *zLogin = 0; /* Login user for credentials */ |
| 983 | |
| 984 | /* Only run this check once. */ |
| @@ -992,11 +948,11 @@ | |
| 992 | ** then there is no need to check user credentials. |
| 993 | ** |
| 994 | ** This feature allows the "fossil ui" command to give the user |
| 995 | ** full access rights without having to log in. |
| 996 | */ |
| 997 | zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil")); |
| 998 | if( ( cgi_is_loopback(zIpAddr) |
| 999 | || (g.fSshClient & CGI_SSH_CLIENT)!=0 ) |
| 1000 | && g.useLocalauth |
| 1001 | && db_get_int("localauth",0)==0 |
| 1002 | && P("HTTPS")==0 |
| @@ -1041,12 +997,11 @@ | |
| 1041 | ** SECRET is the "captcha-secret" value in the repository. |
| 1042 | */ |
| 1043 | double rTime = atof(zArg); |
| 1044 | Blob b; |
| 1045 | blob_zero(&b); |
| 1046 | blob_appendf(&b, "%s/%s/%s", |
| 1047 | zArg, zRemoteAddr, db_get("captcha-secret","")); |
| 1048 | sha1sum_blob(&b, &b); |
| 1049 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1050 | uid = db_int(0, |
| 1051 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1052 | " AND length(cap)>0" |
| @@ -1059,13 +1014,13 @@ | |
| 1059 | }else{ |
| 1060 | /* Cookies of the form "HASH/CODE/USER". Search first in the |
| 1061 | ** local user table, then the user table for project CODE if we |
| 1062 | ** are part of a login-group. |
| 1063 | */ |
| 1064 | uid = login_find_user(zUser, zHash, zRemoteAddr); |
| 1065 | if( uid==0 && login_transfer_credentials(zUser,zArg,zHash,zRemoteAddr) ){ |
| 1066 | uid = login_find_user(zUser, zHash, zRemoteAddr); |
| 1067 | if( uid ) record_login_attempt(zUser, zIpAddr, 1); |
| 1068 | } |
| 1069 | } |
| 1070 | sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash); |
| 1071 | } |
| 1072 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -111,36 +111,10 @@ | |
| 111 | }else{ |
| 112 | fossil_redirect_home(); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | /* |
| 117 | ** Return an abbreviated project code. The abbreviation is the first |
| 118 | ** 16 characters of the project code. |
| 119 | ** |
| 120 | ** Memory is obtained from malloc. |
| @@ -299,30 +273,27 @@ | |
| 273 | const char *zExpire = db_get("cookie-expire","8766"); |
| 274 | int expires = atoi(zExpire)*3600; |
| 275 | char *zHash; |
| 276 | char *zCookie; |
| 277 | const char *zIpAddr = PD("REMOTE_ADDR","nil"); /* IP address of user */ |
| 278 | |
| 279 | assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data."); |
| 280 | zHash = db_text(0, |
| 281 | "SELECT cookie FROM user" |
| 282 | " WHERE uid=%d" |
| 283 | " AND cexpire>julianday('now')" |
| 284 | " AND length(cookie)>30", |
| 285 | uid); |
| 286 | if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))"); |
| 287 | zCookie = login_gen_user_cookie_value(zUsername, zHash); |
| 288 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 289 | record_login_attempt(zUsername, zIpAddr, 1); |
| 290 | db_multi_exec( |
| 291 | "UPDATE user SET cookie=%Q," |
| 292 | " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", |
| 293 | zHash, expires, uid |
| 294 | ); |
| 295 | free(zHash); |
| 296 | if( zDest ){ |
| 297 | *zDest = zCookie; |
| 298 | }else{ |
| 299 | free(zCookie); |
| @@ -331,34 +302,25 @@ | |
| 302 | |
| 303 | /* Sets a cookie for an anonymous user login, which looks like this: |
| 304 | ** |
| 305 | ** HASH/TIME/anonymous |
| 306 | ** |
| 307 | ** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret. |
| 308 | ** |
| 309 | ** If zCookieDest is not NULL then the generated cookie is assigned to |
| 310 | ** *zCookieDest and the caller must eventually free() it. |
| 311 | */ |
| 312 | void login_set_anon_cookie(const char *zIpAddr, char **zCookieDest ){ |
| 313 | const char *zNow; /* Current time (julian day number) */ |
| 314 | char *zCookie; /* The login cookie */ |
| 315 | const char *zCookieName; /* Name of the login cookie */ |
| 316 | Blob b; /* Blob used during cookie construction */ |
| 317 | zCookieName = login_cookie_name(); |
| 318 | zNow = db_text("0", "SELECT julianday('now')"); |
| 319 | assert( zCookieName && zNow ); |
| 320 | blob_init(&b, zNow, -1); |
| 321 | blob_appendf(&b, "/%s", db_get("captcha-secret","")); |
| 322 | sha1sum_blob(&b, &b); |
| 323 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 324 | blob_reset(&b); |
| 325 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600); |
| 326 | if( zCookieDest ){ |
| @@ -813,12 +775,11 @@ | |
| 775 | ** Return true if a transfer was made and false if not. |
| 776 | */ |
| 777 | static int login_transfer_credentials( |
| 778 | const char *zLogin, /* Login we are looking for */ |
| 779 | const char *zCode, /* Project code of peer repository */ |
| 780 | const char *zHash /* HASH from login cookie HASH/CODE/LOGIN */ |
| 781 | ){ |
| 782 | sqlite3 *pOther = 0; /* The other repository */ |
| 783 | sqlite3_stmt *pStmt; /* Query against the other repository */ |
| 784 | char *zSQL; /* SQL of the query against other repo */ |
| 785 | char *zOtherRepo; /* Filename of the other repository */ |
| @@ -842,24 +803,23 @@ | |
| 803 | constant_time_cmp_function, 0, 0); |
| 804 | sqlite3_busy_timeout(pOther, 5000); |
| 805 | zSQL = mprintf( |
| 806 | "SELECT cexpire FROM user" |
| 807 | " WHERE login=%Q" |
| 808 | " AND length(cap)>0" |
| 809 | " AND length(pw)>0" |
| 810 | " AND cexpire>julianday('now')" |
| 811 | " AND constant_time_cmp(cookie,%Q)=0", |
| 812 | zLogin, zHash |
| 813 | ); |
| 814 | pStmt = 0; |
| 815 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 816 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 817 | db_multi_exec( |
| 818 | "UPDATE user SET cookie=%Q, cexpire=%.17g" |
| 819 | " WHERE login=%Q", |
| 820 | zHash, |
| 821 | sqlite3_column_double(pStmt, 0), zLogin |
| 822 | ); |
| 823 | nXfer++; |
| 824 | } |
| 825 | sqlite3_finalize(pStmt); |
| @@ -879,33 +839,30 @@ | |
| 839 | if( fossil_strcmp(zLogin, "reader")==0 ) return 1; |
| 840 | return 0; |
| 841 | } |
| 842 | |
| 843 | /* |
| 844 | ** Lookup the uid for a non-built-in user with zLogin and zCookie. |
| 845 | ** Return 0 if not found. |
| 846 | ** |
| 847 | ** Note that this only searches for logged-in entries with matching |
| 848 | ** zCookie (db: user.cookie) entries. |
| 849 | */ |
| 850 | static int login_find_user( |
| 851 | const char *zLogin, /* User name */ |
| 852 | const char *zCookie /* Login cookie value */ |
| 853 | ){ |
| 854 | int uid; |
| 855 | if( login_is_special(zLogin) ) return 0; |
| 856 | uid = db_int(0, |
| 857 | "SELECT uid FROM user" |
| 858 | " WHERE login=%Q" |
| 859 | " AND cexpire>julianday('now')" |
| 860 | " AND length(cap)>0" |
| 861 | " AND length(pw)>0" |
| 862 | " AND constant_time_cmp(cookie,%Q)=0", |
| 863 | zLogin, zCookie |
| 864 | ); |
| 865 | return uid; |
| 866 | } |
| 867 | |
| 868 | /* |
| @@ -974,11 +931,10 @@ | |
| 931 | */ |
| 932 | void login_check_credentials(void){ |
| 933 | int uid = 0; /* User id */ |
| 934 | const char *zCookie; /* Text of the login cookie */ |
| 935 | const char *zIpAddr; /* Raw IP address of the requestor */ |
| 936 | const char *zCap = 0; /* Capability string */ |
| 937 | const char *zPublicPages = 0; /* GLOB patterns of public pages */ |
| 938 | const char *zLogin = 0; /* Login user for credentials */ |
| 939 | |
| 940 | /* Only run this check once. */ |
| @@ -992,11 +948,11 @@ | |
| 948 | ** then there is no need to check user credentials. |
| 949 | ** |
| 950 | ** This feature allows the "fossil ui" command to give the user |
| 951 | ** full access rights without having to log in. |
| 952 | */ |
| 953 | zIpAddr = PD("REMOTE_ADDR","nil"); |
| 954 | if( ( cgi_is_loopback(zIpAddr) |
| 955 | || (g.fSshClient & CGI_SSH_CLIENT)!=0 ) |
| 956 | && g.useLocalauth |
| 957 | && db_get_int("localauth",0)==0 |
| 958 | && P("HTTPS")==0 |
| @@ -1041,12 +997,11 @@ | |
| 997 | ** SECRET is the "captcha-secret" value in the repository. |
| 998 | */ |
| 999 | double rTime = atof(zArg); |
| 1000 | Blob b; |
| 1001 | blob_zero(&b); |
| 1002 | blob_appendf(&b, "%s/%s", zArg, db_get("captcha-secret","")); |
| 1003 | sha1sum_blob(&b, &b); |
| 1004 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1005 | uid = db_int(0, |
| 1006 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1007 | " AND length(cap)>0" |
| @@ -1059,13 +1014,13 @@ | |
| 1014 | }else{ |
| 1015 | /* Cookies of the form "HASH/CODE/USER". Search first in the |
| 1016 | ** local user table, then the user table for project CODE if we |
| 1017 | ** are part of a login-group. |
| 1018 | */ |
| 1019 | uid = login_find_user(zUser, zHash); |
| 1020 | if( uid==0 && login_transfer_credentials(zUser,zArg,zHash) ){ |
| 1021 | uid = login_find_user(zUser, zHash); |
| 1022 | if( uid ) record_login_attempt(zUser, zIpAddr, 1); |
| 1023 | } |
| 1024 | } |
| 1025 | sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash); |
| 1026 | } |
| 1027 |
-12
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -402,22 +402,10 @@ | ||
| 402 | 402 | @ variable or the "Authentication:" HTTP header to find the username and |
| 403 | 403 | @ password. This is another way of supporting Basic Authenitication. |
| 404 | 404 | @ (Property: "http_authentication_ok") |
| 405 | 405 | @ </p> |
| 406 | 406 | @ |
| 407 | - @ <hr /> | |
| 408 | - entry_attribute("IP address terms used in login cookie", 3, | |
| 409 | - "ip-prefix-terms", "ipt", "2", 0); | |
| 410 | - @ <p>The number of octets of of the IP address used in the login cookie | |
| 411 | - @ when using unencrypted HTTP instead of HTTPS. | |
| 412 | - @ Set to zero to omit the IP address from the login cookie. A value of | |
| 413 | - @ 2 is recommended. | |
| 414 | - @ If the "Redirect to HTTP" above is set to "Always", then the IP address | |
| 415 | - @ is not used in the login cookie and this setting is irrelevant. | |
| 416 | - @ (Property: "ip-prefix-terms") | |
| 417 | - @ </p> | |
| 418 | - @ | |
| 419 | 407 | @ <hr /> |
| 420 | 408 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| 421 | 409 | "8766", 0); |
| 422 | 410 | @ <p>The number of hours for which a login is valid. This must be a |
| 423 | 411 | @ positive number. The default is 8766 hours which is approximately equal |
| 424 | 412 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -402,22 +402,10 @@ | |
| 402 | @ variable or the "Authentication:" HTTP header to find the username and |
| 403 | @ password. This is another way of supporting Basic Authenitication. |
| 404 | @ (Property: "http_authentication_ok") |
| 405 | @ </p> |
| 406 | @ |
| 407 | @ <hr /> |
| 408 | entry_attribute("IP address terms used in login cookie", 3, |
| 409 | "ip-prefix-terms", "ipt", "2", 0); |
| 410 | @ <p>The number of octets of of the IP address used in the login cookie |
| 411 | @ when using unencrypted HTTP instead of HTTPS. |
| 412 | @ Set to zero to omit the IP address from the login cookie. A value of |
| 413 | @ 2 is recommended. |
| 414 | @ If the "Redirect to HTTP" above is set to "Always", then the IP address |
| 415 | @ is not used in the login cookie and this setting is irrelevant. |
| 416 | @ (Property: "ip-prefix-terms") |
| 417 | @ </p> |
| 418 | @ |
| 419 | @ <hr /> |
| 420 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| 421 | "8766", 0); |
| 422 | @ <p>The number of hours for which a login is valid. This must be a |
| 423 | @ positive number. The default is 8766 hours which is approximately equal |
| 424 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -402,22 +402,10 @@ | |
| 402 | @ variable or the "Authentication:" HTTP header to find the username and |
| 403 | @ password. This is another way of supporting Basic Authenitication. |
| 404 | @ (Property: "http_authentication_ok") |
| 405 | @ </p> |
| 406 | @ |
| 407 | @ <hr /> |
| 408 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| 409 | "8766", 0); |
| 410 | @ <p>The number of hours for which a login is valid. This must be a |
| 411 | @ positive number. The default is 8766 hours which is approximately equal |
| 412 |