Diff
Not logged in

Differences From:

File src/login.c part of check-in [355ee47555] - Add the new "reader" role, analogous to "developer". by drh on 2009-03-31 16:47:41. [view]

To:

File src/login.c part of check-in [b4a29fac93] - Add an ascii-art captcha for anonymous login. by drh on 2009-08-10 02:29:14. [view]

@@ -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>&nbsp;</p>
-    }else{
-      @ <p>&nbsp;</p><p>&nbsp;</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;