Changes On Branch dmitry-security
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch dmitry-security Excluding Merge-Ins

This is equivalent to a diff from 3fac77d7b0 to f4eb0f5afc

2011-10-04
15:15
Merge protection against timing attacks into trunk. check-in: d4a341b49d user: dmitry tags: trunk
14:38
Merge trunk into dmitry-security branch. Closed-Leaf check-in: f4eb0f5afc user: dmitry tags: dmitry-security
14:34
Rename constant_time_eq to constant_time_cmp to better indicate that these functions return 0 when values are equal, like memcmp, strcmp, etc., not truth, to avoid possible mistakes. check-in: d244c484e7 user: dmitry tags: dmitry-security
2011-10-03
16:34
Disabling Cache-control: no-store, as it made firefox forget about form field contents on back/forward in history.    Resolution achieved by a minimal consensus at this thread on the mailing list. check-in: 3fac77d7b0 user: viriketo tags: trunk
2011-10-02
13:30
minor hack to name_search() to make it stop searching after it determines there is an ambiguity. check-in: ae64088627 user: stephan tags: trunk

Changes to src/blob.c.

   309    309     szA = blob_size(pA);
   310    310     szB = blob_size(pB);
   311    311     sz = szA<szB ? szA : szB;
   312    312     rc = memcmp(blob_buffer(pA), blob_buffer(pB), sz);
   313    313     if( rc==0 ){
   314    314       rc = szA - szB;
   315    315     }
          316  +  return rc;
          317  +}
          318  +
          319  +/*
          320  +** Compare two blobs in constant time and return zero if they are equal.
          321  +** Constant time comparison only applies for blobs of the same length.
          322  +** If lengths are different, immediately returns 1.
          323  +*/
          324  +int blob_constant_time_cmp(Blob *pA, Blob *pB){
          325  +  int szA, szB, i;
          326  +  unsigned char *buf1, *buf2;
          327  +  unsigned char rc = 0;
          328  +
          329  +  blob_is_init(pA);
          330  +  blob_is_init(pB);
          331  +  szA = blob_size(pA);
          332  +  szB = blob_size(pB);
          333  +  if( szA!=szB || szA==0 ) return 1;
          334  +
          335  +  buf1 = blob_buffer(pA);
          336  +  buf2 = blob_buffer(pB);
          337  +
          338  +  for( i=0; i<szA; i++ ){
          339  +    rc = rc | (buf1[i] ^ buf2[i]);
          340  +  }
          341  +
   316    342     return rc;
   317    343   }
   318    344   
   319    345   /*
   320    346   ** Compare a blob to a string.  Return TRUE if they are equal.
   321    347   */
   322    348   int blob_eq_str(Blob *pBlob, const char *z, int n){

Changes to src/login.c.

   228    228       cgi_set_cookie(zCookieName, "", login_cookie_path(), -86400);
   229    229       redirect_to_g();
   230    230     }
   231    231     if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
   232    232       /* The user requests a password change */
   233    233       zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
   234    234       if( db_int(1, "SELECT 0 FROM user"
   235         -                  " WHERE uid=%d AND (pw=%Q OR pw=%Q)", 
   236         -                  g.userUid, zPasswd, zSha1Pw) ){
          235  +                  " WHERE uid=%d"
          236  +                  " AND (constant_time_cmp(pw,%Q)=0"
          237  +                  "      OR constant_time_cmp(pw,%Q)=0)", 
          238  +                  g.userUid, zSha1Pw, zPasswd) ){
   237    239         sleep(1);
   238    240         zErrMsg = 
   239    241            @ <p><span class="loginError">
   240    242            @ You entered an incorrect old password while attempting to change
   241    243            @ your password.  Your password is unchanged.
   242    244            @ </span></p>
   243    245         ;
................................................................................
   306    308       */
   307    309       zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
   308    310       uid = db_int(0,
   309    311           "SELECT uid FROM user"
   310    312           " WHERE login=%Q"
   311    313           "   AND length(cap)>0 AND length(pw)>0"
   312    314           "   AND login NOT IN ('anonymous','nobody','developer','reader')"
   313         -        "   AND (pw=%Q OR pw=%Q)",
   314         -        zUsername, zPasswd, zSha1Pw
          315  +        "   AND (constant_time_cmp(pw,%Q)=0 OR constant_time_cmp(pw,%Q)=0)",
          316  +        zUsername, zSha1Pw, zPasswd
   315    317       );
   316    318       if( uid<=0 ){
   317    319         sleep(1);
   318    320         zErrMsg = 
   319    321            @ <p><span class="loginError">
   320    322            @ You entered an unknown user or an incorrect password.
   321    323            @ </span></p>
................................................................................
   449    451       @ <tr><td></td>
   450    452       @ <td><input type="submit" value="Change Password" /></td></tr>
   451    453       @ </table>
   452    454       @ </form>
   453    455     }
   454    456     style_footer();
   455    457   }
          458  +
          459  +/*
          460  +** SQL function for constant time comparison of two values.
          461  +** Sets result to 0 if two values are equal.
          462  +*/
          463  +static void constant_time_cmp_function(
          464  + sqlite3_context *context,
          465  + int argc,
          466  + sqlite3_value **argv
          467  +){
          468  +  const unsigned char *buf1, *buf2;
          469  +  int len, i;
          470  +  unsigned char rc = 0;
          471  +
          472  +  assert( argc==2 );
          473  +  len = sqlite3_value_bytes(argv[0]);
          474  +  if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){
          475  +    rc = 1;
          476  +  }else{
          477  +    buf1 = sqlite3_value_text(argv[0]);
          478  +    buf2 = sqlite3_value_text(argv[1]);
          479  +    for( i=0; i<len; i++ ){
          480  +      rc = rc | (buf1[i] ^ buf2[i]);
          481  +    }
          482  +  }
          483  +  sqlite3_result_int(context, rc);
          484  +}
   456    485   
   457    486   /*
   458    487   ** Attempt to find login credentials for user zLogin on a peer repository
   459    488   ** with project code zCode.  Transfer those credentials to the local 
   460    489   ** repository.
   461    490   **
   462    491   ** Return true if a transfer was made and false if not.
................................................................................
   479    508          zCode
   480    509     );
   481    510     if( zOtherRepo==0 ) return 0;  /* No such peer repository */
   482    511   
   483    512     rc = sqlite3_open(zOtherRepo, &pOther);
   484    513     if( rc==SQLITE_OK ){
   485    514       sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0);
          515  +    sqlite3_create_function(pOther, "constant_time_cmp", 2, SQLITE_UTF8, 0,
          516  +		  constant_time_cmp_function, 0, 0);
   486    517       sqlite3_busy_timeout(pOther, 5000);
   487    518       zSQL = mprintf(
   488    519         "SELECT cexpire FROM user"
   489         -      " WHERE cookie=%Q"
          520  +      " WHERE login=%Q"
   490    521         "   AND ipaddr=%Q"
   491         -      "   AND login=%Q"
   492    522         "   AND length(cap)>0"
   493    523         "   AND length(pw)>0"
   494         -      "   AND cexpire>julianday('now')",
   495         -      zHash, zRemoteAddr, zLogin
          524  +      "   AND cexpire>julianday('now')"
          525  +      "   AND constant_time_cmp(cookie,%Q)=0",
          526  +      zLogin, zRemoteAddr, zHash
   496    527       );
   497    528       pStmt = 0;
   498    529       rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0);
   499    530       if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
   500    531         db_multi_exec(
   501    532           "UPDATE user SET cookie=%Q, ipaddr=%Q, cexpire=%.17g"
   502    533           " WHERE login=%Q",
................................................................................
   525    556     if( fossil_strcmp(zLogin, "anonymous")==0 ) return 0;
   526    557     if( fossil_strcmp(zLogin, "nobody")==0 ) return 0;
   527    558     if( fossil_strcmp(zLogin, "developer")==0 ) return 0;
   528    559     if( fossil_strcmp(zLogin, "reader")==0 ) return 0;
   529    560     uid = db_int(0, 
   530    561       "SELECT uid FROM user"
   531    562       " WHERE login=%Q"
   532         -    "   AND cookie=%Q"
   533    563       "   AND ipaddr=%Q"
   534    564       "   AND cexpire>julianday('now')"
   535    565       "   AND length(cap)>0"
   536         -    "   AND length(pw)>0",
   537         -    zLogin, zCookie, zRemoteAddr
          566  +    "   AND length(pw)>0"
          567  +    "   AND constant_time_cmp(cookie,%Q)=0",
          568  +    zLogin, zRemoteAddr, zCookie
   538    569     );
   539    570     return uid;
   540    571   }
   541    572   
   542    573   /*
   543    574   ** This routine examines the login cookie to see if it exists and
   544    575   ** and is valid.  If the login cookie checks out, it then sets 
................................................................................
   552    583     const char *zCookie;          /* Text of the login cookie */
   553    584     const char *zIpAddr;          /* Raw IP address of the requestor */
   554    585     char *zRemoteAddr;            /* Abbreviated IP address of the requestor */
   555    586     const char *zCap = 0;         /* Capability string */
   556    587   
   557    588     /* Only run this check once.  */
   558    589     if( g.userUid!=0 ) return;
          590  +
          591  +  sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
          592  +		  constant_time_cmp_function, 0, 0);
   559    593   
   560    594     /* If the HTTP connection is coming over 127.0.0.1 and if
   561    595     ** local login is disabled and if we are using HTTP and not HTTPS, 
   562    596     ** then there is no need to check user credentials.
   563    597     **
   564    598     ** This feature allows the "fossil ui" command to give the user
   565    599     ** full access rights without having to log in.

Changes to src/xfer.c.

   571    571       db_ephemeral_blob(&q, 0, &pw);
   572    572       szPw = blob_size(&pw);
   573    573       blob_zero(&combined);
   574    574       blob_copy(&combined, pNonce);
   575    575       blob_append(&combined, blob_buffer(&pw), szPw);
   576    576       sha1sum_blob(&combined, &hash);
   577    577       assert( blob_size(&hash)==40 );
   578         -    rc = blob_compare(&hash, pSig);
          578  +    rc = blob_constant_time_cmp(&hash, pSig);
   579    579       blob_reset(&hash);
   580    580       blob_reset(&combined);
   581    581       if( rc!=0 && szPw!=40 ){
   582    582         /* If this server stores cleartext passwords and the password did not
   583    583         ** match, then perhaps the client is sending SHA1 passwords.  Try
   584    584         ** again with the SHA1 password.
   585    585         */
................................................................................
   586    586         const char *zPw = db_column_text(&q, 0);
   587    587         char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin), 0);
   588    588         blob_zero(&combined);
   589    589         blob_copy(&combined, pNonce);
   590    590         blob_append(&combined, zSecret, -1);
   591    591         free(zSecret);
   592    592         sha1sum_blob(&combined, &hash);
   593         -      rc = blob_compare(&hash, pSig);
          593  +      rc = blob_constant_time_cmp(&hash, pSig);
   594    594         blob_reset(&hash);
   595    595         blob_reset(&combined);
   596    596       }
   597    597       if( rc==0 ){
   598    598         const char *zCap;
   599    599         zCap = db_column_text(&q, 1);
   600    600         login_set_capabilities(zCap, 0);