Fossil SCM

Add the "Remember me?" checkbox to the login page. If not checked, you get a session cookie for the login.

drh 2020-07-28 19:45 trunk merge
Commit 6b7b3232dfbb2f19a7458728e238e755d4a6a9400994d917256759ebfe1bea4f
3 files changed +8 -5 +2 -2 +53 -31
+8 -5
--- src/cgi.c
+++ src/cgi.c
@@ -234,11 +234,12 @@
234234
235235
/*
236236
** Set a cookie by queuing up the appropriate HTTP header output. If
237237
** !g.isHTTP, this is a no-op.
238238
**
239
-** Zero lifetime implies a session cookie.
239
+** Zero lifetime implies a session cookie. A negative one expires
240
+** the cookie immediately.
240241
*/
241242
void cgi_set_cookie(
242243
const char *zName, /* Name of the cookie */
243244
const char *zValue, /* Value of the cookie. Automatically escaped */
244245
const char *zPath, /* Path cookie applies to. NULL means "/" */
@@ -251,17 +252,19 @@
251252
if( zPath[0]==0 ) zPath = "/";
252253
}
253254
if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){
254255
zSecure = " secure;";
255256
}
256
- if( lifetime>0 ){
257
+ if( lifetime!=0 ){
257258
blob_appendf(&extraHeader,
258
- "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly;%s Version=1\r\n",
259
- zName, zValue, zPath, lifetime, zSecure);
259
+ "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly; "
260
+ "SameSite=strict; %s Version=1\r\n",
261
+ zName, lifetime>0 ? zValue : "null", zPath, lifetime, zSecure);
260262
}else{
261263
blob_appendf(&extraHeader,
262
- "Set-Cookie: %s=%t; Path=%s; HttpOnly;%s Version=1\r\n",
264
+ "Set-Cookie: %s=%t; Path=%s; HttpOnly; SameSite=strict; "
265
+ "%s Version=1\r\n",
263266
zName, zValue, zPath, zSecure);
264267
}
265268
}
266269
267270
268271
--- src/cgi.c
+++ src/cgi.c
@@ -234,11 +234,12 @@
234
235 /*
236 ** Set a cookie by queuing up the appropriate HTTP header output. If
237 ** !g.isHTTP, this is a no-op.
238 **
239 ** Zero lifetime implies a session cookie.
 
240 */
241 void cgi_set_cookie(
242 const char *zName, /* Name of the cookie */
243 const char *zValue, /* Value of the cookie. Automatically escaped */
244 const char *zPath, /* Path cookie applies to. NULL means "/" */
@@ -251,17 +252,19 @@
251 if( zPath[0]==0 ) zPath = "/";
252 }
253 if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){
254 zSecure = " secure;";
255 }
256 if( lifetime>0 ){
257 blob_appendf(&extraHeader,
258 "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly;%s Version=1\r\n",
259 zName, zValue, zPath, lifetime, zSecure);
 
260 }else{
261 blob_appendf(&extraHeader,
262 "Set-Cookie: %s=%t; Path=%s; HttpOnly;%s Version=1\r\n",
 
263 zName, zValue, zPath, zSecure);
264 }
265 }
266
267
268
--- src/cgi.c
+++ src/cgi.c
@@ -234,11 +234,12 @@
234
235 /*
236 ** Set a cookie by queuing up the appropriate HTTP header output. If
237 ** !g.isHTTP, this is a no-op.
238 **
239 ** Zero lifetime implies a session cookie. A negative one expires
240 ** the cookie immediately.
241 */
242 void cgi_set_cookie(
243 const char *zName, /* Name of the cookie */
244 const char *zValue, /* Value of the cookie. Automatically escaped */
245 const char *zPath, /* Path cookie applies to. NULL means "/" */
@@ -251,17 +252,19 @@
252 if( zPath[0]==0 ) zPath = "/";
253 }
254 if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){
255 zSecure = " secure;";
256 }
257 if( lifetime!=0 ){
258 blob_appendf(&extraHeader,
259 "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly; "
260 "SameSite=strict; %s Version=1\r\n",
261 zName, lifetime>0 ? zValue : "null", zPath, lifetime, zSecure);
262 }else{
263 blob_appendf(&extraHeader,
264 "Set-Cookie: %s=%t; Path=%s; HttpOnly; SameSite=strict; "
265 "%s Version=1\r\n",
266 zName, zValue, zPath, zSecure);
267 }
268 }
269
270
271
--- src/json_login.c
+++ src/json_login.c
@@ -145,13 +145,13 @@
145145
}else{
146146
char * cookie = NULL;
147147
cson_object * po;
148148
char * cap = NULL;
149149
if(anonSeed){
150
- login_set_anon_cookie(NULL, &cookie);
150
+ login_set_anon_cookie(NULL, &cookie, 0);
151151
}else{
152
- login_set_user_cookie(name, uid, &cookie);
152
+ login_set_user_cookie(name, uid, &cookie, 0);
153153
}
154154
payload = cson_value_new_object();
155155
po = cson_value_get_object(payload);
156156
cson_object_set(po, "authToken", json_new_string(cookie));
157157
free(cookie);
158158
--- src/json_login.c
+++ src/json_login.c
@@ -145,13 +145,13 @@
145 }else{
146 char * cookie = NULL;
147 cson_object * po;
148 char * cap = NULL;
149 if(anonSeed){
150 login_set_anon_cookie(NULL, &cookie);
151 }else{
152 login_set_user_cookie(name, uid, &cookie);
153 }
154 payload = cson_value_new_object();
155 po = cson_value_get_object(payload);
156 cson_object_set(po, "authToken", json_new_string(cookie));
157 free(cookie);
158
--- src/json_login.c
+++ src/json_login.c
@@ -145,13 +145,13 @@
145 }else{
146 char * cookie = NULL;
147 cson_object * po;
148 char * cap = NULL;
149 if(anonSeed){
150 login_set_anon_cookie(NULL, &cookie, 0);
151 }else{
152 login_set_user_cookie(name, uid, &cookie, 0);
153 }
154 payload = cson_value_new_object();
155 po = cson_value_get_object(payload);
156 cson_object_set(po, "authToken", json_new_string(cookie));
157 free(cookie);
158
+53 -31
--- src/login.c
+++ src/login.c
@@ -261,20 +261,27 @@
261261
** and user.cexpire fields for the given user.
262262
**
263263
** If zDest is not NULL then the generated cookie is copied to
264264
** *zDdest and ownership is transfered to the caller (who should
265265
** 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.
266272
*/
267273
void login_set_user_cookie(
268274
const char *zUsername, /* User's name */
269275
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 */
271278
){
272279
const char *zCookieName = login_cookie_name();
273280
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;
276283
char *zCookie;
277284
const char *zIpAddr = PD("REMOTE_ADDR","nil"); /* IP address of user */
278285
279286
assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data.");
280287
zHash = db_text(0,
@@ -283,18 +290,17 @@
283290
" AND cexpire>julianday('now')"
284291
" AND length(cookie)>30",
285292
uid);
286293
if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))");
287294
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);
289297
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,"
292299
" 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);
296302
if( zDest ){
297303
*zDest = zCookie;
298304
}else{
299305
free(zCookie);
300306
}
@@ -306,31 +312,34 @@
306312
**
307313
** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret.
308314
**
309315
** If zCookieDest is not NULL then the generated cookie is assigned to
310316
** *zCookieDest and the caller must eventually free() it.
317
+**
318
+** If bSessionCookie is true, the cookie will be a session cookie.
311319
*/
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 ){
313322
const char *zNow; /* Current time (julian day number) */
314323
char *zCookie; /* The login cookie */
315324
const char *zCookieName; /* Name of the login cookie */
316325
Blob b; /* Blob used during cookie construction */
326
+ int expires = bSessionCookie ? 0 : 6*3600;
317327
zCookieName = login_cookie_name();
318328
zNow = db_text("0", "SELECT julianday('now')");
319329
assert( zCookieName && zNow );
320330
blob_init(&b, zNow, -1);
321331
blob_appendf(&b, "/%s", db_get("captcha-secret",""));
322332
sha1sum_blob(&b, &b);
323333
zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
324334
blob_reset(&b);
325
- cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
335
+ cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
326336
if( zCookieDest ){
327337
*zCookieDest = zCookie;
328338
}else{
329339
free(zCookie);
330340
}
331
-
332341
}
333342
334343
/*
335344
** "Unsets" the login cookie (insofar as cookies can be unset) and
336345
** clears the current user's (g.userUid) login information from the
@@ -515,20 +524,22 @@
515524
char *zErrMsg = "";
516525
int uid; /* User id logged in user */
517526
char *zSha1Pw;
518527
const char *zIpAddr; /* IP address of requestor */
519528
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. */
521533
522534
login_check_credentials();
523535
fossil_redirect_to_https_if_needed(1);
524536
sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
525537
constant_time_cmp_function, 0, 0);
526538
zUsername = P("u");
527539
zPasswd = P("p");
528540
anonFlag = g.zLogin==0 && PB("anon");
529
-
530541
/* Handle log-out requests */
531542
if( P("out") ){
532543
login_clear_login_data();
533544
redirect_to_g();
534545
return;
@@ -600,12 +611,18 @@
600611
}
601612
}
602613
zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
603614
zReferer = P("HTTP_REFERER");
604615
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
+ }
605622
if( uid>0 ){
606
- login_set_anon_cookie(zIpAddr, NULL);
623
+ login_set_anon_cookie(zIpAddr, NULL, rememberMe?0:1);
607624
record_login_attempt("anonymous", zIpAddr, 1);
608625
redirect_to_g();
609626
}
610627
if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
611628
/* Attempting to log in as a user other than anonymous.
@@ -625,11 +642,11 @@
625642
** HASH/PROJECT/LOGIN
626643
**
627644
** where HASH is a random hex number, PROJECT is either project
628645
** code prefix, and LOGIN is the user name.
629646
*/
630
- login_set_user_cookie(zUsername, uid, NULL);
647
+ login_set_user_cookie(zUsername, uid, NULL, rememberMe?0:1);
631648
redirect_to_g();
632649
}
633650
}
634651
style_header("Login/Logout");
635652
style_adunit_config(ADUNIT_OFF);
@@ -646,10 +663,11 @@
646663
@ <p>Login as <b>anonymous</b> or any named user
647664
@ to access page <b>%h(zAbbrev)</b>.
648665
}else{
649666
@ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
650667
}
668
+ fossil_free(zAbbrev);
651669
}
652670
if( g.sslNotAvailable==0
653671
&& strncmp(g.zBaseURL,"https:",6)!=0
654672
&& db_get_boolean("https-login",0)
655673
){
@@ -677,10 +695,21 @@
677695
" AND cap!=''");
678696
}else{
679697
zAnonPw = 0;
680698
}
681699
@ <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
+ }
682711
@ <tr>
683712
@ <td class="form_label" id="userlabel1">User ID:</td>
684713
@ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
685714
@ size="30" value="%s(anonFlag?"anonymous":"")"></td>
686715
@ </tr>
@@ -691,26 +720,19 @@
691720
if( zAnonPw && !noAnon ){
692721
captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
693722
}
694723
@ </td>
695724
@ </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>
709731
@ <tr>
710732
@ <td></td>
711
- @ <td><input type="submit" name="in" value="Login"></td>
733
+ @ <td><input type="submit" name="in" value="Login">
712734
@ </tr>
713735
if( !noAnon && login_self_register_available(0) ){
714736
@ <tr>
715737
@ <td></td>
716738
@ <td><input type="submit" name="self" value="Create A New Account">
@@ -1598,11 +1620,11 @@
15981620
"'%q <%q>\nself-register from ip %q on '||datetime('now'),now())",
15991621
zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr);
16001622
fossil_free(zPass);
16011623
db_multi_exec("%s", blob_sql_text(&sql));
16021624
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);
16041626
if( doAlerts ){
16051627
/* Also make the new user a subscriber. */
16061628
Blob hdr, body;
16071629
AlertSender *pSender;
16081630
sqlite3_int64 id; /* New subscriber Id */
16091631
--- src/login.c
+++ src/login.c
@@ -261,20 +261,27 @@
261 ** and user.cexpire fields for the given user.
262 **
263 ** If zDest is not NULL then the generated cookie is copied to
264 ** *zDdest and ownership is transfered to the caller (who should
265 ** eventually pass it to free()).
 
 
 
 
 
 
266 */
267 void login_set_user_cookie(
268 const char *zUsername, /* User's name */
269 int uid, /* User's ID */
270 char **zDest /* Optional: store generated cookie value. */
 
271 ){
272 const char *zCookieName = login_cookie_name();
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,
@@ -283,18 +290,17 @@
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);
300 }
@@ -306,31 +312,34 @@
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 ){
327 *zCookieDest = zCookie;
328 }else{
329 free(zCookie);
330 }
331
332 }
333
334 /*
335 ** "Unsets" the login cookie (insofar as cookies can be unset) and
336 ** clears the current user's (g.userUid) login information from the
@@ -515,20 +524,22 @@
515 char *zErrMsg = "";
516 int uid; /* User id logged in user */
517 char *zSha1Pw;
518 const char *zIpAddr; /* IP address of requestor */
519 const char *zReferer;
520 int noAnon = P("noanon")!=0;
 
 
 
521
522 login_check_credentials();
523 fossil_redirect_to_https_if_needed(1);
524 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
525 constant_time_cmp_function, 0, 0);
526 zUsername = P("u");
527 zPasswd = P("p");
528 anonFlag = g.zLogin==0 && PB("anon");
529
530 /* Handle log-out requests */
531 if( P("out") ){
532 login_clear_login_data();
533 redirect_to_g();
534 return;
@@ -600,12 +611,18 @@
600 }
601 }
602 zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
603 zReferer = P("HTTP_REFERER");
604 uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs"));
 
 
 
 
 
 
605 if( uid>0 ){
606 login_set_anon_cookie(zIpAddr, NULL);
607 record_login_attempt("anonymous", zIpAddr, 1);
608 redirect_to_g();
609 }
610 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
611 /* Attempting to log in as a user other than anonymous.
@@ -625,11 +642,11 @@
625 ** HASH/PROJECT/LOGIN
626 **
627 ** where HASH is a random hex number, PROJECT is either project
628 ** code prefix, and LOGIN is the user name.
629 */
630 login_set_user_cookie(zUsername, uid, NULL);
631 redirect_to_g();
632 }
633 }
634 style_header("Login/Logout");
635 style_adunit_config(ADUNIT_OFF);
@@ -646,10 +663,11 @@
646 @ <p>Login as <b>anonymous</b> or any named user
647 @ to access page <b>%h(zAbbrev)</b>.
648 }else{
649 @ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
650 }
 
651 }
652 if( g.sslNotAvailable==0
653 && strncmp(g.zBaseURL,"https:",6)!=0
654 && db_get_boolean("https-login",0)
655 ){
@@ -677,10 +695,21 @@
677 " AND cap!=''");
678 }else{
679 zAnonPw = 0;
680 }
681 @ <table class="login_out">
 
 
 
 
 
 
 
 
 
 
 
682 @ <tr>
683 @ <td class="form_label" id="userlabel1">User ID:</td>
684 @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
685 @ size="30" value="%s(anonFlag?"anonymous":"")"></td>
686 @ </tr>
@@ -691,26 +720,19 @@
691 if( zAnonPw && !noAnon ){
692 captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
693 }
694 @ </td>
695 @ </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 }
709 @ <tr>
710 @ <td></td>
711 @ <td><input type="submit" name="in" value="Login"></td>
712 @ </tr>
713 if( !noAnon && login_self_register_available(0) ){
714 @ <tr>
715 @ <td></td>
716 @ <td><input type="submit" name="self" value="Create A New Account">
@@ -1598,11 +1620,11 @@
1598 "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())",
1599 zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr);
1600 fossil_free(zPass);
1601 db_multi_exec("%s", blob_sql_text(&sql));
1602 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID);
1603 login_set_user_cookie(zUserID, uid, NULL);
1604 if( doAlerts ){
1605 /* Also make the new user a subscriber. */
1606 Blob hdr, body;
1607 AlertSender *pSender;
1608 sqlite3_int64 id; /* New subscriber Id */
1609
--- src/login.c
+++ src/login.c
@@ -261,20 +261,27 @@
261 ** and user.cexpire fields for the given user.
262 **
263 ** If zDest is not NULL then the generated cookie is copied to
264 ** *zDdest and ownership is transfered to the caller (who should
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.
272 */
273 void login_set_user_cookie(
274 const char *zUsername, /* User's name */
275 int uid, /* User's ID */
276 char **zDest, /* Optional: store generated cookie value. */
277 int bSessionCookie /* True for session-only cookie */
278 ){
279 const char *zCookieName = login_cookie_name();
280 const char *zExpire = db_get("cookie-expire","8766");
281 const int expires = atoi(zExpire)*3600;
282 char *zHash = 0;
283 char *zCookie;
284 const char *zIpAddr = PD("REMOTE_ADDR","nil"); /* IP address of user */
285
286 assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data.");
287 zHash = db_text(0,
@@ -283,18 +290,17 @@
290 " AND cexpire>julianday('now')"
291 " AND length(cookie)>30",
292 uid);
293 if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))");
294 zCookie = login_gen_user_cookie_value(zUsername, zHash);
295 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(),
296 bSessionCookie ? 0 : expires);
297 record_login_attempt(zUsername, zIpAddr, 1);
298 db_multi_exec("UPDATE user SET cookie=%Q,"
 
299 " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
300 zHash, expires, uid);
301 fossil_free(zHash);
 
302 if( zDest ){
303 *zDest = zCookie;
304 }else{
305 free(zCookie);
306 }
@@ -306,31 +312,34 @@
312 **
313 ** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret.
314 **
315 ** If zCookieDest is not NULL then the generated cookie is assigned to
316 ** *zCookieDest and the caller must eventually free() it.
317 **
318 ** If bSessionCookie is true, the cookie will be a session cookie.
319 */
320 void login_set_anon_cookie(const char *zIpAddr, char **zCookieDest,
321 int bSessionCookie ){
322 const char *zNow; /* Current time (julian day number) */
323 char *zCookie; /* The login cookie */
324 const char *zCookieName; /* Name of the login cookie */
325 Blob b; /* Blob used during cookie construction */
326 int expires = bSessionCookie ? 0 : 6*3600;
327 zCookieName = login_cookie_name();
328 zNow = db_text("0", "SELECT julianday('now')");
329 assert( zCookieName && zNow );
330 blob_init(&b, zNow, -1);
331 blob_appendf(&b, "/%s", db_get("captcha-secret",""));
332 sha1sum_blob(&b, &b);
333 zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
334 blob_reset(&b);
335 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
336 if( zCookieDest ){
337 *zCookieDest = zCookie;
338 }else{
339 free(zCookie);
340 }
 
341 }
342
343 /*
344 ** "Unsets" the login cookie (insofar as cookies can be unset) and
345 ** clears the current user's (g.userUid) login information from the
@@ -515,20 +524,22 @@
524 char *zErrMsg = "";
525 int uid; /* User id logged in user */
526 char *zSha1Pw;
527 const char *zIpAddr; /* IP address of requestor */
528 const char *zReferer;
529 const int noAnon = P("noanon")!=0;
530 int rememberMe; /* If true, use persistent cookie, else
531 session cookie. Toggled per
532 checkbox. */
533
534 login_check_credentials();
535 fossil_redirect_to_https_if_needed(1);
536 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
537 constant_time_cmp_function, 0, 0);
538 zUsername = P("u");
539 zPasswd = P("p");
540 anonFlag = g.zLogin==0 && PB("anon");
 
541 /* Handle log-out requests */
542 if( P("out") ){
543 login_clear_login_data();
544 redirect_to_g();
545 return;
@@ -600,12 +611,18 @@
611 }
612 }
613 zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
614 zReferer = P("HTTP_REFERER");
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 }
622 if( uid>0 ){
623 login_set_anon_cookie(zIpAddr, NULL, rememberMe?0:1);
624 record_login_attempt("anonymous", zIpAddr, 1);
625 redirect_to_g();
626 }
627 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
628 /* Attempting to log in as a user other than anonymous.
@@ -625,11 +642,11 @@
642 ** HASH/PROJECT/LOGIN
643 **
644 ** where HASH is a random hex number, PROJECT is either project
645 ** code prefix, and LOGIN is the user name.
646 */
647 login_set_user_cookie(zUsername, uid, NULL, rememberMe?0:1);
648 redirect_to_g();
649 }
650 }
651 style_header("Login/Logout");
652 style_adunit_config(ADUNIT_OFF);
@@ -646,10 +663,11 @@
663 @ <p>Login as <b>anonymous</b> or any named user
664 @ to access page <b>%h(zAbbrev)</b>.
665 }else{
666 @ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
667 }
668 fossil_free(zAbbrev);
669 }
670 if( g.sslNotAvailable==0
671 && strncmp(g.zBaseURL,"https:",6)!=0
672 && db_get_boolean("https-login",0)
673 ){
@@ -677,10 +695,21 @@
695 " AND cap!=''");
696 }else{
697 zAnonPw = 0;
698 }
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 }
711 @ <tr>
712 @ <td class="form_label" id="userlabel1">User ID:</td>
713 @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
714 @ size="30" value="%s(anonFlag?"anonymous":"")"></td>
715 @ </tr>
@@ -691,26 +720,19 @@
720 if( zAnonPw && !noAnon ){
721 captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
722 }
723 @ </td>
724 @ </tr>
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>
 
 
 
 
 
 
 
731 @ <tr>
732 @ <td></td>
733 @ <td><input type="submit" name="in" value="Login">
734 @ </tr>
735 if( !noAnon && login_self_register_available(0) ){
736 @ <tr>
737 @ <td></td>
738 @ <td><input type="submit" name="self" value="Create A New Account">
@@ -1598,11 +1620,11 @@
1620 "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())",
1621 zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr);
1622 fossil_free(zPass);
1623 db_multi_exec("%s", blob_sql_text(&sql));
1624 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID);
1625 login_set_user_cookie(zUserID, uid, NULL, 0);
1626 if( doAlerts ){
1627 /* Also make the new user a subscriber. */
1628 Blob hdr, body;
1629 AlertSender *pSender;
1630 sqlite3_int64 id; /* New subscriber Id */
1631

Keyboard Shortcuts

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