Fossil SCM

Update the CGI extension documentation. Improved filename security in the CGI extension subsystem.

drh 2019-07-27 15:30 trunk
Commit 12c8cc709d3376ba65439b4c240c6683116215f287ea6631d3bf0e893c9c9b37
2 files changed +13 -12 +22 -2
+13 -12
--- src/extcgi.c
+++ src/extcgi.c
@@ -110,13 +110,15 @@
110110
FILE *toChild = 0; /* FILE for sending to child */
111111
FILE *fromChild = 0; /* FILE for reading from child */
112112
int pidChild = 0; /* Process id of the child */
113113
int rc; /* Reply code from subroutine call */
114114
int nContent = -1; /* Content length */
115
+ const char *zPathInfo; /* Original PATH_INFO value */
115116
Blob reply; /* The reply */
116117
char zLine[1000]; /* One line of the CGI reply */
117118
119
+ zPathInfo = P("PATH_INFO");
118120
login_check_credentials();
119121
blob_init(&reply, 0, 0);
120122
if( g.zExtRoot==0 ){
121123
zFailReason = "extroot is not set";
122124
goto ext_not_found;
@@ -127,13 +129,20 @@
127129
}
128130
if( zName==0 ){
129131
zFailReason = "no path beyond /ext";
130132
goto ext_not_found;
131133
}
132
- if( zName[0]=='.' || zName[0]=='-' ){
133
- zFailReason = "path element begins with '.' or '-'";
134
- goto ext_not_found;
134
+ for(i=0; zName[i]; i++){
135
+ char c = zName[i];
136
+ if( (c=='.' || c=='-') && (i==0 || zName[i-1]=='/') ){
137
+ zFailReason = "path element begins with '.' or '-'";
138
+ goto ext_not_found;
139
+ }
140
+ if( !fossil_isalnum(c) && c!='_' && c!='-' && c!='.' && c!='/' ){
141
+ zFailReason = "illegal character in path";
142
+ goto ext_not_found;
143
+ }
135144
}
136145
if( file_isdir(g.zExtRoot,ExtFILE)!=1 ){
137146
zFailReason = "extroot is not a directory";
138147
goto ext_not_found;
139148
}
@@ -143,18 +152,10 @@
143152
nScript = (int)strlen(zPath);
144153
zScript = zPath;
145154
}else{
146155
for(i=nRoot+1; zPath[i]; i++){
147156
char c = zPath[i];
148
- if( (c=='.' || c=='-') && zPath[i-1]=='/' ){
149
- zFailReason = "path element begins with '.' or '-'";
150
- goto ext_not_found;
151
- }
152
- if( !fossil_isalnum(c) && c!='_' && c!='-' && c!='.' && c!='/' ){
153
- zFailReason = "illegal character in path";
154
- goto ext_not_found;
155
- }
156157
if( c=='/' ){
157158
int isDir, isFile;
158159
zPath[i] = 0;
159160
isDir = file_isdir(zPath, ExtFILE);
160161
isFile = isDir==2 ? file_isfile(zPath, ExtFILE) : 0;
@@ -282,12 +283,12 @@
282283
if( zFailReason==0 ){
283284
document_render(&reply, zMime, zName, zName);
284285
}else{
285286
cgi_set_status(404, "Not Found");
286287
@ <h1>Not Found</h1>
287
- @ <p>Page not found: %h(g.zPath)</p>
288
+ @ <p>Page not found: %h(zPathInfo)</p>
288289
if( g.perm.Debug ){
289290
@ <p>Reason for failure: %h(zFailReason)</p>
290291
}
291292
}
292293
return;
293294
}
294295
--- src/extcgi.c
+++ src/extcgi.c
@@ -110,13 +110,15 @@
110 FILE *toChild = 0; /* FILE for sending to child */
111 FILE *fromChild = 0; /* FILE for reading from child */
112 int pidChild = 0; /* Process id of the child */
113 int rc; /* Reply code from subroutine call */
114 int nContent = -1; /* Content length */
 
115 Blob reply; /* The reply */
116 char zLine[1000]; /* One line of the CGI reply */
117
 
118 login_check_credentials();
119 blob_init(&reply, 0, 0);
120 if( g.zExtRoot==0 ){
121 zFailReason = "extroot is not set";
122 goto ext_not_found;
@@ -127,13 +129,20 @@
127 }
128 if( zName==0 ){
129 zFailReason = "no path beyond /ext";
130 goto ext_not_found;
131 }
132 if( zName[0]=='.' || zName[0]=='-' ){
133 zFailReason = "path element begins with '.' or '-'";
134 goto ext_not_found;
 
 
 
 
 
 
 
135 }
136 if( file_isdir(g.zExtRoot,ExtFILE)!=1 ){
137 zFailReason = "extroot is not a directory";
138 goto ext_not_found;
139 }
@@ -143,18 +152,10 @@
143 nScript = (int)strlen(zPath);
144 zScript = zPath;
145 }else{
146 for(i=nRoot+1; zPath[i]; i++){
147 char c = zPath[i];
148 if( (c=='.' || c=='-') && zPath[i-1]=='/' ){
149 zFailReason = "path element begins with '.' or '-'";
150 goto ext_not_found;
151 }
152 if( !fossil_isalnum(c) && c!='_' && c!='-' && c!='.' && c!='/' ){
153 zFailReason = "illegal character in path";
154 goto ext_not_found;
155 }
156 if( c=='/' ){
157 int isDir, isFile;
158 zPath[i] = 0;
159 isDir = file_isdir(zPath, ExtFILE);
160 isFile = isDir==2 ? file_isfile(zPath, ExtFILE) : 0;
@@ -282,12 +283,12 @@
282 if( zFailReason==0 ){
283 document_render(&reply, zMime, zName, zName);
284 }else{
285 cgi_set_status(404, "Not Found");
286 @ <h1>Not Found</h1>
287 @ <p>Page not found: %h(g.zPath)</p>
288 if( g.perm.Debug ){
289 @ <p>Reason for failure: %h(zFailReason)</p>
290 }
291 }
292 return;
293 }
294
--- src/extcgi.c
+++ src/extcgi.c
@@ -110,13 +110,15 @@
110 FILE *toChild = 0; /* FILE for sending to child */
111 FILE *fromChild = 0; /* FILE for reading from child */
112 int pidChild = 0; /* Process id of the child */
113 int rc; /* Reply code from subroutine call */
114 int nContent = -1; /* Content length */
115 const char *zPathInfo; /* Original PATH_INFO value */
116 Blob reply; /* The reply */
117 char zLine[1000]; /* One line of the CGI reply */
118
119 zPathInfo = P("PATH_INFO");
120 login_check_credentials();
121 blob_init(&reply, 0, 0);
122 if( g.zExtRoot==0 ){
123 zFailReason = "extroot is not set";
124 goto ext_not_found;
@@ -127,13 +129,20 @@
129 }
130 if( zName==0 ){
131 zFailReason = "no path beyond /ext";
132 goto ext_not_found;
133 }
134 for(i=0; zName[i]; i++){
135 char c = zName[i];
136 if( (c=='.' || c=='-') && (i==0 || zName[i-1]=='/') ){
137 zFailReason = "path element begins with '.' or '-'";
138 goto ext_not_found;
139 }
140 if( !fossil_isalnum(c) && c!='_' && c!='-' && c!='.' && c!='/' ){
141 zFailReason = "illegal character in path";
142 goto ext_not_found;
143 }
144 }
145 if( file_isdir(g.zExtRoot,ExtFILE)!=1 ){
146 zFailReason = "extroot is not a directory";
147 goto ext_not_found;
148 }
@@ -143,18 +152,10 @@
152 nScript = (int)strlen(zPath);
153 zScript = zPath;
154 }else{
155 for(i=nRoot+1; zPath[i]; i++){
156 char c = zPath[i];
 
 
 
 
 
 
 
 
157 if( c=='/' ){
158 int isDir, isFile;
159 zPath[i] = 0;
160 isDir = file_isdir(zPath, ExtFILE);
161 isFile = isDir==2 ? file_isfile(zPath, ExtFILE) : 0;
@@ -282,12 +283,12 @@
283 if( zFailReason==0 ){
284 document_render(&reply, zMime, zName, zName);
285 }else{
286 cgi_set_status(404, "Not Found");
287 @ <h1>Not Found</h1>
288 @ <p>Page not found: %h(zPathInfo)</p>
289 if( g.perm.Debug ){
290 @ <p>Reason for failure: %h(zFailReason)</p>
291 }
292 }
293 return;
294 }
295
--- www/serverext.wiki
+++ www/serverext.wiki
@@ -80,11 +80,11 @@
8080
that file to be executable, so it runs it as CGI and returns the result.
8181
8282
The /sqlite-src-ext/checklist file is a
8383
[https://wapp.tcl.tk|Wapp program]. The complete source code to the
8484
this program can be seen at
85
-[https://www.sqlite.org/checklistapp/file/checklist.tcl].
85
+[https://www.sqlite.org/src/ext/checklist/self].
8686
8787
There is a cascade of CGIs happening here. The webserver that receives
8888
the initial HTTP request runs Fossil as a CGI based on the
8989
"https://sqlite.org/src" portion of the URL. The Fossil instance then
9090
runs the checklist sub-CGI based on the "/ext/checklists" suffix. The
@@ -213,11 +213,31 @@
213213
214214
Except for the three cases noted above, Fossil makes no changes or
215215
additions to the CGI-generated content, but simply passes the verbatim
216216
content back up the stack towards the requester.
217217
218
-<h2>5.0 Trouble-Shooting Hints</h2>
218
+<h2>5.0 Filename Restrictions</h2>
219
+
220
+For security reasons, Fossil places restrictions on the names of files
221
+in the extroot directory that can participate in the extension CGI
222
+mechanism:
223
+
224
+ 1. Filenames must consist of only ASCII alphanumeric characters,
225
+ ".", "_", and "-", and of course "/" as the file separator.
226
+ Files with names that includes spaces or
227
+ other punctuation or special characters are ignored.
228
+
229
+ 2. No element of the pathname can begin with "." or "-". Files or
230
+ directories whose names begin with "." or "-" are ignored.
231
+
232
+If a CGI program requires separate data files, it is safe to put those
233
+files in the same directory as the CGI program itself as long as the names
234
+of the data files contain special characters that cause them to be ignored
235
+by Fossil. For example, ensure that all datafile begin with "-" or
236
+end with ",data" or "~data".
237
+
238
+<h2>6.0 Trouble-Shooting Hints</h2>
219239
220240
Remember that the /ext will return any file in the extroot directory
221241
hierarchy as static content if the file is readable but not executable.
222242
When initially setting up the /ext mechanism, it is sometimes helpful
223243
to verify that you are able to receive static content prior to working
224244
--- www/serverext.wiki
+++ www/serverext.wiki
@@ -80,11 +80,11 @@
80 that file to be executable, so it runs it as CGI and returns the result.
81
82 The /sqlite-src-ext/checklist file is a
83 [https://wapp.tcl.tk|Wapp program]. The complete source code to the
84 this program can be seen at
85 [https://www.sqlite.org/checklistapp/file/checklist.tcl].
86
87 There is a cascade of CGIs happening here. The webserver that receives
88 the initial HTTP request runs Fossil as a CGI based on the
89 "https://sqlite.org/src" portion of the URL. The Fossil instance then
90 runs the checklist sub-CGI based on the "/ext/checklists" suffix. The
@@ -213,11 +213,31 @@
213
214 Except for the three cases noted above, Fossil makes no changes or
215 additions to the CGI-generated content, but simply passes the verbatim
216 content back up the stack towards the requester.
217
218 <h2>5.0 Trouble-Shooting Hints</h2>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
220 Remember that the /ext will return any file in the extroot directory
221 hierarchy as static content if the file is readable but not executable.
222 When initially setting up the /ext mechanism, it is sometimes helpful
223 to verify that you are able to receive static content prior to working
224
--- www/serverext.wiki
+++ www/serverext.wiki
@@ -80,11 +80,11 @@
80 that file to be executable, so it runs it as CGI and returns the result.
81
82 The /sqlite-src-ext/checklist file is a
83 [https://wapp.tcl.tk|Wapp program]. The complete source code to the
84 this program can be seen at
85 [https://www.sqlite.org/src/ext/checklist/self].
86
87 There is a cascade of CGIs happening here. The webserver that receives
88 the initial HTTP request runs Fossil as a CGI based on the
89 "https://sqlite.org/src" portion of the URL. The Fossil instance then
90 runs the checklist sub-CGI based on the "/ext/checklists" suffix. The
@@ -213,11 +213,31 @@
213
214 Except for the three cases noted above, Fossil makes no changes or
215 additions to the CGI-generated content, but simply passes the verbatim
216 content back up the stack towards the requester.
217
218 <h2>5.0 Filename Restrictions</h2>
219
220 For security reasons, Fossil places restrictions on the names of files
221 in the extroot directory that can participate in the extension CGI
222 mechanism:
223
224 1. Filenames must consist of only ASCII alphanumeric characters,
225 ".", "_", and "-", and of course "/" as the file separator.
226 Files with names that includes spaces or
227 other punctuation or special characters are ignored.
228
229 2. No element of the pathname can begin with "." or "-". Files or
230 directories whose names begin with "." or "-" are ignored.
231
232 If a CGI program requires separate data files, it is safe to put those
233 files in the same directory as the CGI program itself as long as the names
234 of the data files contain special characters that cause them to be ignored
235 by Fossil. For example, ensure that all datafile begin with "-" or
236 end with ",data" or "~data".
237
238 <h2>6.0 Trouble-Shooting Hints</h2>
239
240 Remember that the /ext will return any file in the extroot directory
241 hierarchy as static content if the file is readable but not executable.
242 When initially setting up the /ext mechanism, it is sometimes helpful
243 to verify that you are able to receive static content prior to working
244

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button