| | @@ -261,20 +261,27 @@ |
| 261 | 261 | ** and user.cexpire fields for the given user. |
| 262 | 262 | ** |
| 263 | 263 | ** If zDest is not NULL then the generated cookie is copied to |
| 264 | 264 | ** *zDdest and ownership is transfered to the caller (who should |
| 265 | 265 | ** eventually pass it to free()). |
| 266 | +** |
| 267 | +** If bSessionCookie is true, the cookie will be a session cookie, |
| 268 | +** else a persistent cookie. If it's a session cookie, the |
| 269 | +** [user].[cexpire] and [user].[cookie] entries will be modified as if |
| 270 | +** it were a persistent cookie because doing so is necessary for |
| 271 | +** fossil's own "is this cookie still valid?" checks to work. |
| 266 | 272 | */ |
| 267 | 273 | void login_set_user_cookie( |
| 268 | 274 | const char *zUsername, /* User's name */ |
| 269 | 275 | int uid, /* User's ID */ |
| 270 | | - char **zDest /* Optional: store generated cookie value. */ |
| 276 | + char **zDest, /* Optional: store generated cookie value. */ |
| 277 | + int bSessionCookie /* True for session-only cookie */ |
| 271 | 278 | ){ |
| 272 | 279 | const char *zCookieName = login_cookie_name(); |
| 273 | 280 | const char *zExpire = db_get("cookie-expire","8766"); |
| 274 | | - int expires = atoi(zExpire)*3600; |
| 275 | | - char *zHash; |
| 281 | + const int expires = atoi(zExpire)*3600; |
| 282 | + char *zHash = 0; |
| 276 | 283 | char *zCookie; |
| 277 | 284 | const char *zIpAddr = PD("REMOTE_ADDR","nil"); /* IP address of user */ |
| 278 | 285 | |
| 279 | 286 | assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data."); |
| 280 | 287 | zHash = db_text(0, |
| | @@ -283,18 +290,17 @@ |
| 283 | 290 | " AND cexpire>julianday('now')" |
| 284 | 291 | " AND length(cookie)>30", |
| 285 | 292 | uid); |
| 286 | 293 | if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))"); |
| 287 | 294 | zCookie = login_gen_user_cookie_value(zUsername, zHash); |
| 288 | | - cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 295 | + cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), |
| 296 | + bSessionCookie ? 0 : expires); |
| 289 | 297 | record_login_attempt(zUsername, zIpAddr, 1); |
| 290 | | - db_multi_exec( |
| 291 | | - "UPDATE user SET cookie=%Q," |
| 298 | + db_multi_exec("UPDATE user SET cookie=%Q," |
| 292 | 299 | " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", |
| 293 | | - zHash, expires, uid |
| 294 | | - ); |
| 295 | | - free(zHash); |
| 300 | + zHash, expires, uid); |
| 301 | + fossil_free(zHash); |
| 296 | 302 | if( zDest ){ |
| 297 | 303 | *zDest = zCookie; |
| 298 | 304 | }else{ |
| 299 | 305 | free(zCookie); |
| 300 | 306 | } |
| | @@ -306,31 +312,34 @@ |
| 306 | 312 | ** |
| 307 | 313 | ** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret. |
| 308 | 314 | ** |
| 309 | 315 | ** If zCookieDest is not NULL then the generated cookie is assigned to |
| 310 | 316 | ** *zCookieDest and the caller must eventually free() it. |
| 317 | +** |
| 318 | +** If bSessionCookie is true, the cookie will be a session cookie. |
| 311 | 319 | */ |
| 312 | | -void login_set_anon_cookie(const char *zIpAddr, char **zCookieDest ){ |
| 320 | +void login_set_anon_cookie(const char *zIpAddr, char **zCookieDest, |
| 321 | + int bSessionCookie ){ |
| 313 | 322 | const char *zNow; /* Current time (julian day number) */ |
| 314 | 323 | char *zCookie; /* The login cookie */ |
| 315 | 324 | const char *zCookieName; /* Name of the login cookie */ |
| 316 | 325 | Blob b; /* Blob used during cookie construction */ |
| 326 | + int expires = bSessionCookie ? 0 : 6*3600; |
| 317 | 327 | zCookieName = login_cookie_name(); |
| 318 | 328 | zNow = db_text("0", "SELECT julianday('now')"); |
| 319 | 329 | assert( zCookieName && zNow ); |
| 320 | 330 | blob_init(&b, zNow, -1); |
| 321 | 331 | blob_appendf(&b, "/%s", db_get("captcha-secret","")); |
| 322 | 332 | sha1sum_blob(&b, &b); |
| 323 | 333 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 324 | 334 | blob_reset(&b); |
| 325 | | - cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600); |
| 335 | + cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 326 | 336 | if( zCookieDest ){ |
| 327 | 337 | *zCookieDest = zCookie; |
| 328 | 338 | }else{ |
| 329 | 339 | free(zCookie); |
| 330 | 340 | } |
| 331 | | - |
| 332 | 341 | } |
| 333 | 342 | |
| 334 | 343 | /* |
| 335 | 344 | ** "Unsets" the login cookie (insofar as cookies can be unset) and |
| 336 | 345 | ** clears the current user's (g.userUid) login information from the |
| | @@ -515,20 +524,22 @@ |
| 515 | 524 | char *zErrMsg = ""; |
| 516 | 525 | int uid; /* User id logged in user */ |
| 517 | 526 | char *zSha1Pw; |
| 518 | 527 | const char *zIpAddr; /* IP address of requestor */ |
| 519 | 528 | const char *zReferer; |
| 520 | | - int noAnon = P("noanon")!=0; |
| 529 | + const int noAnon = P("noanon")!=0; |
| 530 | + int rememberMe; /* If true, use persistent cookie, else |
| 531 | + session cookie. Toggled per |
| 532 | + checkbox. */ |
| 521 | 533 | |
| 522 | 534 | login_check_credentials(); |
| 523 | 535 | fossil_redirect_to_https_if_needed(1); |
| 524 | 536 | sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, |
| 525 | 537 | constant_time_cmp_function, 0, 0); |
| 526 | 538 | zUsername = P("u"); |
| 527 | 539 | zPasswd = P("p"); |
| 528 | 540 | anonFlag = g.zLogin==0 && PB("anon"); |
| 529 | | - |
| 530 | 541 | /* Handle log-out requests */ |
| 531 | 542 | if( P("out") ){ |
| 532 | 543 | login_clear_login_data(); |
| 533 | 544 | redirect_to_g(); |
| 534 | 545 | return; |
| | @@ -600,12 +611,18 @@ |
| 600 | 611 | } |
| 601 | 612 | } |
| 602 | 613 | zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */ |
| 603 | 614 | zReferer = P("HTTP_REFERER"); |
| 604 | 615 | uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs")); |
| 616 | + if(zUsername==0){ |
| 617 | + /* Initial login page hit. */ |
| 618 | + rememberMe = 0; |
| 619 | + }else{ |
| 620 | + rememberMe = P("remember")!=0; |
| 621 | + } |
| 605 | 622 | if( uid>0 ){ |
| 606 | | - login_set_anon_cookie(zIpAddr, NULL); |
| 623 | + login_set_anon_cookie(zIpAddr, NULL, rememberMe?0:1); |
| 607 | 624 | record_login_attempt("anonymous", zIpAddr, 1); |
| 608 | 625 | redirect_to_g(); |
| 609 | 626 | } |
| 610 | 627 | if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){ |
| 611 | 628 | /* Attempting to log in as a user other than anonymous. |
| | @@ -625,11 +642,11 @@ |
| 625 | 642 | ** HASH/PROJECT/LOGIN |
| 626 | 643 | ** |
| 627 | 644 | ** where HASH is a random hex number, PROJECT is either project |
| 628 | 645 | ** code prefix, and LOGIN is the user name. |
| 629 | 646 | */ |
| 630 | | - login_set_user_cookie(zUsername, uid, NULL); |
| 647 | + login_set_user_cookie(zUsername, uid, NULL, rememberMe?0:1); |
| 631 | 648 | redirect_to_g(); |
| 632 | 649 | } |
| 633 | 650 | } |
| 634 | 651 | style_header("Login/Logout"); |
| 635 | 652 | style_adunit_config(ADUNIT_OFF); |
| | @@ -646,10 +663,11 @@ |
| 646 | 663 | @ <p>Login as <b>anonymous</b> or any named user |
| 647 | 664 | @ to access page <b>%h(zAbbrev)</b>. |
| 648 | 665 | }else{ |
| 649 | 666 | @ <p>Login as a named user to access page <b>%h(zAbbrev)</b>. |
| 650 | 667 | } |
| 668 | + fossil_free(zAbbrev); |
| 651 | 669 | } |
| 652 | 670 | if( g.sslNotAvailable==0 |
| 653 | 671 | && strncmp(g.zBaseURL,"https:",6)!=0 |
| 654 | 672 | && db_get_boolean("https-login",0) |
| 655 | 673 | ){ |
| | @@ -677,10 +695,21 @@ |
| 677 | 695 | " AND cap!=''"); |
| 678 | 696 | }else{ |
| 679 | 697 | zAnonPw = 0; |
| 680 | 698 | } |
| 681 | 699 | @ <table class="login_out"> |
| 700 | + if( P("HTTPS")==0 ){ |
| 701 | + @ <tr><td class="form_label">Warning:</td> |
| 702 | + @ <td><span class='securityWarning'> |
| 703 | + @ Login information, including the password, |
| 704 | + @ will be sent in the clear over an unencrypted connection. |
| 705 | + if( !g.sslNotAvailable ){ |
| 706 | + @ Consider logging in at |
| 707 | + @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead. |
| 708 | + } |
| 709 | + @ </span></td></tr> |
| 710 | + } |
| 682 | 711 | @ <tr> |
| 683 | 712 | @ <td class="form_label" id="userlabel1">User ID:</td> |
| 684 | 713 | @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \ |
| 685 | 714 | @ size="30" value="%s(anonFlag?"anonymous":"")"></td> |
| 686 | 715 | @ </tr> |
| | @@ -691,26 +720,19 @@ |
| 691 | 720 | if( zAnonPw && !noAnon ){ |
| 692 | 721 | captcha_speakit_button(uSeed, "Speak password for \"anonymous\""); |
| 693 | 722 | } |
| 694 | 723 | @ </td> |
| 695 | 724 | @ </tr> |
| 696 | | - if( P("HTTPS")==0 ){ |
| 697 | | - @ <tr><td class="form_label">Warning:</td> |
| 698 | | - @ <td><span class='securityWarning'> |
| 699 | | - @ Your password will be sent in the clear over an |
| 700 | | - @ unencrypted connection. |
| 701 | | - if( g.sslNotAvailable ){ |
| 702 | | - @ No encrypted connection is available on this server. |
| 703 | | - }else{ |
| 704 | | - @ Consider logging in at |
| 705 | | - @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead. |
| 706 | | - } |
| 707 | | - @ </span></td></tr> |
| 708 | | - } |
| 725 | + @ <tr> |
| 726 | + @ <td></td> |
| 727 | + @ <td><input type="checkbox" name="remember" value="1" \ |
| 728 | + @ id="remember-me" %s(rememberMe ? "checked=\"checked\"" : "")> |
| 729 | + @ <label for="remember-me">Remember me?</label></td> |
| 730 | + @ </tr> |
| 709 | 731 | @ <tr> |
| 710 | 732 | @ <td></td> |
| 711 | | - @ <td><input type="submit" name="in" value="Login"></td> |
| 733 | + @ <td><input type="submit" name="in" value="Login"> |
| 712 | 734 | @ </tr> |
| 713 | 735 | if( !noAnon && login_self_register_available(0) ){ |
| 714 | 736 | @ <tr> |
| 715 | 737 | @ <td></td> |
| 716 | 738 | @ <td><input type="submit" name="self" value="Create A New Account"> |
| | @@ -1598,11 +1620,11 @@ |
| 1598 | 1620 | "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", |
| 1599 | 1621 | zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr); |
| 1600 | 1622 | fossil_free(zPass); |
| 1601 | 1623 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 1602 | 1624 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); |
| 1603 | | - login_set_user_cookie(zUserID, uid, NULL); |
| 1625 | + login_set_user_cookie(zUserID, uid, NULL, 0); |
| 1604 | 1626 | if( doAlerts ){ |
| 1605 | 1627 | /* Also make the new user a subscriber. */ |
| 1606 | 1628 | Blob hdr, body; |
| 1607 | 1629 | AlertSender *pSender; |
| 1608 | 1630 | sqlite3_int64 id; /* New subscriber Id */ |
| 1609 | 1631 | |