Fossil

Check-in [ab0d81f8]
Login

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

Overview
Comment:If the '--cgiupperparamsok' command line option or 'uppercase_params' CGI control line are present, allow parameter names to start with an uppercase letter.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | noJsonCgiFlag
Files: files | file ages | folders
SHA3-256: ab0d81f893b1f60c13cc11be0169685f34cf3c3ea662f808d80c630f6af869a7
User & Date: mistachkin 2019-08-27 05:34:42
Context
2019-08-27
10:40
Query and post parameters may never begin with an upper-case letter. To allow that is a huge security hole. check-in: 72c721ea user: drh tags: noJsonCgiFlag
05:55
More refinements. check-in: c1f4a846 user: mistachkin tags: noJsonCgiFlag
05:34
If the '--cgiupperparamsok' command line option or 'uppercase_params' CGI control line are present, allow parameter names to start with an uppercase letter. check-in: ab0d81f8 user: mistachkin tags: noJsonCgiFlag
04:15
Make it possible to disable JSON auto-detection in the CGI subsystem. check-in: a7754353 user: mistachkin tags: noJsonCgiFlag
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/cgi.c.

560
561
562
563
564
565
566













567
568
569
570
571
572
573
...
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
...
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
...
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
** Add a query parameter.  The zName portion is fixed but a copy
** must be made of zValue.
*/
void cgi_setenv(const char *zName, const char *zValue){
  cgi_set_parameter_nocopy(zName, mprintf("%s",zValue), 0);
}















/*
** Add a list of query parameters or cookies to the parameter set.
**
** Each parameter is of the form NAME=VALUE.  Both the NAME and the
** VALUE may be url-encoded ("+" for space, "%HH" for other special
** characters).  But this routine assumes that NAME contains no
................................................................................
        z++;
      }
      dehttpize(zValue);
    }else{
      if( *z ){ *z++ = 0; }
      zValue = "";
    }
    if( fossil_islower(zName[0]) && fossil_no_strange_characters(zName+1) ){
      cgi_set_parameter_nocopy(zName, zValue, isQP);
    }
#ifdef FOSSIL_ENABLE_JSON
    json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
#endif /* FOSSIL_ENABLE_JSON */
  }
}
................................................................................

  zBoundry = get_line_from_string(&z, &len);
  if( zBoundry==0 ) return;
  while( (zLine = get_line_from_string(&z, &len))!=0 ){
    if( zLine[0]==0 ){
      int nContent = 0;
      zValue = get_bounded_content(&z, &len, zBoundry, &nContent);
      if( zName && zValue && fossil_islower(zName[0]) ){
        cgi_set_parameter_nocopy(zName, zValue, 1);
        if( showBytes ){
          cgi_set_parameter_nocopy(mprintf("%s:bytes", zName),
               mprintf("%d",nContent), 1);
        }
      }
      zName = 0;
................................................................................
        int n = strlen(azArg[i]);
        if( c=='c' && sqlite3_strnicmp(azArg[i],"content-disposition:",n)==0 ){
          i++;
        }else if( c=='n' && sqlite3_strnicmp(azArg[i],"name=",n)==0 ){
          zName = azArg[++i];
        }else if( c=='f' && sqlite3_strnicmp(azArg[i],"filename=",n)==0 ){
          char *z = azArg[++i];
          if( zName && z && fossil_islower(zName[0]) ){
            cgi_set_parameter_nocopy(mprintf("%s:filename",zName), z, 1);
          }
          showBytes = 1;
        }else if( c=='c' && sqlite3_strnicmp(azArg[i],"content-type:",n)==0 ){
          char *z = azArg[++i];
          if( zName && z && fossil_islower(zName[0]) ){
            cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z, 1);
          }
        }
      }
    }
  }
}







>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







|







 







|





|







560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
...
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
...
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
...
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
** Add a query parameter.  The zName portion is fixed but a copy
** must be made of zValue.
*/
void cgi_setenv(const char *zName, const char *zValue){
  cgi_set_parameter_nocopy(zName, mprintf("%s",zValue), 0);
}


/*
** Returns non-zero if the specified character is lower case -OR-
** CGI has been configured to allow uppercase parameter names.
*/
int cgi_char_allowed(char c){
  if( fossil_islower(c) ){
    return 1; /* lowercase letter, always OK */
  }else if( fossil_isupper(c) && g.cgiUpperParamsOk ){
    return 1; /* uppercase letter, OK if allowed explicitly */
  }
  return 0; /* something else, never OK */
}

/*
** Add a list of query parameters or cookies to the parameter set.
**
** Each parameter is of the form NAME=VALUE.  Both the NAME and the
** VALUE may be url-encoded ("+" for space, "%HH" for other special
** characters).  But this routine assumes that NAME contains no
................................................................................
        z++;
      }
      dehttpize(zValue);
    }else{
      if( *z ){ *z++ = 0; }
      zValue = "";
    }
    if( cgi_char_allowed(zName[0]) && fossil_no_strange_characters(zName+1) ){
      cgi_set_parameter_nocopy(zName, zValue, isQP);
    }
#ifdef FOSSIL_ENABLE_JSON
    json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
#endif /* FOSSIL_ENABLE_JSON */
  }
}
................................................................................

  zBoundry = get_line_from_string(&z, &len);
  if( zBoundry==0 ) return;
  while( (zLine = get_line_from_string(&z, &len))!=0 ){
    if( zLine[0]==0 ){
      int nContent = 0;
      zValue = get_bounded_content(&z, &len, zBoundry, &nContent);
      if( zName && zValue && cgi_char_allowed(zName[0]) ){
        cgi_set_parameter_nocopy(zName, zValue, 1);
        if( showBytes ){
          cgi_set_parameter_nocopy(mprintf("%s:bytes", zName),
               mprintf("%d",nContent), 1);
        }
      }
      zName = 0;
................................................................................
        int n = strlen(azArg[i]);
        if( c=='c' && sqlite3_strnicmp(azArg[i],"content-disposition:",n)==0 ){
          i++;
        }else if( c=='n' && sqlite3_strnicmp(azArg[i],"name=",n)==0 ){
          zName = azArg[++i];
        }else if( c=='f' && sqlite3_strnicmp(azArg[i],"filename=",n)==0 ){
          char *z = azArg[++i];
          if( zName && z && cgi_char_allowed(zName[0]) ){
            cgi_set_parameter_nocopy(mprintf("%s:filename",zName), z, 1);
          }
          showBytes = 1;
        }else if( c=='c' && sqlite3_strnicmp(azArg[i],"content-type:",n)==0 ){
          char *z = azArg[++i];
          if( zName && z && cgi_char_allowed(zName[0]) ){
            cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z, 1);
          }
        }
      }
    }
  }
}

Changes to src/dispatch.c.

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
      *(z++) = 0;
      cgi_delete_query_parameter(zName);
      zName = "";
    }else{
      if( *z ){ *z++ = 0; }
      zValue = "";
    }
    if( fossil_islower(zName[0]) ){
      cgi_replace_query_parameter(zName, zValue);
    }
  }
  return 0;
}

/*







|







192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
      *(z++) = 0;
      cgi_delete_query_parameter(zName);
      zName = "";
    }else{
      if( *z ){ *z++ = 0; }
      zValue = "";
    }
    if( cgi_char_allowed(zName[0]) ){
      cgi_replace_query_parameter(zName, zValue);
    }
  }
  return 0;
}

/*

Changes to src/main.c.

209
210
211
212
213
214
215

216
217
218
219
220
221
222
...
728
729
730
731
732
733
734

735
736
737
738
739
740
741
....
2007
2008
2009
2010
2011
2012
2013








2014
2015
2016
2017
2018
2019
2020
  char isHTTP;            /* True if server/CGI modes, else assume CLI. */
  char javascriptHyperlink; /* If true, set href= using script, not HTML */
  Blob httpHeader;        /* Complete text of the HTTP request header */
  UrlData url;            /* Information about current URL */
  const char *zLogin;     /* Login name.  NULL or "" if not logged in. */
  const char *zSSLIdentity;  /* Value of --ssl-identity option, filename of
                             ** SSL client identity */

  int useLocalauth;       /* No login required if from 127.0.0.1 */
  int noPswd;             /* Logged in without password (on 127.0.0.1) */
  int userUid;            /* Integer user id */
  int isHuman;            /* True if access by a human, not a spider or bot */
  int comFmtFlags;        /* Zero or more "COMMENT_PRINT_*" bit flags, should be
                          ** accessed through get_comment_format(). */

................................................................................
      "another flag and is treated as such. --args FILENAME may be used\n"
      "in conjunction with any other flags.\n");
    fossil_exit(1);
  }else{
    const char *zChdir = find_option("chdir",0,1);
    g.isHTTP = 0;
    g.rcvid = 0;

    g.fQuiet = find_option("quiet", 0, 0)!=0;
    g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
    g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
    g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
    g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
    g.fCgiTrace = find_option("cgitrace", 0, 0)!=0;
    g.fSshClient = 0;
................................................................................
  fossil_binary_mode(g.httpIn);
  g.cgiOutput = 1;
  fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT);
  blob_read_from_file(&config, zFile, ExtFILE);
  while( blob_line(&config, &line) ){
    if( !blob_token(&line, &key) ) continue;
    if( blob_buffer(&key)[0]=='#' ) continue;








    if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){
      /* repository: FILENAME
      **
      ** The name of the Fossil repository to be served via CGI.  Most
      ** fossil CGI scripts have a single non-comment line that contains
      ** this one entry.
      */







>







 







>







 







>
>
>
>
>
>
>
>







209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
...
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
....
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
  char isHTTP;            /* True if server/CGI modes, else assume CLI. */
  char javascriptHyperlink; /* If true, set href= using script, not HTML */
  Blob httpHeader;        /* Complete text of the HTTP request header */
  UrlData url;            /* Information about current URL */
  const char *zLogin;     /* Login name.  NULL or "" if not logged in. */
  const char *zSSLIdentity;  /* Value of --ssl-identity option, filename of
                             ** SSL client identity */
  int cgiUpperParamsOk;   /* Allow CGI parameters to start with uppercase */
  int useLocalauth;       /* No login required if from 127.0.0.1 */
  int noPswd;             /* Logged in without password (on 127.0.0.1) */
  int userUid;            /* Integer user id */
  int isHuman;            /* True if access by a human, not a spider or bot */
  int comFmtFlags;        /* Zero or more "COMMENT_PRINT_*" bit flags, should be
                          ** accessed through get_comment_format(). */

................................................................................
      "another flag and is treated as such. --args FILENAME may be used\n"
      "in conjunction with any other flags.\n");
    fossil_exit(1);
  }else{
    const char *zChdir = find_option("chdir",0,1);
    g.isHTTP = 0;
    g.rcvid = 0;
    g.cgiUpperParamsOk = find_option("cgiupperparamsok", 0, 0)!=0;
    g.fQuiet = find_option("quiet", 0, 0)!=0;
    g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
    g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
    g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
    g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
    g.fCgiTrace = find_option("cgitrace", 0, 0)!=0;
    g.fSshClient = 0;
................................................................................
  fossil_binary_mode(g.httpIn);
  g.cgiOutput = 1;
  fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT);
  blob_read_from_file(&config, zFile, ExtFILE);
  while( blob_line(&config, &line) ){
    if( !blob_token(&line, &key) ) continue;
    if( blob_buffer(&key)[0]=='#' ) continue;
    if( blob_eq(&key, "uppercase_params") ){
      /* uppercase_params
      **
      ** Allow CGI parameters to start with an uppercase letter.
      */
      g.cgiUpperParamsOk = 1;
      continue;
    }
    if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){
      /* repository: FILENAME
      **
      ** The name of the Fossil repository to be served via CGI.  Most
      ** fossil CGI scripts have a single non-comment line that contains
      ** this one entry.
      */