|
1
|
/* |
|
2
|
** Copyright (c) 2017 D. Richard Hipp |
|
3
|
** |
|
4
|
** This program is free software; you can redistribute it and/or |
|
5
|
** modify it under the terms of the Simplified BSD License (also |
|
6
|
** known as the "2-Clause License" or "FreeBSD License".) |
|
7
|
|
|
8
|
** This program is distributed in the hope that it will be useful, |
|
9
|
** but without any warranty; without even the implied warranty of |
|
10
|
** merchantability or fitness for a particular purpose. |
|
11
|
** |
|
12
|
** Author contact information: |
|
13
|
** [email protected] |
|
14
|
** http://www.hwaci.com/drh/ |
|
15
|
** |
|
16
|
******************************************************************************* |
|
17
|
** |
|
18
|
** This file implements various web pages use for running a security audit |
|
19
|
** of a Fossil configuration. |
|
20
|
*/ |
|
21
|
#include "config.h" |
|
22
|
#include <assert.h> |
|
23
|
#include "security_audit.h" |
|
24
|
|
|
25
|
/* |
|
26
|
** Return TRUE if any of the capability letters in zTest are found |
|
27
|
** in the capability string zCap. |
|
28
|
*/ |
|
29
|
static int hasAnyCap(const char *zCap, const char *zTest){ |
|
30
|
while( zTest[0] ){ |
|
31
|
if( strchr(zCap, zTest[0]) ) return 1; |
|
32
|
zTest++; |
|
33
|
} |
|
34
|
return 0; |
|
35
|
} |
|
36
|
|
|
37
|
/* |
|
38
|
** Parse the content-security-policy |
|
39
|
** into separate fields, and return a pointer to a null-terminated |
|
40
|
** array of pointers to strings, one entry for each field. Or return |
|
41
|
** a NULL pointer if no CSP could be located in the header. |
|
42
|
** |
|
43
|
** Memory to hold the returned array and of the strings is obtained from |
|
44
|
** a single memory allocation, which the caller should free to avoid a |
|
45
|
** memory leak. |
|
46
|
*/ |
|
47
|
static char **parse_content_security_policy(void){ |
|
48
|
char **azCSP = 0; |
|
49
|
int nCSP = 0; |
|
50
|
char *zAll; |
|
51
|
char *zCopy; |
|
52
|
int nAll = 0; |
|
53
|
int jj; |
|
54
|
int nSemi; |
|
55
|
|
|
56
|
zAll = style_csp(0); |
|
57
|
nAll = (int)strlen(zAll); |
|
58
|
for(jj=nSemi=0; jj<nAll; jj++){ if( zAll[jj]==';' ) nSemi++; } |
|
59
|
azCSP = fossil_malloc( nAll+1+(nSemi+2)*sizeof(char*) ); |
|
60
|
zCopy = (char*)&azCSP[nSemi+2]; |
|
61
|
memcpy(zCopy,zAll,nAll); |
|
62
|
zCopy[nAll] = 0; |
|
63
|
while( fossil_isspace(zCopy[0]) || zCopy[0]==';' ){ zCopy++; } |
|
64
|
azCSP[0] = zCopy; |
|
65
|
nCSP = 1; |
|
66
|
for(jj=0; zCopy[jj]; jj++){ |
|
67
|
if( zCopy[jj]==';' ){ |
|
68
|
int k; |
|
69
|
for(k=jj-1; k>0 && fossil_isspace(zCopy[k]); k--){ zCopy[k] = 0; } |
|
70
|
zCopy[jj] = 0; |
|
71
|
while( jj+1<nAll |
|
72
|
&& (fossil_isspace(zCopy[jj+1]) || zCopy[jj+1]==';') |
|
73
|
){ |
|
74
|
jj++; |
|
75
|
} |
|
76
|
assert( nCSP<nSemi+1 ); |
|
77
|
azCSP[nCSP++] = zCopy+jj; |
|
78
|
} |
|
79
|
} |
|
80
|
assert( nCSP<=nSemi+2 ); |
|
81
|
azCSP[nCSP] = 0; |
|
82
|
fossil_free(zAll); |
|
83
|
return azCSP; |
|
84
|
} |
|
85
|
|
|
86
|
/* |
|
87
|
** WEBPAGE: secaudit0 |
|
88
|
** |
|
89
|
** Run a security audit of the current Fossil setup, looking |
|
90
|
** for configuration problems that might allow unauthorized |
|
91
|
** access to the repository. |
|
92
|
** |
|
93
|
** This page requires administrator access. It is usually |
|
94
|
** accessed using the Admin/Security-Audit menu option |
|
95
|
** from any of the default skins. |
|
96
|
*/ |
|
97
|
void secaudit0_page(void){ |
|
98
|
const char *zAnonCap; /* Capabilities of user "anonymous" and "nobody" */ |
|
99
|
const char *zDevCap; /* Capabilities of user group "developer" */ |
|
100
|
const char *zReadCap; /* Capabilities of user group "reader" */ |
|
101
|
const char *zPubPages; /* GLOB pattern for public pages */ |
|
102
|
const char *zSelfCap; /* Capabilities of self-registered users */ |
|
103
|
int hasSelfReg = 0; /* True if able to self-register */ |
|
104
|
const char *zPublicUrl; /* Canonical access URL */ |
|
105
|
const char *zVulnReport; /* The vuln-report setting */ |
|
106
|
Blob cmd; |
|
107
|
char *z; |
|
108
|
int n, i; |
|
109
|
CapabilityString *pCap; |
|
110
|
char **azCSP; /* Parsed content security policy */ |
|
111
|
|
|
112
|
login_check_credentials(); |
|
113
|
if( !g.perm.Admin ){ |
|
114
|
login_needed(0); |
|
115
|
return; |
|
116
|
} |
|
117
|
style_header("Security Audit"); |
|
118
|
@ <ol> |
|
119
|
|
|
120
|
/* Step 1: Determine if the repository is public or private. "Public" |
|
121
|
** means that any anonymous user on the internet can access all content. |
|
122
|
** "Private" repos require (non-anonymous) login to access all content, |
|
123
|
** though some content may be accessible anonymously. |
|
124
|
*/ |
|
125
|
zAnonCap = db_text("", "SELECT fullcap(NULL)"); |
|
126
|
zDevCap = db_text("", "SELECT fullcap('v')"); |
|
127
|
zReadCap = db_text("", "SELECT fullcap('u')"); |
|
128
|
zPubPages = db_get("public-pages",0); |
|
129
|
hasSelfReg = db_get_boolean("self-register",0); |
|
130
|
pCap = capability_add(0, db_get("default-perms","u")); |
|
131
|
capability_expand(pCap); |
|
132
|
zSelfCap = capability_string(pCap); |
|
133
|
capability_free(pCap); |
|
134
|
if( hasAnyCap(zAnonCap,"as") ){ |
|
135
|
@ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because |
|
136
|
@ it grants administrator privileges to anonymous users. You |
|
137
|
@ should <a href="takeitprivate">take this repository private</a> |
|
138
|
@ immediately! Or, at least remove the Setup and Admin privileges |
|
139
|
@ for users "anonymous" and "login" on the |
|
140
|
@ <a href="setup_ulist">User Configuration</a> page. |
|
141
|
}else if( hasAnyCap(zSelfCap,"as") && hasSelfReg ){ |
|
142
|
@ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because |
|
143
|
@ it grants administrator privileges to self-registered users. You |
|
144
|
@ should <a href="takeitprivate">take this repository private</a> |
|
145
|
@ and/or disable self-registration |
|
146
|
@ immediately! Or, at least remove the Setup and Admin privileges |
|
147
|
@ from the default permissions for new users. |
|
148
|
}else if( hasAnyCap(zAnonCap,"y") ){ |
|
149
|
@ <li><p>This repository is <big><b>INSECURE</b></big> because |
|
150
|
@ it allows anonymous users to push unversioned files. |
|
151
|
@ Fix this by <a href="takeitprivate">taking the repository private</a> |
|
152
|
@ or by removing the "y" permission from users "anonymous" and |
|
153
|
@ "nobody" on the <a href="setup_ulist">User Configuration</a> page. |
|
154
|
}else if( hasAnyCap(zSelfCap,"y") ){ |
|
155
|
@ <li><p>This repository is <big><b>INSECURE</b></big> because |
|
156
|
@ it allows self-registered users to push unversioned files. |
|
157
|
@ Fix this by <a href="takeitprivate">taking the repository private</a> |
|
158
|
@ or by removing the "y" permission from the default permissions or |
|
159
|
@ by disabling self-registration. |
|
160
|
}else if( hasAnyCap(zAnonCap,"goz") ){ |
|
161
|
@ <li><p>This repository is <big><b>PUBLIC</b></big>. All |
|
162
|
@ checked-in content can be accessed by anonymous users. |
|
163
|
@ <a href="takeitprivate">Take it private</a>.<p> |
|
164
|
}else if( hasAnyCap(zSelfCap,"goz") && hasSelfReg ){ |
|
165
|
@ <li><p>This repository is <big><b>PUBLIC</b></big> because all |
|
166
|
@ checked-in content can be accessed by self-registered users. |
|
167
|
@ This repostory would be private if you disabled self-registration.</p> |
|
168
|
}else if( !hasAnyCap(zAnonCap, "jrwy234567") |
|
169
|
&& (!hasSelfReg || !hasAnyCap(zSelfCap, "jrwy234567")) |
|
170
|
&& (zPubPages==0 || zPubPages[0]==0) ){ |
|
171
|
@ <li><p>This repository is <big><b>Completely PRIVATE</b></big>. |
|
172
|
@ A valid login and password is required to access any content. |
|
173
|
}else{ |
|
174
|
@ <li><p>This repository is <big><b>Mostly PRIVATE</b></big>. |
|
175
|
@ A valid login and password is usually required, however some |
|
176
|
@ content can be accessed either anonymously or by self-registered |
|
177
|
@ users: |
|
178
|
@ <ul> |
|
179
|
if( hasSelfReg ){ |
|
180
|
if( hasAnyCap(zAnonCap,"j") || hasAnyCap(zSelfCap,"j") ){ |
|
181
|
@ <li> Wiki pages |
|
182
|
} |
|
183
|
if( hasAnyCap(zAnonCap,"r") || hasAnyCap(zSelfCap,"r") ){ |
|
184
|
@ <li> Tickets |
|
185
|
} |
|
186
|
if( hasAnyCap(zAnonCap,"234567") || hasAnyCap(zSelfCap,"234567") ){ |
|
187
|
@ <li> Forum posts |
|
188
|
} |
|
189
|
} |
|
190
|
if( zPubPages && zPubPages[0] ){ |
|
191
|
Glob *pGlob = glob_create(zPubPages); |
|
192
|
int i; |
|
193
|
@ <li> "Public Pages" are URLs that match any of these GLOB patterns: |
|
194
|
@ <p><ul> |
|
195
|
for(i=0; i<pGlob->nPattern; i++){ |
|
196
|
@ <li> %h(pGlob->azPattern[i]) |
|
197
|
} |
|
198
|
@ </ul> |
|
199
|
@ <p>Anoymous users are vested with capabilities "%h(zSelfCap)" on |
|
200
|
@ public pages. See the "Public Pages" entry in the |
|
201
|
@ "User capability summary" below. |
|
202
|
} |
|
203
|
@ </ul> |
|
204
|
if( zPubPages && zPubPages[0] ){ |
|
205
|
@ <p>Change GLOB patterns exceptions using the "Public pages" setting |
|
206
|
@ on the <a href="setup_access">Access Settings</a> page.</p> |
|
207
|
} |
|
208
|
} |
|
209
|
|
|
210
|
zPublicUrl = public_url(); |
|
211
|
if( zPublicUrl!=0 ){ |
|
212
|
int nOther = db_int(0, "SELECT count(*) FROM config" |
|
213
|
" WHERE name GLOB 'baseurl:*'" |
|
214
|
" AND name<>'baseurl:%q'", zPublicUrl); |
|
215
|
@ <li><p>The <a href="setup_config#eurl">canonical URL</a> for this |
|
216
|
@ repository is <a href="%s(zPublicUrl)">%h(zPublicUrl)</a>. |
|
217
|
if( nOther==1 ){ |
|
218
|
@ This is also <a href="urllist?urlonly">1 other URL</a> that has |
|
219
|
@ been used to access this repository. |
|
220
|
}else if( nOther>=2 ){ |
|
221
|
@ There are also |
|
222
|
@ <a href="urllist?all&urlonly">%d(nOther) other URLs</a> that have |
|
223
|
@ been used to access this repository. |
|
224
|
} |
|
225
|
}else{ |
|
226
|
int nUrl = db_int(0, "SELECT count(*) FROM config" |
|
227
|
" WHERE name GLOB 'baseurl:*'"); |
|
228
|
@ <li><p>This repository does not have a |
|
229
|
@ <a href="setup_config#eurl">canonical access URL</a>. |
|
230
|
if( nUrl==1 ){ |
|
231
|
@ There is |
|
232
|
@ <a href="urllist?urlonly">1 non-canonical URL</a> |
|
233
|
@ that has been used to access this repository. |
|
234
|
}else if( nUrl>=2 ){ |
|
235
|
@ There are |
|
236
|
@ <a href="urllist?all&urlonly">%d(nUrl) non-canonical URLs</a> |
|
237
|
@ that have been used to access this repository. |
|
238
|
} |
|
239
|
} |
|
240
|
|
|
241
|
/* Make sure the HTTPS is required for login, at least, so that the |
|
242
|
** password does not go across the Internet in the clear. |
|
243
|
*/ |
|
244
|
if( db_get_int("redirect-to-https",0)==0 ){ |
|
245
|
@ <li><p><b>WARNING:</b> |
|
246
|
@ Sensitive material such as login passwords can be sent over an |
|
247
|
@ unencrypted connection. |
|
248
|
@ Fix this by changing the "Redirect to HTTPS" setting on the |
|
249
|
@ <a href="setup_access">Access Control</a> page. If you were using |
|
250
|
@ the old "Redirect to HTTPS on Login Page" setting, switch to the |
|
251
|
@ new setting: it has a more secure implementation. |
|
252
|
} |
|
253
|
|
|
254
|
#ifdef FOSSIL_ENABLE_TH1_DOCS |
|
255
|
/* The use of embedded TH1 is dangerous. Warn if it is possible. |
|
256
|
*/ |
|
257
|
if( !Th_AreDocsEnabled() ){ |
|
258
|
@ <li><p> |
|
259
|
@ This server is compiled with -DFOSSIL_ENABLE_TH1_DOCS. TH1 docs |
|
260
|
@ are disabled for this particular repository, so you are safe for |
|
261
|
@ now. However, to prevent future problems caused by accidentally |
|
262
|
@ enabling TH1 docs in the future, it is recommended that you |
|
263
|
@ recompile Fossil without the -DFOSSIL_ENABLE_TH1_DOCS flag.</p> |
|
264
|
}else{ |
|
265
|
@ <li><p><b>DANGER:</b> |
|
266
|
@ This server is compiled with -DFOSSIL_ENABLE_TH1_DOCS and TH1 docs |
|
267
|
@ are enabled for this repository. Anyone who can check-in or push |
|
268
|
@ to this repository can create a malicious TH1 script and then cause |
|
269
|
@ that script to be run on the server. This is a serious security concern. |
|
270
|
@ TH1 docs should only be enabled for repositories with a very limited |
|
271
|
@ number of trusted committers, and the repository should be monitored |
|
272
|
@ closely to ensure no hostile content sneaks in. If a bad TH1 script |
|
273
|
@ does make it into the repository, the only want to prevent it from |
|
274
|
@ being run is to shun it.</p> |
|
275
|
@ |
|
276
|
@ <p>Disable TH1 docs by recompiling Fossil without the |
|
277
|
@ -DFOSSIL_ENABLE_TH1_DOCS flag, and/or clear the th1-docs setting |
|
278
|
@ and ensure that the TH1_ENABLE_DOCS environment variable does not |
|
279
|
@ exist in the environment.</p> |
|
280
|
} |
|
281
|
#endif |
|
282
|
|
|
283
|
#if FOSSIL_ENABLE_TCL |
|
284
|
@ <li><p> |
|
285
|
if( db_get_boolean("tcl",0) ){ |
|
286
|
#ifdef FOSSIL_ENABLE_TH1_DOCS |
|
287
|
if( Th_AreDocsEnabled() ){ |
|
288
|
@ <b>DANGER:</b> |
|
289
|
}else{ |
|
290
|
@ <b>WARNING:</b> |
|
291
|
} |
|
292
|
#else |
|
293
|
@ <b>WARNING:</b> |
|
294
|
#endif |
|
295
|
@ This server is compiled with -DFOSSIL_ENABLE_TCL and Tcl integration |
|
296
|
@ is enabled for this repository. Anyone who can execute malicious |
|
297
|
@ TH1 script on that server can also execute arbitrary Tcl script |
|
298
|
@ under the identity of the operating system process of that server. |
|
299
|
@ This is a serious security concern.</p> |
|
300
|
@ |
|
301
|
@ <p>Disable Tcl integration by recompiling Fossil without the |
|
302
|
@ -DFOSSIL_ENABLE_TCL flag, and/or clear the 'tcl' setting.</p> |
|
303
|
}else{ |
|
304
|
@ This server is compiled with -DFOSSIL_ENABLE_TCL. Tcl integration |
|
305
|
@ is disabled for this particular repository, so you are safe for |
|
306
|
@ now. However, to prevent potential problems caused by accidentally |
|
307
|
@ enabling Tcl integration in the future, it is recommended that you |
|
308
|
@ recompile Fossil without the -DFOSSIL_ENABLE_TCL flag.</p> |
|
309
|
} |
|
310
|
#endif |
|
311
|
|
|
312
|
/* Anonymous users should not be able to harvest email addresses |
|
313
|
** from tickets. |
|
314
|
*/ |
|
315
|
if( hasAnyCap(zAnonCap, "e") ){ |
|
316
|
@ <li><p><b>WARNING:</b> |
|
317
|
@ Anonymous users can view email addresses and other personally |
|
318
|
@ identifiable information on tickets. |
|
319
|
@ Fix this by removing the "Email" privilege |
|
320
|
@ (<a href="setup_ucap_list">capability "e"</a>) from users |
|
321
|
@ "anonymous" and "nobody" on the |
|
322
|
@ <a href="setup_ulist">User Configuration</a> page. |
|
323
|
} |
|
324
|
|
|
325
|
/* Anonymous users probably should not be allowed to push content |
|
326
|
** to the repository. |
|
327
|
*/ |
|
328
|
if( hasAnyCap(zAnonCap, "i") ){ |
|
329
|
@ <li><p><b>WARNING:</b> |
|
330
|
@ Anonymous users can push new check-ins into the repository. |
|
331
|
@ Fix this by removing the "Check-in" privilege |
|
332
|
@ (<a href="setup_ucap_list">capability</a> "i") from users |
|
333
|
@ "anonymous" and "nobody" on the |
|
334
|
@ <a href="setup_ulist">User Configuration</a> page. |
|
335
|
} |
|
336
|
|
|
337
|
/* Anonymous users probably should not be allowed act as moderators |
|
338
|
** for wiki or tickets. |
|
339
|
*/ |
|
340
|
if( hasAnyCap(zAnonCap, "lq5") ){ |
|
341
|
@ <li><p><b>WARNING:</b> |
|
342
|
@ Anonymous users can act as moderators for wiki, tickets, or |
|
343
|
@ forum posts. This defeats the whole purpose of moderation. |
|
344
|
@ Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum" |
|
345
|
@ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5") |
|
346
|
@ from users "anonymous" and "nobody" |
|
347
|
@ on the <a href="setup_ulist">User Configuration</a> page. |
|
348
|
} |
|
349
|
|
|
350
|
/* Check to see if any TH1 scripts are configured to run on a sync |
|
351
|
*/ |
|
352
|
if( db_exists("SELECT 1 FROM config WHERE name GLOB 'xfer-*-script'" |
|
353
|
" AND length(value)>0") ){ |
|
354
|
@ <li><p><b>WARNING:</b> |
|
355
|
@ TH1 scripts might be configured to run on any sync, push, pull, or |
|
356
|
@ clone operation. See the <a href="%R/xfersetup">/xfersetup</a> |
|
357
|
@ page for more information. These TH1 scripts are a potential |
|
358
|
@ security concern and so should be carefully audited by a human. |
|
359
|
} |
|
360
|
|
|
361
|
/* The strict-manifest-syntax setting should be on. */ |
|
362
|
if( db_get_boolean("strict-manifest-syntax",1)==0 ){ |
|
363
|
@ <li><p><b>WARNING:</b> |
|
364
|
@ The "strict-manifest-syntax" flag is off. This is a security |
|
365
|
@ risk. Turn this setting on (its default) to protect the users |
|
366
|
@ of this repository. |
|
367
|
} |
|
368
|
|
|
369
|
zVulnReport = db_get("vuln-report","log"); |
|
370
|
if( fossil_strcmp(zVulnReport,"block")!=0 |
|
371
|
&& fossil_strcmp(zVulnReport,"fatal")!=0 |
|
372
|
){ |
|
373
|
@ <li><p><b>WARNING:</b> |
|
374
|
@ The <a href="%R/help/vuln-report">vuln-report setting</a> |
|
375
|
@ has a value of "%h(zVulnReport)". This disables defenses against |
|
376
|
@ XSS or SQL-injection vulnerabilities caused by coding errors in |
|
377
|
@ custom TH1 scripts. For the best security, change |
|
378
|
@ the value of the vuln-report setting to "block" or "fatal". |
|
379
|
} |
|
380
|
|
|
381
|
/* Obsolete: */ |
|
382
|
if( hasAnyCap(zAnonCap, "d") || |
|
383
|
hasAnyCap(zDevCap, "d") || |
|
384
|
hasAnyCap(zReadCap, "d") ){ |
|
385
|
@ <li><p><b>WARNING:</b> |
|
386
|
@ One or more users has the <a |
|
387
|
@ href="https://fossil-scm.org/forum/forumpost/43c78f4bef">obsolete</a> |
|
388
|
@ "d" capability. You should remove it using the |
|
389
|
@ <a href="setup_ulist">User Configuration</a> page in case we |
|
390
|
@ ever reuse the letter for another purpose. |
|
391
|
} |
|
392
|
|
|
393
|
/* If anonymous users are allowed to create new Wiki, then |
|
394
|
** wiki moderation should be activated to prevent spam. |
|
395
|
*/ |
|
396
|
if( hasAnyCap(zAnonCap, "fk") ){ |
|
397
|
if( db_get_boolean("modreq-wiki",0)==0 ){ |
|
398
|
@ <li><p><b>WARNING:</b> |
|
399
|
@ Anonymous users can create or edit wiki without moderation. |
|
400
|
@ This can result in robots inserting lots of wiki spam into |
|
401
|
@ repository. |
|
402
|
@ Fix this by removing the "New-Wiki" and "Write-Wiki" |
|
403
|
@ privileges from users "anonymous" and "nobody" on the |
|
404
|
@ <a href="setup_ulist">User Configuration</a> page or |
|
405
|
@ by enabling wiki moderation on the |
|
406
|
@ <a href="setup_modreq">Moderation Setup</a> page. |
|
407
|
}else{ |
|
408
|
@ <li><p> |
|
409
|
@ Anonymous users can create or edit wiki, but moderator |
|
410
|
@ approval is required before the edits become permanent. |
|
411
|
} |
|
412
|
} |
|
413
|
|
|
414
|
/* Anonymous users should not be able to create trusted forum |
|
415
|
** posts. |
|
416
|
*/ |
|
417
|
if( hasAnyCap(zAnonCap, "456") ){ |
|
418
|
@ <li><p><b>WARNING:</b> |
|
419
|
@ Anonymous users can create forum posts that are |
|
420
|
@ accepted into the permanent record without moderation. |
|
421
|
@ This can result in robots generating spam on forum posts. |
|
422
|
@ Fix this by removing the "WriteTrusted-Forum" privilege |
|
423
|
@ (<a href="setup_ucap_list">capabilities</a> "456") from |
|
424
|
@ users "anonymous" and "nobody" on the |
|
425
|
@ <a href="setup_ulist">User Configuration</a> page or |
|
426
|
} |
|
427
|
|
|
428
|
/* Anonymous users should not be able to send announcements. |
|
429
|
*/ |
|
430
|
if( hasAnyCap(zAnonCap, "A") ){ |
|
431
|
@ <li><p><b>WARNING:</b> |
|
432
|
@ Anonymous users can send announcements to anybody who is signed |
|
433
|
@ up to receive announcements. This can result in spam. |
|
434
|
@ Fix this by removing the "Announce" privilege |
|
435
|
@ (<a href="setup_ucap_list">capability</a> "A") from |
|
436
|
@ users "anonymous" and "nobody" on the |
|
437
|
@ <a href="setup_ulist">User Configuration</a> page or |
|
438
|
} |
|
439
|
|
|
440
|
/* Administrative privilege should only be provided to |
|
441
|
** specific individuals, not to entire classes of people. |
|
442
|
** And not too many people should have administrator privilege. |
|
443
|
*/ |
|
444
|
z = db_text(0, |
|
445
|
"SELECT group_concat(" |
|
446
|
"printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," |
|
447
|
"' and ')" |
|
448
|
" FROM user" |
|
449
|
" WHERE cap GLOB '*[as]*'" |
|
450
|
" AND login in ('anonymous','nobody','reader','developer')" |
|
451
|
); |
|
452
|
if( z && z[0] ){ |
|
453
|
@ <li><p><b>WARNING:</b> |
|
454
|
@ Administrative privilege ('a' or 's') |
|
455
|
@ is granted to an entire class of users: %s(z). |
|
456
|
@ Administrative privilege should only be |
|
457
|
@ granted to specific individuals. |
|
458
|
} |
|
459
|
n = db_int(0,"SELECT count(*) FROM user WHERE fullcap(cap) GLOB '*[as]*'"); |
|
460
|
if( n==0 ){ |
|
461
|
@ <li><p> |
|
462
|
@ No users have administrator privilege. |
|
463
|
}else{ |
|
464
|
z = db_text(0, |
|
465
|
"SELECT group_concat(" |
|
466
|
"printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," |
|
467
|
"', ')" |
|
468
|
" FROM user" |
|
469
|
" WHERE fullcap(cap) GLOB '*[as]*'" |
|
470
|
); |
|
471
|
@ <li><p> |
|
472
|
@ Users with administrator privilege are: %s(z) |
|
473
|
fossil_free(z); |
|
474
|
if( n>3 ){ |
|
475
|
@ <li><p><b>WARNING:</b> |
|
476
|
@ Administrator privilege is granted to |
|
477
|
@ <a href='setup_ulist?with=as'>%d(n) users</a>. |
|
478
|
@ Ideally, administrator privilege ('s' or 'a') should only |
|
479
|
@ be granted to one or two users. |
|
480
|
} |
|
481
|
} |
|
482
|
|
|
483
|
/* The push-unversioned privilege should only be provided to |
|
484
|
** specific individuals, not to entire classes of people. |
|
485
|
** And no too many people should have this privilege. |
|
486
|
*/ |
|
487
|
z = db_text(0, |
|
488
|
"SELECT group_concat(" |
|
489
|
"printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," |
|
490
|
"' and ')" |
|
491
|
" FROM user" |
|
492
|
" WHERE cap GLOB '*y*'" |
|
493
|
" AND login in ('anonymous','nobody','reader','developer')" |
|
494
|
); |
|
495
|
if( z && z[0] ){ |
|
496
|
@ <li><p><b>WARNING:</b> |
|
497
|
@ The "Write-Unver" privilege is granted to an entire class of users: %s(z). |
|
498
|
@ The Write-Unver privilege should only be granted to specific individuals. |
|
499
|
fossil_free(z); |
|
500
|
} |
|
501
|
n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*y*'"); |
|
502
|
if( n>0 ){ |
|
503
|
z = db_text(0, |
|
504
|
"SELECT group_concat(" |
|
505
|
"printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),', ')" |
|
506
|
" FROM user WHERE fullcap(cap) GLOB '*y*'" |
|
507
|
); |
|
508
|
@ <li><p> |
|
509
|
@ Users with "Write-Unver" privilege: %s(z) |
|
510
|
fossil_free(z); |
|
511
|
if( n>3 ){ |
|
512
|
@ <p><b>Caution:</b> |
|
513
|
@ The "Write-Unver" privilege ('y') is granted to an excessive |
|
514
|
@ number of users (%d(n)). |
|
515
|
@ Ideally, the Write-Unver privilege should only |
|
516
|
@ be granted to one or two users. |
|
517
|
} |
|
518
|
} |
|
519
|
|
|
520
|
/* Providing hyperlink capability to user "nobody" can lead to robots |
|
521
|
** making excessive requests resulting in DoS |
|
522
|
*/ |
|
523
|
if( db_exists("SELECT 1 FROM user WHERE login='nobody' AND cap GLOB '*h*'") ){ |
|
524
|
int nobodyId = db_int(0,"SELECT uid FROM user WHERE login='nobody'"); |
|
525
|
int anonId = db_int(0, |
|
526
|
"SELECT uid FROM user WHERE login='anonymous' AND cap NOT GLOB '*h*'"); |
|
527
|
@ <li><p> |
|
528
|
@ User "nobody" has "Hyperlink" privilege ('h') which can lead to |
|
529
|
@ robots walking a nearly endless progression of pages on public-facing |
|
530
|
@ repositories, causing excessive server load and possible DoS. |
|
531
|
@ Suggested remediation: |
|
532
|
@ <ol type="a"> |
|
533
|
@ <li>Remove the 'h' privilege from the |
|
534
|
@ <a href="%R/setup_uedit?id=%d(nobodyId)">'nobody' user</a> so that |
|
535
|
@ robots cannot see hyperlinks. |
|
536
|
@ <li>Activate <a href="%R/setup_robot">autohyperlink</a> so that |
|
537
|
@ human readers can still see hyperlinks even if they are not logged in. |
|
538
|
@ Set the delay to at least 50 milliseconds and require a mouse |
|
539
|
@ event for maximum robot defense. |
|
540
|
if( anonId>0 ){ |
|
541
|
@ <li>Perhaps set the 'h' privilege on the |
|
542
|
@ <a href="%R/setup_uedit?id=%d(anonId)">'anonymous' user</a> so |
|
543
|
@ that humans that have javascript disabled in their browsers can |
|
544
|
@ still see hyperlinks if they will log in as "anonymous". |
|
545
|
} |
|
546
|
@ </ol> |
|
547
|
} |
|
548
|
|
|
549
|
/* Notify if REMOTE_USER or HTTP_AUTHENTICATION is used for login. |
|
550
|
*/ |
|
551
|
if( db_get_boolean("remote_user_ok", 0) ){ |
|
552
|
@ <li><p><b>Caution:</b> |
|
553
|
@ This repository trusts that the REMOTE_USER environment variable set |
|
554
|
@ up by the webcontains the name of an authenticated user. |
|
555
|
@ Fossil's built-in authentication mechanism is bypassed. |
|
556
|
@ Fix this by deactivating the "Allow REMOTE_USER authentication" |
|
557
|
@ checkbox on the <a href="setup_access">Access Control</a> page. |
|
558
|
} |
|
559
|
if( db_get_boolean("http_authentication_ok", 0) ){ |
|
560
|
@ <li><p><b>Caution:</b> |
|
561
|
@ This repository trusts that the HTTP_AUTHENTICATION environment |
|
562
|
@ variable set up by the webserver contains the name of an |
|
563
|
@ authenticated user. |
|
564
|
@ Fossil's built-in authentication mechanism is bypassed. |
|
565
|
@ Fix this by deactivating the "Allow HTTP_AUTHENTICATION authentication" |
|
566
|
@ checkbox on the <a href="setup_access">Access Control</a> page. |
|
567
|
} |
|
568
|
|
|
569
|
/* Logging should be turned on |
|
570
|
*/ |
|
571
|
if( db_get_boolean("access-log",1)==0 ){ |
|
572
|
@ <li><p> |
|
573
|
@ The <a href="access_log">User Log</a> is disabled. The user log |
|
574
|
@ keeps a record of successful and unsuccessful login attempts and is |
|
575
|
@ useful for security monitoring. |
|
576
|
} |
|
577
|
if( db_get_boolean("admin-log",1)==0 ){ |
|
578
|
@ <li><p> |
|
579
|
@ The <a href="admin_log">Administrative Log</a> is disabled. |
|
580
|
@ The administrative log provides a record of configuration changes |
|
581
|
@ and is useful for security monitoring. |
|
582
|
} |
|
583
|
|
|
584
|
#if !defined(_WIN32) && !defined(FOSSIL_OMIT_LOAD_AVERAGE) |
|
585
|
/* Make sure that the load-average limiter is armed and working */ |
|
586
|
if( load_average()==0.0 ){ |
|
587
|
@ <li><p> |
|
588
|
@ Unable to get the system load average. This can prevent Fossil |
|
589
|
@ from throttling expensive operations during peak demand. |
|
590
|
@ If running in a chroot jail on Linux, verify that the /proc |
|
591
|
@ filesystem is mounted within the jail, so that the load average |
|
592
|
@ can be obtained from the /proc/loadavg file. |
|
593
|
}else { |
|
594
|
double r = fossil_atof(db_get("max-loadavg", "0.0")); |
|
595
|
if( r<=0.0 ){ |
|
596
|
@ <li><p> |
|
597
|
@ Load average limiting is turned off. This can cause the server |
|
598
|
@ to bog down if many requests for expensive services (such as |
|
599
|
@ large diffs or tarballs) arrive at about the same time. |
|
600
|
@ To fix this, set the |
|
601
|
@ <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on the |
|
602
|
@ <a href='%R/setup_access'>Access Control</a> page to the approximate |
|
603
|
@ the number of available cores on your server, or maybe just a little |
|
604
|
@ less. |
|
605
|
}else if( r>=8.0 ){ |
|
606
|
@ <li><p> |
|
607
|
@ The <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on |
|
608
|
@ the <a href="setup_access">Access Control</a> page is set to %g(r), |
|
609
|
@ which seems high. Is this server really a %d((int)r)-core machine? |
|
610
|
} |
|
611
|
} |
|
612
|
#endif |
|
613
|
|
|
614
|
if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
|
615
|
@ <li><p> |
|
616
|
@ The server error log is disabled. |
|
617
|
@ To set up an error log, |
|
618
|
if( fossil_strcmp(g.zCmdName, "cgi")==0 ){ |
|
619
|
@ make an entry like "errorlog: <i>FILENAME</i>" in the |
|
620
|
@ CGI script at %h(P("SCRIPT_FILENAME")). |
|
621
|
}else{ |
|
622
|
@ add the "--errorlog <i>FILENAME</i>" option to the |
|
623
|
@ "%h(g.argv[0]) %h(g.zCmdName)" command that launched this server. |
|
624
|
} |
|
625
|
}else{ |
|
626
|
FILE *pTest = fossil_fopen(g.zErrlog,"a"); |
|
627
|
if( pTest==0 ){ |
|
628
|
@ <li><p> |
|
629
|
@ <b>Error:</b> |
|
630
|
@ There is an error log at "%h(g.zErrlog)" but that file is not |
|
631
|
@ writable and so no logging will occur. |
|
632
|
}else{ |
|
633
|
fclose(pTest); |
|
634
|
@ <li><p> |
|
635
|
@ The error log at "<a href='%R/errorlog'>%h(g.zErrlog)</a>" is |
|
636
|
@ %,lld(file_size(g.zErrlog, ExtFILE)) bytes in size. |
|
637
|
} |
|
638
|
} |
|
639
|
|
|
640
|
if( g.zExtRoot ){ |
|
641
|
int nFile; |
|
642
|
int nCgi; |
|
643
|
ext_files(); |
|
644
|
nFile = db_int(0, "SELECT count(*) FROM sfile"); |
|
645
|
nCgi = nFile==0 ? 0 : db_int(0,"SELECT count(*) FROM sfile WHERE isexe"); |
|
646
|
@ <li><p> CGI Extensions are enabled with a document root |
|
647
|
@ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding |
|
648
|
@ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files. |
|
649
|
} |
|
650
|
|
|
651
|
if( fileedit_glob()!=0 ){ |
|
652
|
@ <li><p><a href='%R/fileedit'>Online File Editing</a> is enabled |
|
653
|
@ for this repository. Clear the |
|
654
|
@ <a href='%R/setup_settings'>"fileedit-glob" setting</a> to |
|
655
|
@ disable online editing.</p> |
|
656
|
} |
|
657
|
|
|
658
|
@ <li><p> User capability summary: |
|
659
|
capability_summary(); |
|
660
|
|
|
661
|
|
|
662
|
azCSP = parse_content_security_policy(); |
|
663
|
if( azCSP==0 ){ |
|
664
|
@ <li><p> WARNING: No Content Security Policy (CSP) is specified in the |
|
665
|
@ header. Though not required, a strong CSP is recommended. Fossil will |
|
666
|
@ automatically insert an appropriate CSP if you let it generate the |
|
667
|
@ HTML <tt><head></tt> element by omitting <tt><body></tt> |
|
668
|
@ from the header configuration in your customized skin. |
|
669
|
@ |
|
670
|
}else{ |
|
671
|
int ii; |
|
672
|
@ <li><p> Content Security Policy: |
|
673
|
@ <ol type="a"> |
|
674
|
for(ii=0; azCSP[ii]; ii++){ |
|
675
|
@ <li>%h(azCSP[ii]) |
|
676
|
} |
|
677
|
@ </ |
|
678
|
fossil_free(azCSP); |
|
679
|
|
|
680
|
if( alert_enabled() ){ |
|
681
|
char * zListId = db_get("email-listid", 0); |
|
682
|
@ <li><p> Email alert configuration summary: |
|
683
|
if( !zListId || !zListId[0] ){ |
|
684
|
@ <br><strong>WARNING:</strong> <code>email-listid</code> is not set, |
|
685
|
@ so notifications will not include unsubscribe links. |
|
686
|
} |
|
687
|
fossil_free(zListId); |
|
688
|
@ <table class="label-value"> |
|
689
|
stats_for_email(); |
|
690
|
@ </table> |
|
691
|
}else{ |
|
692
|
@ <li><p> Email alerts are disabled |
|
693
|
} |
|
694
|
|
|
695
|
n = db_int(0,"SELECT count(*) FROM (" |
|
696
|
"SELECT rid FROM phantom EXCEPT SELECT rid FROM private)"); |
|
697
|
if( n>0 ){ |
|
698
|
@ <li><p>\ |
|
699
|
@ There exists public phantom artifacts in this repository, shown below. |
|
700
|
@ Phantom artifacts are artifacts whose hash name is referenced by some |
|
701
|
@ other artifact but whose content is unknown. Some phantoms are marked |
|
702
|
@ private and those are ignored. But public phantoms cause unnecessary |
|
703
|
@ sync traffic and might represent malicious attempts to corrupt the |
|
704
|
@ repository structure. |
|
705
|
@ </p><p> |
|
706
|
@ To suppress unnecessary sync traffic caused by phantoms, add the RID |
|
707
|
@ of each phantom to the "private" table. Example: |
|
708
|
@ <blockquote><pre> |
|
709
|
@ INSERT INTO private SELECT rid FROM blob WHERE content IS NULL; |
|
710
|
@ </pre></blockquote> |
|
711
|
@ </p> |
|
712
|
table_of_public_phantoms(); |
|
713
|
@ </li> |
|
714
|
} |
|
715
|
|
|
716
|
@ <li><p>Robot Defenses: |
|
717
|
@ <ol type="a"> |
|
718
|
switch( db_get_int("auto-hyperlink",1) ){ |
|
719
|
default: |
|
720
|
@ <li> No auto-enable of hyperlinks. |
|
721
|
break; |
|
722
|
case 1: |
|
723
|
@ <li> Hyperlinks auto-enabled based on UserAgent and Javascript. |
|
724
|
break; |
|
725
|
case 2: |
|
726
|
@ <li> Hyperlinks auto-enabled based on UserAgent only. |
|
727
|
break; |
|
728
|
} |
|
729
|
z = db_get("max-loadavg",0); |
|
730
|
if( z && fossil_atof(z)>0.0 ){ |
|
731
|
@ <li> Maximum load average for expensive requests: %h(z); |
|
732
|
}else{ |
|
733
|
@ <li> No limits on the load average |
|
734
|
} |
|
735
|
z = db_get("robot-restrict",0); |
|
736
|
if( z==0 ){ |
|
737
|
@ <li> No complex-request constraints on robots |
|
738
|
}else{ |
|
739
|
@ <li> Complex requests limited for pages matching: %h(z) |
|
740
|
} |
|
741
|
@ </ol> |
|
742
|
|
|
743
|
blob_init(&cmd, 0, 0); |
|
744
|
for(i=0; g.argvOrig[i]!=0; i++){ |
|
745
|
blob_append_escaped_arg(&cmd, g.argvOrig[i], 0); |
|
746
|
} |
|
747
|
@ <li><p> |
|
748
|
if( g.zCgiFile ){ |
|
749
|
Blob fullname; |
|
750
|
blob_init(&fullname, 0, 0); |
|
751
|
file_canonical_name(g.zCgiFile, &fullname, 0); |
|
752
|
@ The CGI control file for this page is "%h(blob_str(&fullname))". |
|
753
|
} |
|
754
|
@ The command that generated this page: |
|
755
|
@ <blockquote> |
|
756
|
@ <tt>%h(blob_str(&cmd))</tt> |
|
757
|
@ </blockquote></li> |
|
758
|
blob_zero(&cmd); |
|
759
|
|
|
760
|
@ </ol> |
|
761
|
style_finish_page(); |
|
762
|
} |
|
763
|
|
|
764
|
/* |
|
765
|
** WEBPAGE: takeitprivate |
|
766
|
** |
|
767
|
** Disable anonymous access to this website |
|
768
|
*/ |
|
769
|
void takeitprivate_page(void){ |
|
770
|
login_check_credentials(); |
|
771
|
if( !g.perm.Admin ){ |
|
772
|
login_needed(0); |
|
773
|
return; |
|
774
|
} |
|
775
|
if( P("cancel") ){ |
|
776
|
/* User pressed the cancel button. Go back */ |
|
777
|
cgi_redirect("secaudit0"); |
|
778
|
} |
|
779
|
if( P("apply") ){ |
|
780
|
db_unprotect(PROTECT_ALL); |
|
781
|
db_multi_exec( |
|
782
|
"UPDATE user SET cap=''" |
|
783
|
" WHERE login IN ('nobody','anonymous');" |
|
784
|
"DELETE FROM config WHERE name='public-pages';" |
|
785
|
); |
|
786
|
db_set("self-register","0",0); |
|
787
|
db_protect_pop(); |
|
788
|
cgi_redirect("secaudit0"); |
|
789
|
} |
|
790
|
style_header("Make This Website Private"); |
|
791
|
@ <p>Click the "Make It Private" button below to disable all |
|
792
|
@ anonymous access to this repository. A valid login and password |
|
793
|
@ will be required to access this repository after clicking that |
|
794
|
@ button.</p> |
|
795
|
@ |
|
796
|
@ <p>Click the "Cancel" button to leave things as they are.</p> |
|
797
|
@ |
|
798
|
@ <form action="%s(g.zPath)" method="post"> |
|
799
|
@ <input type="submit" name="apply" value="Make It Private"> |
|
800
|
@ <input type="submit" name="cancel" value="Cancel"> |
|
801
|
@ </form> |
|
802
|
|
|
803
|
style_finish_page(); |
|
804
|
} |
|
805
|
|
|
806
|
/* |
|
807
|
** Output a message explaining that no error log is available. |
|
808
|
*/ |
|
809
|
static void no_error_log_available(void){ |
|
810
|
@ <p>No error log is configured. |
|
811
|
if( g.zCgiFile==0 ){ |
|
812
|
@ To create an error log, add the "--errorlog FILENAME" |
|
813
|
@ command-line option to the command that launches the Fossil server. |
|
814
|
}else{ |
|
815
|
Blob fullname; |
|
816
|
blob_init(&fullname, 0, 0); |
|
817
|
file_canonical_name(g.zCgiFile, &fullname, 0); |
|
818
|
@ To create an error log, edit the CGI control file |
|
819
|
@ named "%h(blob_str(&fullname))" to add a line like this: |
|
820
|
@ <blockquote><pre> |
|
821
|
@ errorlog: <i>FILENAME</i> |
|
822
|
@ </pre></blockquote> |
|
823
|
blob_reset(&fullname); |
|
824
|
} |
|
825
|
} |
|
826
|
|
|
827
|
/* |
|
828
|
** WEBPAGE: errorlog |
|
829
|
** |
|
830
|
** Show the content of the error log. Only the administrator can view |
|
831
|
** this page. |
|
832
|
** |
|
833
|
** y=0x001 Show only hack attempts |
|
834
|
** y=0x002 Show only panics and assertion faults |
|
835
|
** y=0x004 Show hung backoffice processes |
|
836
|
** y=0x008 Show POST requests from a different origin |
|
837
|
** y=0x010 Show SQLITE_AUTH and similar |
|
838
|
** y=0x020 Show SMTP error reports |
|
839
|
** y=0x040 Show TH1 vulnerability reports |
|
840
|
** y=0x080 Show SQL errors |
|
841
|
** y=0x100 Show timeouts |
|
842
|
** y=0x200 Show WAL recoveries |
|
843
|
** y=0x8000 Show other uncategorized messages |
|
844
|
** |
|
845
|
** If y is omitted or is zero, a count of the various message types is |
|
846
|
** shown. |
|
847
|
*/ |
|
848
|
void errorlog_page(void){ |
|
849
|
i64 szFile; |
|
850
|
FILE *in; |
|
851
|
char *zLog; |
|
852
|
const char *zType = P("y"); |
|
853
|
static const int eAllTypes = 0x83ff; |
|
854
|
long eType = 0; |
|
855
|
int bOutput = 0; |
|
856
|
int prevWasTime = 0; |
|
857
|
int nHack = 0; |
|
858
|
int nPanic = 0; |
|
859
|
int nOther = 0; |
|
860
|
int nHang = 0; |
|
861
|
int nXPost = 0; |
|
862
|
int nAuth = 0; |
|
863
|
int nSmtp = 0; |
|
864
|
int nVuln = 0; |
|
865
|
int nSqlErr = 0; |
|
866
|
int nTimeout = 0; |
|
867
|
int nRecover = 0; |
|
868
|
char z[10000]; |
|
869
|
char zTime[10000]; |
|
870
|
|
|
871
|
login_check_credentials(); |
|
872
|
if( !g.perm.Admin ){ |
|
873
|
login_needed(0); |
|
874
|
return; |
|
875
|
} |
|
876
|
if( zType ){ |
|
877
|
eType = strtol(zType,0,0) & eAllTypes; |
|
878
|
} |
|
879
|
style_header("Server Error Log"); |
|
880
|
style_submenu_element("Test", "%R/test-warning"); |
|
881
|
style_submenu_element("Refresh", "%R/errorlog"); |
|
882
|
style_submenu_element("Download", "%R/errorlog?download"); |
|
883
|
style_submenu_element("Truncate", "%R/errorlog?truncate"); |
|
884
|
style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
|
885
|
if( eType ){ |
|
886
|
style_submenu_element("Summary", "%R/errorlog"); |
|
887
|
} |
|
888
|
|
|
889
|
if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
|
890
|
no_error_log_available(); |
|
891
|
style_finish_page(); |
|
892
|
return; |
|
893
|
} |
|
894
|
if( P("truncate1") && cgi_csrf_safe(2) ){ |
|
895
|
fclose(fopen(g.zErrlog,"w")); |
|
896
|
} |
|
897
|
if( P("download") ){ |
|
898
|
Blob log; |
|
899
|
blob_read_from_file(&log, g.zErrlog, ExtFILE); |
|
900
|
cgi_set_content_type("text/plain"); |
|
901
|
cgi_set_content(&log); |
|
902
|
return; |
|
903
|
} |
|
904
|
szFile = file_size(g.zErrlog, ExtFILE); |
|
905
|
if( P("truncate") ){ |
|
906
|
@ <form action="%R/errorlog" method="POST"> |
|
907
|
login_insert_csrf_secret(); |
|
908
|
@ <p>Confirm that you want to truncate the %,lld(szFile)-byte error log: |
|
909
|
@ <input type="submit" name="truncate1" value="Confirm"> |
|
910
|
@ <input type="submit" name="cancel" value="Cancel"> |
|
911
|
@ </form> |
|
912
|
style_finish_page(); |
|
913
|
return; |
|
914
|
} |
|
915
|
zLog = file_canonical_name_dup(g.zErrlog); |
|
916
|
@ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size. |
|
917
|
fossil_free(zLog); |
|
918
|
in = fossil_fopen(g.zErrlog, "rb"); |
|
919
|
if( in==0 ){ |
|
920
|
@ <p class='generalError'>Unable to open that file for reading!</p> |
|
921
|
style_finish_page(); |
|
922
|
return; |
|
923
|
} |
|
924
|
if( eType==0 ){ |
|
925
|
/* will do a summary */ |
|
926
|
}else if( (eType&eAllTypes)!=eAllTypes ){ |
|
927
|
@ Only the following types of messages displayed: |
|
928
|
@ <ul> |
|
929
|
if( eType & 0x01 ){ |
|
930
|
@ <li>Hack attempts |
|
931
|
} |
|
932
|
if( eType & 0x02 ){ |
|
933
|
@ <li>Panics and assertion faults |
|
934
|
} |
|
935
|
if( eType & 0x04 ){ |
|
936
|
@ <li>Hung backoffice processes |
|
937
|
} |
|
938
|
if( eType & 0x08 ){ |
|
939
|
@ <li>POST requests from different origin |
|
940
|
} |
|
941
|
if( eType & 0x10 ){ |
|
942
|
@ <li>SQLITE_AUTH and similar errors |
|
943
|
} |
|
944
|
if( eType & 0x20 ){ |
|
945
|
@ <li>SMTP malfunctions |
|
946
|
} |
|
947
|
if( eType & 0x40 ){ |
|
948
|
@ <li>TH1 vulnerabilities |
|
949
|
} |
|
950
|
if( eType & 0x80 ){ |
|
951
|
@ <li>SQL errors |
|
952
|
} |
|
953
|
if( eType & 0x100 ){ |
|
954
|
@ <li>Timeouts |
|
955
|
} |
|
956
|
if( eType & 0x200 ){ |
|
957
|
@ <li>WAL recoveries |
|
958
|
} |
|
959
|
if( eType & 0x8000 ){ |
|
960
|
@ <li>Other uncategorized messages |
|
961
|
} |
|
962
|
@ </ul> |
|
963
|
} |
|
964
|
@ <hr> |
|
965
|
if( eType ){ |
|
966
|
@ <pre> |
|
967
|
} |
|
968
|
while( fgets(z, sizeof(z), in) ){ |
|
969
|
if( prevWasTime ){ |
|
970
|
if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){ |
|
971
|
bOutput = (eType & 0x01)!=0; |
|
972
|
nHack++; |
|
973
|
}else |
|
974
|
if( strncmp(z,"panic: ", 7)==0 ){ |
|
975
|
if( strncmp(z+7,"Timeout",7)==0 ){ |
|
976
|
bOutput = (eType & 0x100)!=0; |
|
977
|
nTimeout++; |
|
978
|
}else{ |
|
979
|
bOutput = (eType & 0x02)!=0; |
|
980
|
nPanic++; |
|
981
|
} |
|
982
|
}else |
|
983
|
if( strstr(z,"assertion fault")!=0 ){ |
|
984
|
bOutput = (eType & 0x02)!=0; |
|
985
|
nPanic++; |
|
986
|
}else |
|
987
|
if( strncmp(z,"SMTP:", 5)==0 ){ |
|
988
|
bOutput = (eType & 0x20)!=0; |
|
989
|
nSmtp++; |
|
990
|
}else |
|
991
|
if( sqlite3_strglob("warning: SQLITE_NOTICE(283):*",z)==0 ){ |
|
992
|
bOutput = (eType & 0x200)!=0; |
|
993
|
nRecover++; |
|
994
|
}else |
|
995
|
if( sqlite3_strglob("warning: backoffice process * still *",z)==0 ){ |
|
996
|
bOutput = (eType & 0x04)!=0; |
|
997
|
nHang++; |
|
998
|
}else |
|
999
|
if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){ |
|
1000
|
bOutput = (eType & 0x08)!=0; |
|
1001
|
nXPost++; |
|
1002
|
}else |
|
1003
|
if( sqlite3_strglob("SECURITY: authorizer blocks*",z)==0 |
|
1004
|
|| sqlite3_strglob("warning: SQLITE_AUTH*",z)==0 |
|
1005
|
){ |
|
1006
|
bOutput = (eType & 0x10)!=0; |
|
1007
|
nAuth++; |
|
1008
|
}else |
|
1009
|
if( strncmp(z,"possible", 8)==0 && strstr(z,"tainted")!=0 ){ |
|
1010
|
bOutput = (eType & 0x40)!=0; |
|
1011
|
nVuln++; |
|
1012
|
}else |
|
1013
|
if( strstr(z,"statement aborts at ") ){ |
|
1014
|
bOutput = (eType & 0x80)!=0; |
|
1015
|
nSqlErr++; |
|
1016
|
}else |
|
1017
|
{ |
|
1018
|
bOutput = (eType & 0x8000)!=0; |
|
1019
|
nOther++; |
|
1020
|
} |
|
1021
|
if( bOutput ){ |
|
1022
|
@ %h(zTime)\ |
|
1023
|
} |
|
1024
|
} |
|
1025
|
if( strncmp(z, "--------", 8)==0 ){ |
|
1026
|
size_t n = strlen(z); |
|
1027
|
memcpy(zTime, z, n+1); |
|
1028
|
prevWasTime = 1; |
|
1029
|
bOutput = 0; |
|
1030
|
}else{ |
|
1031
|
prevWasTime = 0; |
|
1032
|
} |
|
1033
|
if( bOutput && eType ){ |
|
1034
|
@ %h(z)\ |
|
1035
|
} |
|
1036
|
} |
|
1037
|
fclose(in); |
|
1038
|
if( eType ){ |
|
1039
|
@ </pre> |
|
1040
|
} |
|
1041
|
if( eType==0 ){ |
|
1042
|
int nNonHack = nPanic + nHang + nAuth + nSmtp + nVuln + nOther + nSqlErr; |
|
1043
|
int nTotal = nNonHack + nHack + nXPost; |
|
1044
|
@ <p><table border="a" cellspacing="0" cell |