Fossil

Check-in [f372e189]
Login

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

Overview
Comment:Provide the option to force all web page requests to go over HTTPS.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | https-all-pages-option
Files: files | file ages | folders
SHA3-256: f372e18979177614b552a5ddca34daa96a0a199c811e367442b33f66b3619e3f
User & Date: drh 2019-01-21 17:33:02
Context
2019-01-21
18:05
Fixes to the automatic HTTPS redirector. check-in: 14ff7af4 user: drh tags: https-all-pages-option
17:33
Provide the option to force all web page requests to go over HTTPS. check-in: f372e189 user: drh tags: https-all-pages-option
16:57
Fix a documentation error on the setup_access page. check-in: 742d64d9 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/login.c.

   547    547     int uid;                     /* User id logged in user */
   548    548     char *zSha1Pw;
   549    549     const char *zIpAddr;         /* IP address of requestor */
   550    550     const char *zReferer;
   551    551     int noAnon = P("noanon")!=0;
   552    552   
   553    553     login_check_credentials();
   554         -  if( login_wants_https_redirect() ){
   555         -    const char *zQS = P("QUERY_STRING");
   556         -    if( P("redir")!=0 ){
   557         -      style_header("Insecure Connection");
   558         -      @ <h1>Unable To Establish An Encrypted Connection</h1>
   559         -      @ <p>This website requires that login credentials be sent over
   560         -      @ an encrypted connection.  The current connection is not encrypted
   561         -      @ across the entire route between your browser and the server.
   562         -      @ An attempt was made to redirect to %h(g.zHttpsURL) but
   563         -      @ the connection is still insecure even after the redirect.</p>
   564         -      @ <p>This is probably some kind of configuration problem.  Please
   565         -      @ contact your sysadmin.</p>
   566         -      @ <p>Sorry it did not work out.</p>
   567         -      style_footer();
   568         -      return;
   569         -    }
   570         -    if( zQS==0 ){
   571         -      zQS = "?redir=1";
   572         -    }else if( zQS[0]!=0 ){
   573         -      zQS = mprintf("?%s&redir=1", zQS);
   574         -    }
   575         -    cgi_redirectf("%s%T%s", g.zHttpsURL, P("PATH_INFO"), zQS);
   576         -    return;
   577         -  }
          554  +  fossil_redirect_to_https_if_needed(1);
   578    555     sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
   579    556                     constant_time_cmp_function, 0, 0);
   580    557     zUsername = P("u");
   581    558     zPasswd = P("p");
   582    559     anonFlag = g.zLogin==0 && PB("anon");
   583    560   
   584    561     /* Handle log-out requests */
................................................................................
   912    889       "   AND length(cap)>0"
   913    890       "   AND length(pw)>0"
   914    891       "   AND constant_time_cmp(cookie,%Q)=0",
   915    892       zLogin, zRemoteAddr, zCookie
   916    893     );
   917    894     return uid;
   918    895   }
   919         -
   920         -/*
   921         -** Return true if it is appropriate to redirect login requests to HTTPS.
   922         -**
   923         -** Redirect to https is appropriate if all of the above are true:
   924         -**    (1) The redirect-to-https flag is set
   925         -**    (2) The current connection is http, not https or ssh
   926         -**    (3) The sslNotAvailable flag is clear
   927         -*/
   928         -int login_wants_https_redirect(void){
   929         -  if( g.sslNotAvailable ) return 0;
   930         -  if( db_get_boolean("redirect-to-https",0)==0 ) return 0;
   931         -  if( P("HTTPS")!=0 ) return 0;
   932         -  return 1;
   933         -}
   934         -
   935    896   
   936    897   /*
   937    898   ** Attempt to use Basic Authentication to establish the user.  Return the
   938    899   ** (non-zero) uid if successful.  Return 0 if it does not work.
   939    900   */
   940    901   static int logic_basic_authentication(const char *zIpAddr){
   941    902     const char *zAuth = PD("HTTP_AUTHORIZATION", 0);
................................................................................
  1459   1420     }else
  1460   1421   #endif /* FOSSIL_ENABLE_JSON */
  1461   1422     {
  1462   1423       const char *zUrl = PD("REQUEST_URI", "index");
  1463   1424       const char *zQS = P("QUERY_STRING");
  1464   1425       Blob redir;
  1465   1426       blob_init(&redir, 0, 0);
  1466         -    if( login_wants_https_redirect() && !g.sslNotAvailable ){
         1427  +    if( fossil_wants_https(1) ){
  1467   1428         blob_appendf(&redir, "%s/login?g=%T", g.zHttpsURL, zUrl);
  1468   1429       }else{
  1469   1430         blob_appendf(&redir, "%R/login?g=%T", zUrl);
  1470   1431       }
  1471   1432       if( anonOk ) blob_append(&redir, "&anon", 5);
  1472   1433       if( zQS && zQS[0] ){
  1473   1434         blob_appendf(&redir, "&%s", zQS);

Changes to src/main.c.

  1332   1332     if( g.fAnyTrace ){
  1333   1333       fprintf(stderr,"/***** sigpipe received by subprocess %d ****\n", getpid());
  1334   1334     }
  1335   1335   #endif
  1336   1336     db_panic_close();
  1337   1337     exit(1);
  1338   1338   }
         1339  +
         1340  +/*
         1341  +** Return true if it is appropriate to redirect requests to HTTPS.
         1342  +**
         1343  +** Redirect to https is appropriate if all of the above are true:
         1344  +**    (1) The redirect-to-https flag has a valud of iLevel or greater.
         1345  +**    (2) The current connection is http, not https or ssh
         1346  +**    (3) The sslNotAvailable flag is clear
         1347  +*/
         1348  +int fossil_wants_https(int iLevel){
         1349  +  if( g.sslNotAvailable ) return 0;
         1350  +  if( db_get_int("redirect-to-https",0)<iLevel ) return 0;
         1351  +  if( P("HTTPS")!=0 ) return 0;
         1352  +  return 1;
         1353  +}
         1354  +
         1355  +/*
         1356  +** Redirect to the equivalent HTTPS request if the current connection is
         1357  +** insecure and if the redirect-to-https flag greater than or equal to 
         1358  +** iLevel.  iLevel is 1 for /login pages and 2 for every other page.
         1359  +*/
         1360  +void fossil_redirect_to_https_if_needed(int iLevel){
         1361  +  if( fossil_wants_https(iLevel) ){
         1362  +    const char *zQS = P("QUERY_STRING");
         1363  +    if( P("redir")!=0 ){
         1364  +      style_header("Insecure Connection");
         1365  +      @ <h1>Unable To Establish An Encrypted Connection</h1>
         1366  +      @ <p>This website requires an encrypted connection.
         1367  +      @ The current connection is not encrypted
         1368  +      @ across the entire route between your browser and the server.
         1369  +      @ An attempt was made to redirect to %h(g.zHttpsURL) but
         1370  +      @ the connection is still insecure even after the redirect.</p>
         1371  +      @ <p>This is probably some kind of configuration problem.  Please
         1372  +      @ contact your sysadmin.</p>
         1373  +      @ <p>Sorry it did not work out.</p>
         1374  +      style_footer();
         1375  +      return;
         1376  +    }
         1377  +    if( zQS==0 ){
         1378  +      zQS = "?redir=1";
         1379  +    }else if( zQS[0]!=0 ){
         1380  +      zQS = mprintf("?%s&redir=1", zQS);
         1381  +    }
         1382  +    cgi_redirectf("%s%T%s", g.zHttpsURL, P("PATH_INFO"), zQS);
         1383  +  }
         1384  +}
  1339   1385   
  1340   1386   /*
  1341   1387   ** Preconditions:
  1342   1388   **
  1343   1389   **  * Environment variables are set up according to the CGI standard.
  1344   1390   **
  1345   1391   ** If the repository is known, it has already been opened.  If unknown,
................................................................................
  1602   1648     if( g.zContentType &&
  1603   1649         strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
  1604   1650       /* Special case:  If the content mimetype shows that it is "fossil sync"
  1605   1651       ** payload, then pretend that the PATH_INFO is /xfer so that we always
  1606   1652       ** invoke the sync page. */
  1607   1653       zPathInfo = "/xfer";
  1608   1654     }
         1655  +
         1656  +  /* If the inbound request is unencrypted and if the redirect-to-https
         1657  +  ** setting is 2 or more, then immediately redirect the equivalent HTTPS
         1658  +  ** URI.
         1659  +  */
         1660  +  fossil_redirect_to_https_if_needed(2);
  1609   1661   
  1610   1662     /* Use the first element of PATH_INFO as the page name
  1611   1663     ** and deliver the appropriate page back to the user.
  1612   1664     */
  1613   1665     set_base_url(0);
  1614   1666     if( zPathInfo==0 || zPathInfo[0]==0
  1615   1667         || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){

Changes to src/setup.c.

   282    282   */
   283    283   void multiple_choice_attribute(
   284    284     const char *zLabel,   /* The text label on the menu */
   285    285     const char *zVar,     /* The corresponding row in the VAR table */
   286    286     const char *zQP,      /* The query parameter */
   287    287     const char *zDflt,    /* Default value if VAR table entry does not exist */
   288    288     int nChoice,          /* Number of choices */
   289         -  const char *const *azChoice /* Choices. 2 per choice: (VAR value, Display) */
          289  +  const char *const *azChoice /* Choices in pairs (VAR value, Display) */
   290    290   ){
   291    291     const char *z = db_get(zVar, zDflt);
   292    292     const char *zQ = P(zQP);
   293    293     int i;
   294    294     if( zQ && fossil_strcmp(zQ,z)!=0){
   295    295       const int nZQ = (int)strlen(zQ);
   296    296       login_verify_csrf_secret();
................................................................................
   310    310   
   311    311   /*
   312    312   ** WEBPAGE: setup_access
   313    313   **
   314    314   ** The access-control settings page.  Requires Setup privileges.
   315    315   */
   316    316   void setup_access(void){
          317  +  static const char * const azRedirectOpts[] = {
          318  +    "0", "Off",
          319  +    "1", "Login Page Only",
          320  +    "2", "All Pages"
          321  +  };
   317    322     login_check_credentials();
   318    323     if( !g.perm.Setup ){
   319    324       login_needed(0);
   320    325       return;
   321    326     }
   322    327   
   323    328     style_header("Access Control Settings");
   324    329     db_begin_transaction();
   325    330     @ <form action="%s(g.zTop)/setup_access" method="post"><div>
   326    331     login_insert_csrf_secret();
   327    332     @ <input type="submit"  name="submit" value="Apply Changes" /></p>
   328    333     @ <hr />
   329         -  onoff_attribute("Redirect to HTTPS on the Login page",
   330         -     "redirect-to-https", "redirhttps", 0, 0);
   331         -  @ <p>When selected, force the use of HTTPS for the Login page.
   332         -  @ <p>Details:  When enabled, this option causes the $secureurl TH1
          334  +  multiple_choice_attribute("Redirect to HTTPS",
          335  +     "redirect-to-https", "redirhttps", "0",
          336  +     count(azRedirectOpts)/2, azRedirectOpts);
          337  +  @ <p>Force the use of HTTPS by redirecting to HTTPS when an 
          338  +  @ unencrypted request is received.  This feature can be enabled
          339  +  @ for the Login page only, or for all pages.
          340  +  @ <p>Further details:  When enabled, this option causes the $secureurl TH1
   333    341     @ variable is set to an "https:" variant of $baseurl.  Otherwise,
   334         -  @ $secureurl is just an alias for $baseurl.  Also when enabled, the
   335         -  @ Login page redirects to https if accessed via http.
   336         -  @ (Property: "redirect-to-https")
          342  +  @ $secureurl is just an alias for $baseurl.
          343  +  @ (Property: "redirect-to-https".  "0" for off, "1" for Login page only,
          344  +  @ "2" otherwise.)
   337    345     @ <hr />
   338    346     onoff_attribute("Require password for local access",
   339    347        "localauth", "localauth", 0, 0);
   340    348     @ <p>When enabled, the password sign-in is always required for
   341    349     @ web access.  When disabled, unrestricted web access from 127.0.0.1
   342    350     @ is allowed for the <a href="%R/help/ui">fossil ui</a> command or
   343    351     @ from the <a href="%R/help/server">fossil server</a>,

Changes to src/style.c.

   410    410   */
   411    411   static void style_init_th1_vars(const char *zTitle){
   412    412     Th_Store("nonce", style_nonce());
   413    413     Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
   414    414     Th_Store("project_description", db_get("project-description",""));
   415    415     if( zTitle ) Th_Store("title", zTitle);
   416    416     Th_Store("baseurl", g.zBaseURL);
   417         -  Th_Store("secureurl", login_wants_https_redirect()? g.zHttpsURL: g.zBaseURL);
          417  +  Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
   418    418     Th_Store("home", g.zTop);
   419    419     Th_Store("index_page", db_get("index-page","/home"));
   420    420     if( local_zCurrentPage==0 ) style_set_current_page("%T", g.zPath);
   421    421     Th_Store("current_page", local_zCurrentPage);
   422    422     Th_Store("csrf_token", g.zCsrfToken);
   423    423     Th_Store("release_version", RELEASE_VERSION);
   424    424     Th_Store("manifest_version", MANIFEST_VERSION);
................................................................................
   912    912       }
   913    913     }
   914    914   
   915    915     /* Process through TH1 in order to give an opportunity to substitute
   916    916     ** variables such as $baseurl.
   917    917     */
   918    918     Th_Store("baseurl", g.zBaseURL);
   919         -  Th_Store("secureurl", login_wants_https_redirect()? g.zHttpsURL: g.zBaseURL);
          919  +  Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
   920    920     Th_Store("home", g.zTop);
   921    921     image_url_var("logo");
   922    922     image_url_var("background");
   923    923     Th_Render(blob_str(&css));
   924    924   
   925    925     /* Tell CGI that the content returned by this page is considered cacheable */
   926    926     g.isConst = 1;