@@ -80,8 +80,32 @@
}
}
/*
+** Check to see if the anonymous login is valid. If it is valid, return
+** the userid of the anonymous user.
+*/
+static int isValidAnonymousLogin(
+ const char *zUsername, /* The username. Must be "anonymous" */
+ const char *zPassword /* The supplied password */
+){
+ const char *zCS; /* The captcha seed value */
+ const char *zPw; /* The correct password shown in the captcha */
+ int uid; /* The user ID of anonymous */
+
+ if( zUsername==0 ) return 0;
+ if( zPassword==0 ) return 0;
+ if( strcmp(zUsername,"anonymous")!=0 ) return 0;
+ zCS = P("cs"); /* The "cs" parameter is the "captcha seed" */
+ if( zCS==0 ) return 0;
+ zPw = captcha_decode((unsigned int)atoi(zCS));
+ if( strcasecmp(zPw, zPassword)!=0 ) return 0;
+ uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
+ " AND length(pw)>0 AND length(cap)>0");
+ return uid;
+}
+
+/*
** WEBPAGE: /login
** WEBPAGE: /logout
**
** Generate the login page
@@ -91,8 +115,9 @@
const char *zNew1, *zNew2;
const char *zAnonPw = 0;
int anonFlag;
char *zErrMsg = "";
+ int uid; /* User id loged in user */
login_check_credentials();
zUsername = P("u");
zPasswd = P("p");
@@ -126,10 +151,30 @@
redirect_to_g();
return;
}
}
+ uid = isValidAnonymousLogin(zUsername, zPasswd);
+ if( uid>0 ){
+ char *zNow; /* Current time (julian day number) */
+ const char *zIpAddr; /* IP address of requestor */
+ char *zCookie; /* The login cookie */
+ const char *zCookieName; /* Name of the login cookie */
+ Blob b; /* Blob used during cookie construction */
+
+ zIpAddr = PD("REMOTE_ADDR","nil");
+ zCookieName = login_cookie_name();
+ zNow = db_text("0", "SELECT julianday('now')");
+ blob_init(&b, zNow, -1);
+ blob_appendf(&b, "/%s/%s", zIpAddr, db_get("captcha-secret",""));
+ sha1sum_blob(&b, &b);
+ zCookie = sqlite3_mprintf("anon/%s/%s", zNow, blob_buffer(&b));
+ blob_reset(&b);
+ free(zNow);
+ cgi_set_cookie(zCookieName, zCookie, 0, 6*3600);
+ redirect_to_g();
+ }
if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
- int uid = db_int(0,
+ uid = db_int(0,
"SELECT uid FROM user"
" WHERE login=%Q AND pw=%Q", zUsername, zPasswd);
if( uid<=0 || strcmp(zUsername,"nobody")==0 ){
sleep(1);
@@ -144,19 +189,15 @@
const char *zExpire = db_get("cookie-expire","8766");
int expires = atoi(zExpire)*3600;
const char *zIpAddr = PD("REMOTE_ADDR","nil");
- if( strcmp(zUsername, "anonymous")==0 ){
- cgi_set_cookie(zCookieName, "anonymous", 0, expires);
- }else{
- zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid);
- cgi_set_cookie(zCookieName, zCookie, 0, expires);
- db_multi_exec(
- "UPDATE user SET cookie=%Q, ipaddr=%Q, "
- " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
- zCookie, zIpAddr, expires, uid
- );
- }
+ zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid);
+ cgi_set_cookie(zCookieName, zCookie, 0, expires);
+ db_multi_exec(
+ "UPDATE user SET cookie=%Q, ipaddr=%Q, "
+ " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
+ zCookie, zIpAddr, expires, uid
+ );
redirect_to_g();
}
}
style_header("Login/Logout");
@@ -181,13 +222,8 @@
if( g.zLogin==0 ){
zAnonPw = db_text(0, "SELECT pw FROM user"
" WHERE login='anonymous'"
" AND cap!=''");
- if( zAnonPw && anonFlag ){
- @ <tr><td></td>
- @ <td>The anonymous password is "<b>%h(zAnonPw)</b>".</td>
- @ </tr>
- }
}
@ <tr>
@ <td></td>
@ <td><input type="submit" name="in" value="Login"></td>
@@ -202,15 +238,19 @@
@ enter the user-id and password at the left and press the
@ "Login" button. Your user name will be stored in a browser cookie.
@ You must configure your web browser to accept cookies in order for
@ the login to take.</p>
- if( g.zLogin==0 ){
- if( zAnonPw && !anonFlag ){
- @ <p>The password for user "anonymous" is "<b>%h(zAnonPw)</b>".</p>
- @ <p> </p>
- }else{
- @ <p> </p><p> </p>
- }
+ if( zAnonPw ){
+ unsigned int uSeed = captcha_seed();
+ char *zCaptcha = captcha_render(captcha_decode(uSeed));
+
+ @ <input type="hidden" name="cs" value="%u(uSeed)">
+ @ <p>To login as user <b>anonymous</b> use the following
+ @ 8-character hexadecimal password:</p>
+ @ <center><table border="1" cellpadding="10"><tr><td><pre>
+ @ %s(zCaptcha)
+ @ </pre></td></tr></table></center>
+ free(zCaptcha);
}
if( g.zLogin ){
@ <br clear="both"><hr>
@ <p>To log off the system (and delete your login cookie)
@@ -279,8 +319,10 @@
/* Check the login cookie to see if it matches a known valid user.
*/
if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){
if( isdigit(zCookie[0]) ){
+ /* Cookies of the form "uid/randomness". There must be a
+ ** corresponding entry in the user table. */
uid = db_int(0,
"SELECT uid FROM user"
" WHERE uid=%d"
" AND cookie=%Q"
@@ -287,10 +329,32 @@
" AND ipaddr=%Q"
" AND cexpire>julianday('now')",
atoi(zCookie), zCookie, zRemoteAddr
);
- }else if( zCookie[0]=='a' ){
- uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'");
+ }else if( memcmp(zCookie,"anon/",5)==0 ){
+ /* Cookies of the form "anon/TIME/HASH". The TIME must not be
+ ** too old and the sha1 hash of TIME+IPADDR+SECRET must match HASH.
+ ** SECRET is the "captcha-secret" value in the repository.
+ */
+ double rTime;
+ int i;
+ Blob b;
+ rTime = atof(&zCookie[5]);
+ for(i=5; zCookie[i] && zCookie[i]!='/'; i++){}
+ blob_init(&b, &zCookie[5], i-5);
+ if( zCookie[i]=='/' ){ i++; }
+ blob_append(&b, "/", 1);
+ blob_appendf(&b, "%s/%s", zRemoteAddr, db_get("captcha-secret",""));
+ sha1sum_blob(&b, &b);
+ uid = db_int(0,
+ "SELECT uid FROM user WHERE login='anonymous'"
+ " AND length(cap)>0"
+ " AND length(pw)>0"
+ " AND %f+0.25>julianday('now')"
+ " AND %Q=%Q",
+ rTime, &zCookie[i], blob_buffer(&b)
+ );
+ blob_reset(&b);
}
sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zCookie);
}
@@ -475,10 +539,10 @@
/*
** Before using the results of a form, first call this routine to verify
** that ths Anti-CSRF token is present and is valid. If the Anti-CSRF token
-** is missing or is incorrect, then this emits and error message and never
-** returns.
+** is missing or is incorrect, that indicates a cross-site scripting attach
+** so emits an error message and abort.
*/
void login_verify_csrf_secret(void){
const char *zCsrf; /* The CSRF secret */
if( g.okCsrf ) return;