Fossil SCM

Fix a bug in single sign-on. Add comments to the login source code.

drh 2011-04-12 23:37 UTC login-groups
Commit 9df4dcb5e1f3ff64a058c1bd3c41e6325a07a8ca
1 file changed +41 -16
+41 -16
--- src/login.c
+++ src/login.c
@@ -17,16 +17,20 @@
1717
**
1818
** This file contains code for generating the login and logout screens.
1919
**
2020
** Notes:
2121
**
22
-** There are two special-case user-ids: "anonymous" and "nobody".
22
+** There are four special-case user-ids: "anonymous", "nobody",
23
+** "developer" and "reader".
24
+**
2325
** The capabilities of the nobody user are available to anyone,
2426
** regardless of whether or not they are logged in. The capabilities
2527
** of anonymous are only available after logging in, but the login
2628
** screen displays the password for the anonymous login, so this
27
-** should not prevent a human user from doing so.
29
+** should not prevent a human user from doing so. The capabilities
30
+** of developer and reader are inherited by any user that has the
31
+** "v" and "u" capabilities, respectively.
2832
**
2933
** The nobody user has capabilities that you want spiders to have.
3034
** The anonymous user has capabilities that you want people without
3135
** logins to have.
3236
**
@@ -78,11 +82,11 @@
7882
/*
7983
** Return the name of the login cookie.
8084
**
8185
** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX
8286
** where the Xs are the first 16 characters of the login-group-code or
83
-** the project-code if we are not a member of any login-group.
87
+** of the project-code if we are not a member of any login-group.
8488
*/
8589
static char *login_cookie_name(void){
8690
static char *zCookieName = 0;
8791
if( zCookieName==0 ){
8892
zCookieName = db_text(0,
@@ -107,15 +111,14 @@
107111
fossil_redirect_home();
108112
}
109113
}
110114
111115
/*
112
-** The IP address of the client is stored as part of the anonymous
113
-** login cookie for additional security. But some clients are behind
114
-** firewalls that shift the IP address with each HTTP request. To
115
-** allow such (broken) clients to log in, extract just a prefix of the
116
-** IP address.
116
+** The IP address of the client is stored as part of login cookies.
117
+** But some clients are behind firewalls that shift the IP address
118
+** with each HTTP request. To allow such (broken) clients to log in,
119
+** extract just a prefix of the IP address.
117120
*/
118121
static char *ipPrefix(const char *zIP){
119122
int i, j;
120123
for(i=j=0; zIP[i]; i++){
121124
if( zIP[i]=='.' ){
@@ -125,11 +128,12 @@
125128
}
126129
return mprintf("%.*s", i, zIP);
127130
}
128131
129132
/*
130
-** Return an abbreviated project code.
133
+** Return an abbreviated project code. The abbreviation is the first
134
+** 16 characters of the project code.
131135
**
132136
** Memory is obtained from malloc.
133137
*/
134138
static char *abbreviated_project_code(const char *zFullCode){
135139
return mprintf("%.16s", zFullCode);
@@ -217,15 +221,17 @@
217221
login_check_credentials();
218222
zUsername = P("u");
219223
zPasswd = P("p");
220224
anonFlag = P("anon")!=0;
221225
if( P("out")!=0 ){
226
+ /* To logout, change the cookie value to an empty string */
222227
const char *zCookieName = login_cookie_name();
223228
cgi_set_cookie(zCookieName, "", login_cookie_path(), -86400);
224229
redirect_to_g();
225230
}
226231
if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
232
+ /* The user requests a password change */
227233
zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
228234
if( db_int(1, "SELECT 0 FROM user"
229235
" WHERE uid=%d AND (pw=%Q OR pw=%Q)",
230236
g.userUid, zPasswd, zSha1Pw) ){
231237
sleep(1);
@@ -264,14 +270,22 @@
264270
redirect_to_g();
265271
return;
266272
}
267273
}
268274
}
269
- zIpAddr = PD("REMOTE_ADDR","nil");
270
- zRemoteAddr = ipPrefix(zIpAddr);
275
+ zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
276
+ zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
271277
uid = isValidAnonymousLogin(zUsername, zPasswd);
272278
if( uid>0 ){
279
+ /* Successful login as anonymous. Set a cookie that looks like
280
+ ** this:
281
+ **
282
+ ** HASH/TIME/anonymous
283
+ **
284
+ ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
285
+ ** is the abbreviated IP address and SECRET is captcha-secret.
286
+ */
273287
char *zNow; /* Current time (julian day number) */
274288
char *zCookie; /* The login cookie */
275289
const char *zCookieName; /* Name of the login cookie */
276290
Blob b; /* Blob used during cookie construction */
277291
@@ -286,10 +300,12 @@
286300
cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
287301
record_login_attempt("anonymous", zIpAddr, 1);
288302
redirect_to_g();
289303
}
290304
if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
305
+ /* Attempting to log in as a user other than anonymous.
306
+ */
291307
zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
292308
uid = db_int(0,
293309
"SELECT uid FROM user"
294310
" WHERE login=%Q"
295311
" AND length(cap)>0 AND length(pw)>0"
@@ -304,10 +320,17 @@
304320
@ You entered an unknown user or an incorrect password.
305321
@ </span></p>
306322
;
307323
record_login_attempt(zUsername, zIpAddr, 0);
308324
}else{
325
+ /* Non-anonymous login is successful. Set a cookie of the form:
326
+ **
327
+ ** HASH/PROJECT/LOGIN
328
+ **
329
+ ** where HASH is a random hex number, PROJECT is either project
330
+ ** code prefix, and LOGIN is the user name.
331
+ */
309332
char *zCookie;
310333
const char *zCookieName = login_cookie_name();
311334
const char *zExpire = db_get("cookie-expire","8766");
312335
int expires = atoi(zExpire)*3600;
313336
char *zCode = abbreviated_project_code(db_get("project-code",""));
@@ -453,11 +476,11 @@
453476
454477
zOtherRepo = db_text(0,
455478
"SELECT value FROM config WHERE name='peer-repo-%q'",
456479
zCode
457480
);
458
- if( zOtherRepo==0 ) return 0;
481
+ if( zOtherRepo==0 ) return 0; /* No such peer repository */
459482
460483
rc = sqlite3_open(zOtherRepo, &pOther);
461484
if( rc==SQLITE_OK ){
462485
zSQL = mprintf(
463486
"SELECT cexpire FROM user"
@@ -512,16 +535,16 @@
512535
zLogin, zCookie, zRemoteAddr
513536
);
514537
return uid;
515538
}
516539
517
-
518
-
519540
/*
520541
** This routine examines the login cookie to see if it exists and
521542
** and is valid. If the login cookie checks out, it then sets
522
-** g.zUserUuid appropriately.
543
+** global variables appropriately. Global variables set include
544
+** g.userUid and g.zLogin and of the g.okRead family of permission
545
+** booleans.
523546
**
524547
*/
525548
void login_check_credentials(void){
526549
int uid = 0; /* User id */
527550
const char *zCookie; /* Text of the login cookie */
@@ -529,15 +552,16 @@
529552
const char *zCap = 0; /* Capability string */
530553
531554
/* Only run this check once. */
532555
if( g.userUid!=0 ) return;
533556
534
-
535557
/* If the HTTP connection is coming over 127.0.0.1 and if
536558
** local login is disabled and if we are using HTTP and not HTTPS,
537559
** then there is no need to check user credentials.
538560
**
561
+ ** This feature allows the "fossil ui" command to give the user
562
+ ** full access rights without having to log in.
539563
*/
540564
zRemoteAddr = ipPrefix(PD("REMOTE_ADDR","nil"));
541565
if( strcmp(zRemoteAddr, "127.0.0.1")==0
542566
&& g.useLocalauth
543567
&& db_get_int("localauth",0)==0
@@ -1200,10 +1224,11 @@
12001224
12011225
/* Create all the necessary CONFIG table entries on both the
12021226
** other repository and on our own repository.
12031227
*/
12041228
zSelfProjCode = abbreviated_project_code(zSelfProjCode);
1229
+ zOtherProjCode = abbreviated_project_code(zOtherProjCode);
12051230
db_begin_transaction();
12061231
db_multi_exec(
12071232
"DELETE FROM %s.config WHERE name GLOB 'peer-*';"
12081233
"INSERT INTO %s.config(name,value) VALUES('peer-repo-%s',%Q);"
12091234
"INSERT INTO %s.config(name,value) "
12101235
--- src/login.c
+++ src/login.c
@@ -17,16 +17,20 @@
17 **
18 ** This file contains code for generating the login and logout screens.
19 **
20 ** Notes:
21 **
22 ** There are two special-case user-ids: "anonymous" and "nobody".
 
 
23 ** The capabilities of the nobody user are available to anyone,
24 ** regardless of whether or not they are logged in. The capabilities
25 ** of anonymous are only available after logging in, but the login
26 ** screen displays the password for the anonymous login, so this
27 ** should not prevent a human user from doing so.
 
 
28 **
29 ** The nobody user has capabilities that you want spiders to have.
30 ** The anonymous user has capabilities that you want people without
31 ** logins to have.
32 **
@@ -78,11 +82,11 @@
78 /*
79 ** Return the name of the login cookie.
80 **
81 ** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX
82 ** where the Xs are the first 16 characters of the login-group-code or
83 ** the project-code if we are not a member of any login-group.
84 */
85 static char *login_cookie_name(void){
86 static char *zCookieName = 0;
87 if( zCookieName==0 ){
88 zCookieName = db_text(0,
@@ -107,15 +111,14 @@
107 fossil_redirect_home();
108 }
109 }
110
111 /*
112 ** The IP address of the client is stored as part of the anonymous
113 ** login cookie for additional security. But some clients are behind
114 ** firewalls that shift the IP address with each HTTP request. To
115 ** allow such (broken) clients to log in, extract just a prefix of the
116 ** IP address.
117 */
118 static char *ipPrefix(const char *zIP){
119 int i, j;
120 for(i=j=0; zIP[i]; i++){
121 if( zIP[i]=='.' ){
@@ -125,11 +128,12 @@
125 }
126 return mprintf("%.*s", i, zIP);
127 }
128
129 /*
130 ** Return an abbreviated project code.
 
131 **
132 ** Memory is obtained from malloc.
133 */
134 static char *abbreviated_project_code(const char *zFullCode){
135 return mprintf("%.16s", zFullCode);
@@ -217,15 +221,17 @@
217 login_check_credentials();
218 zUsername = P("u");
219 zPasswd = P("p");
220 anonFlag = P("anon")!=0;
221 if( P("out")!=0 ){
 
222 const char *zCookieName = login_cookie_name();
223 cgi_set_cookie(zCookieName, "", login_cookie_path(), -86400);
224 redirect_to_g();
225 }
226 if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
 
227 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
228 if( db_int(1, "SELECT 0 FROM user"
229 " WHERE uid=%d AND (pw=%Q OR pw=%Q)",
230 g.userUid, zPasswd, zSha1Pw) ){
231 sleep(1);
@@ -264,14 +270,22 @@
264 redirect_to_g();
265 return;
266 }
267 }
268 }
269 zIpAddr = PD("REMOTE_ADDR","nil");
270 zRemoteAddr = ipPrefix(zIpAddr);
271 uid = isValidAnonymousLogin(zUsername, zPasswd);
272 if( uid>0 ){
 
 
 
 
 
 
 
 
273 char *zNow; /* Current time (julian day number) */
274 char *zCookie; /* The login cookie */
275 const char *zCookieName; /* Name of the login cookie */
276 Blob b; /* Blob used during cookie construction */
277
@@ -286,10 +300,12 @@
286 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
287 record_login_attempt("anonymous", zIpAddr, 1);
288 redirect_to_g();
289 }
290 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
 
 
291 zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
292 uid = db_int(0,
293 "SELECT uid FROM user"
294 " WHERE login=%Q"
295 " AND length(cap)>0 AND length(pw)>0"
@@ -304,10 +320,17 @@
304 @ You entered an unknown user or an incorrect password.
305 @ </span></p>
306 ;
307 record_login_attempt(zUsername, zIpAddr, 0);
308 }else{
 
 
 
 
 
 
 
309 char *zCookie;
310 const char *zCookieName = login_cookie_name();
311 const char *zExpire = db_get("cookie-expire","8766");
312 int expires = atoi(zExpire)*3600;
313 char *zCode = abbreviated_project_code(db_get("project-code",""));
@@ -453,11 +476,11 @@
453
454 zOtherRepo = db_text(0,
455 "SELECT value FROM config WHERE name='peer-repo-%q'",
456 zCode
457 );
458 if( zOtherRepo==0 ) return 0;
459
460 rc = sqlite3_open(zOtherRepo, &pOther);
461 if( rc==SQLITE_OK ){
462 zSQL = mprintf(
463 "SELECT cexpire FROM user"
@@ -512,16 +535,16 @@
512 zLogin, zCookie, zRemoteAddr
513 );
514 return uid;
515 }
516
517
518
519 /*
520 ** This routine examines the login cookie to see if it exists and
521 ** and is valid. If the login cookie checks out, it then sets
522 ** g.zUserUuid appropriately.
 
 
523 **
524 */
525 void login_check_credentials(void){
526 int uid = 0; /* User id */
527 const char *zCookie; /* Text of the login cookie */
@@ -529,15 +552,16 @@
529 const char *zCap = 0; /* Capability string */
530
531 /* Only run this check once. */
532 if( g.userUid!=0 ) return;
533
534
535 /* If the HTTP connection is coming over 127.0.0.1 and if
536 ** local login is disabled and if we are using HTTP and not HTTPS,
537 ** then there is no need to check user credentials.
538 **
 
 
539 */
540 zRemoteAddr = ipPrefix(PD("REMOTE_ADDR","nil"));
541 if( strcmp(zRemoteAddr, "127.0.0.1")==0
542 && g.useLocalauth
543 && db_get_int("localauth",0)==0
@@ -1200,10 +1224,11 @@
1200
1201 /* Create all the necessary CONFIG table entries on both the
1202 ** other repository and on our own repository.
1203 */
1204 zSelfProjCode = abbreviated_project_code(zSelfProjCode);
 
1205 db_begin_transaction();
1206 db_multi_exec(
1207 "DELETE FROM %s.config WHERE name GLOB 'peer-*';"
1208 "INSERT INTO %s.config(name,value) VALUES('peer-repo-%s',%Q);"
1209 "INSERT INTO %s.config(name,value) "
1210
--- src/login.c
+++ src/login.c
@@ -17,16 +17,20 @@
17 **
18 ** This file contains code for generating the login and logout screens.
19 **
20 ** Notes:
21 **
22 ** There are four special-case user-ids: "anonymous", "nobody",
23 ** "developer" and "reader".
24 **
25 ** The capabilities of the nobody user are available to anyone,
26 ** regardless of whether or not they are logged in. The capabilities
27 ** of anonymous are only available after logging in, but the login
28 ** screen displays the password for the anonymous login, so this
29 ** should not prevent a human user from doing so. The capabilities
30 ** of developer and reader are inherited by any user that has the
31 ** "v" and "u" capabilities, respectively.
32 **
33 ** The nobody user has capabilities that you want spiders to have.
34 ** The anonymous user has capabilities that you want people without
35 ** logins to have.
36 **
@@ -78,11 +82,11 @@
82 /*
83 ** Return the name of the login cookie.
84 **
85 ** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX
86 ** where the Xs are the first 16 characters of the login-group-code or
87 ** of the project-code if we are not a member of any login-group.
88 */
89 static char *login_cookie_name(void){
90 static char *zCookieName = 0;
91 if( zCookieName==0 ){
92 zCookieName = db_text(0,
@@ -107,15 +111,14 @@
111 fossil_redirect_home();
112 }
113 }
114
115 /*
116 ** The IP address of the client is stored as part of login cookies.
117 ** But some clients are behind firewalls that shift the IP address
118 ** with each HTTP request. To allow such (broken) clients to log in,
119 ** extract just a prefix of the IP address.
 
120 */
121 static char *ipPrefix(const char *zIP){
122 int i, j;
123 for(i=j=0; zIP[i]; i++){
124 if( zIP[i]=='.' ){
@@ -125,11 +128,12 @@
128 }
129 return mprintf("%.*s", i, zIP);
130 }
131
132 /*
133 ** Return an abbreviated project code. The abbreviation is the first
134 ** 16 characters of the project code.
135 **
136 ** Memory is obtained from malloc.
137 */
138 static char *abbreviated_project_code(const char *zFullCode){
139 return mprintf("%.16s", zFullCode);
@@ -217,15 +221,17 @@
221 login_check_credentials();
222 zUsername = P("u");
223 zPasswd = P("p");
224 anonFlag = P("anon")!=0;
225 if( P("out")!=0 ){
226 /* To logout, change the cookie value to an empty string */
227 const char *zCookieName = login_cookie_name();
228 cgi_set_cookie(zCookieName, "", login_cookie_path(), -86400);
229 redirect_to_g();
230 }
231 if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
232 /* The user requests a password change */
233 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
234 if( db_int(1, "SELECT 0 FROM user"
235 " WHERE uid=%d AND (pw=%Q OR pw=%Q)",
236 g.userUid, zPasswd, zSha1Pw) ){
237 sleep(1);
@@ -264,14 +270,22 @@
270 redirect_to_g();
271 return;
272 }
273 }
274 }
275 zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
276 zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
277 uid = isValidAnonymousLogin(zUsername, zPasswd);
278 if( uid>0 ){
279 /* Successful login as anonymous. Set a cookie that looks like
280 ** this:
281 **
282 ** HASH/TIME/anonymous
283 **
284 ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
285 ** is the abbreviated IP address and SECRET is captcha-secret.
286 */
287 char *zNow; /* Current time (julian day number) */
288 char *zCookie; /* The login cookie */
289 const char *zCookieName; /* Name of the login cookie */
290 Blob b; /* Blob used during cookie construction */
291
@@ -286,10 +300,12 @@
300 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
301 record_login_attempt("anonymous", zIpAddr, 1);
302 redirect_to_g();
303 }
304 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
305 /* Attempting to log in as a user other than anonymous.
306 */
307 zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
308 uid = db_int(0,
309 "SELECT uid FROM user"
310 " WHERE login=%Q"
311 " AND length(cap)>0 AND length(pw)>0"
@@ -304,10 +320,17 @@
320 @ You entered an unknown user or an incorrect password.
321 @ </span></p>
322 ;
323 record_login_attempt(zUsername, zIpAddr, 0);
324 }else{
325 /* Non-anonymous login is successful. Set a cookie of the form:
326 **
327 ** HASH/PROJECT/LOGIN
328 **
329 ** where HASH is a random hex number, PROJECT is either project
330 ** code prefix, and LOGIN is the user name.
331 */
332 char *zCookie;
333 const char *zCookieName = login_cookie_name();
334 const char *zExpire = db_get("cookie-expire","8766");
335 int expires = atoi(zExpire)*3600;
336 char *zCode = abbreviated_project_code(db_get("project-code",""));
@@ -453,11 +476,11 @@
476
477 zOtherRepo = db_text(0,
478 "SELECT value FROM config WHERE name='peer-repo-%q'",
479 zCode
480 );
481 if( zOtherRepo==0 ) return 0; /* No such peer repository */
482
483 rc = sqlite3_open(zOtherRepo, &pOther);
484 if( rc==SQLITE_OK ){
485 zSQL = mprintf(
486 "SELECT cexpire FROM user"
@@ -512,16 +535,16 @@
535 zLogin, zCookie, zRemoteAddr
536 );
537 return uid;
538 }
539
 
 
540 /*
541 ** This routine examines the login cookie to see if it exists and
542 ** and is valid. If the login cookie checks out, it then sets
543 ** global variables appropriately. Global variables set include
544 ** g.userUid and g.zLogin and of the g.okRead family of permission
545 ** booleans.
546 **
547 */
548 void login_check_credentials(void){
549 int uid = 0; /* User id */
550 const char *zCookie; /* Text of the login cookie */
@@ -529,15 +552,16 @@
552 const char *zCap = 0; /* Capability string */
553
554 /* Only run this check once. */
555 if( g.userUid!=0 ) return;
556
 
557 /* If the HTTP connection is coming over 127.0.0.1 and if
558 ** local login is disabled and if we are using HTTP and not HTTPS,
559 ** then there is no need to check user credentials.
560 **
561 ** This feature allows the "fossil ui" command to give the user
562 ** full access rights without having to log in.
563 */
564 zRemoteAddr = ipPrefix(PD("REMOTE_ADDR","nil"));
565 if( strcmp(zRemoteAddr, "127.0.0.1")==0
566 && g.useLocalauth
567 && db_get_int("localauth",0)==0
@@ -1200,10 +1224,11 @@
1224
1225 /* Create all the necessary CONFIG table entries on both the
1226 ** other repository and on our own repository.
1227 */
1228 zSelfProjCode = abbreviated_project_code(zSelfProjCode);
1229 zOtherProjCode = abbreviated_project_code(zOtherProjCode);
1230 db_begin_transaction();
1231 db_multi_exec(
1232 "DELETE FROM %s.config WHERE name GLOB 'peer-*';"
1233 "INSERT INTO %s.config(name,value) VALUES('peer-repo-%s',%Q);"
1234 "INSERT INTO %s.config(name,value) "
1235

Keyboard Shortcuts

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