/src/libxslt/libxslt/security.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * security.c: Implementation of the XSLT security framework |
3 | | * |
4 | | * See Copyright for the status of this software. |
5 | | * |
6 | | * daniel@veillard.com |
7 | | */ |
8 | | |
9 | | #define IN_LIBXSLT |
10 | | #include "libxslt.h" |
11 | | |
12 | | #include <string.h> |
13 | | |
14 | | #ifdef HAVE_SYS_TYPES_H |
15 | | #include <sys/types.h> |
16 | | #endif |
17 | | #ifdef HAVE_SYS_STAT_H |
18 | | #include <sys/stat.h> |
19 | | #endif |
20 | | |
21 | | #if defined(_WIN32) |
22 | | #include <windows.h> |
23 | | #ifndef INVALID_FILE_ATTRIBUTES |
24 | | #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) |
25 | | #endif |
26 | | #endif |
27 | | |
28 | | #ifndef HAVE_STAT |
29 | | # ifdef HAVE__STAT |
30 | | /* MS C library seems to define stat and _stat. The definition |
31 | | * is identical. Still, mapping them to each other causes a warning. */ |
32 | | # ifndef _MSC_VER |
33 | | # define stat(x,y) _stat(x,y) |
34 | | # endif |
35 | | # define HAVE_STAT |
36 | | # endif |
37 | | #endif |
38 | | |
39 | | #include <libxml/xmlmemory.h> |
40 | | #include <libxml/tree.h> |
41 | | #include <libxml/uri.h> |
42 | | #include "xslt.h" |
43 | | #include "xsltInternals.h" |
44 | | #include "xsltutils.h" |
45 | | #include "extensions.h" |
46 | | #include "security.h" |
47 | | |
48 | | |
49 | | struct _xsltSecurityPrefs { |
50 | | xsltSecurityCheck readFile; |
51 | | xsltSecurityCheck createFile; |
52 | | xsltSecurityCheck createDir; |
53 | | xsltSecurityCheck readNet; |
54 | | xsltSecurityCheck writeNet; |
55 | | }; |
56 | | |
57 | | static xsltSecurityPrefsPtr xsltDefaultSecurityPrefs = NULL; |
58 | | |
59 | | /************************************************************************ |
60 | | * * |
61 | | * Module interfaces * |
62 | | * * |
63 | | ************************************************************************/ |
64 | | |
65 | | /** |
66 | | * xsltNewSecurityPrefs: |
67 | | * |
68 | | * Create a new security preference block |
69 | | * |
70 | | * Returns a pointer to the new block or NULL in case of error |
71 | | */ |
72 | | xsltSecurityPrefsPtr |
73 | 3.73k | xsltNewSecurityPrefs(void) { |
74 | 3.73k | xsltSecurityPrefsPtr ret; |
75 | | |
76 | 3.73k | xsltInitGlobals(); |
77 | | |
78 | 3.73k | ret = (xsltSecurityPrefsPtr) xmlMalloc(sizeof(xsltSecurityPrefs)); |
79 | 3.73k | if (ret == NULL) { |
80 | 0 | xsltTransformError(NULL, NULL, NULL, |
81 | 0 | "xsltNewSecurityPrefs : malloc failed\n"); |
82 | 0 | return(NULL); |
83 | 0 | } |
84 | 3.73k | memset(ret, 0, sizeof(xsltSecurityPrefs)); |
85 | 3.73k | return(ret); |
86 | 3.73k | } |
87 | | |
88 | | /** |
89 | | * xsltFreeSecurityPrefs: |
90 | | * @sec: the security block to free |
91 | | * |
92 | | * Free up a security preference block |
93 | | */ |
94 | | void |
95 | 0 | xsltFreeSecurityPrefs(xsltSecurityPrefsPtr sec) { |
96 | 0 | if (sec == NULL) |
97 | 0 | return; |
98 | 0 | xmlFree(sec); |
99 | 0 | } |
100 | | |
101 | | /** |
102 | | * xsltSetSecurityPrefs: |
103 | | * @sec: the security block to update |
104 | | * @option: the option to update |
105 | | * @func: the user callback to use for this option |
106 | | * |
107 | | * Update the security option to use the new callback checking function |
108 | | * |
109 | | * Returns -1 in case of error, 0 otherwise |
110 | | */ |
111 | | int |
112 | | xsltSetSecurityPrefs(xsltSecurityPrefsPtr sec, xsltSecurityOption option, |
113 | 18.6k | xsltSecurityCheck func) { |
114 | 18.6k | xsltInitGlobals(); |
115 | 18.6k | if (sec == NULL) |
116 | 0 | return(-1); |
117 | 18.6k | switch (option) { |
118 | 3.73k | case XSLT_SECPREF_READ_FILE: |
119 | 3.73k | sec->readFile = func; return(0); |
120 | 3.73k | case XSLT_SECPREF_WRITE_FILE: |
121 | 3.73k | sec->createFile = func; return(0); |
122 | 3.73k | case XSLT_SECPREF_CREATE_DIRECTORY: |
123 | 3.73k | sec->createDir = func; return(0); |
124 | 3.73k | case XSLT_SECPREF_READ_NETWORK: |
125 | 3.73k | sec->readNet = func; return(0); |
126 | 3.73k | case XSLT_SECPREF_WRITE_NETWORK: |
127 | 3.73k | sec->writeNet = func; return(0); |
128 | 18.6k | } |
129 | 0 | return(-1); |
130 | 18.6k | } |
131 | | |
132 | | /** |
133 | | * xsltGetSecurityPrefs: |
134 | | * @sec: the security block to update |
135 | | * @option: the option to lookup |
136 | | * |
137 | | * Lookup the security option to get the callback checking function |
138 | | * |
139 | | * Returns NULL if not found, the function otherwise |
140 | | */ |
141 | | xsltSecurityCheck |
142 | 5.31M | xsltGetSecurityPrefs(xsltSecurityPrefsPtr sec, xsltSecurityOption option) { |
143 | 5.31M | if (sec == NULL) |
144 | 0 | return(NULL); |
145 | 5.31M | switch (option) { |
146 | 4.62M | case XSLT_SECPREF_READ_FILE: |
147 | 4.62M | return(sec->readFile); |
148 | 0 | case XSLT_SECPREF_WRITE_FILE: |
149 | 0 | return(sec->createFile); |
150 | 0 | case XSLT_SECPREF_CREATE_DIRECTORY: |
151 | 0 | return(sec->createDir); |
152 | 692k | case XSLT_SECPREF_READ_NETWORK: |
153 | 692k | return(sec->readNet); |
154 | 0 | case XSLT_SECPREF_WRITE_NETWORK: |
155 | 0 | return(sec->writeNet); |
156 | 5.31M | } |
157 | 0 | return(NULL); |
158 | 5.31M | } |
159 | | |
160 | | /** |
161 | | * xsltSetDefaultSecurityPrefs: |
162 | | * @sec: the security block to use |
163 | | * |
164 | | * Set the default security preference application-wide |
165 | | */ |
166 | | void |
167 | 0 | xsltSetDefaultSecurityPrefs(xsltSecurityPrefsPtr sec) { |
168 | |
|
169 | 0 | xsltDefaultSecurityPrefs = sec; |
170 | 0 | } |
171 | | |
172 | | /** |
173 | | * xsltGetDefaultSecurityPrefs: |
174 | | * |
175 | | * Get the default security preference application-wide |
176 | | * |
177 | | * Returns the current xsltSecurityPrefsPtr in use or NULL if none |
178 | | */ |
179 | | xsltSecurityPrefsPtr |
180 | 3.73k | xsltGetDefaultSecurityPrefs(void) { |
181 | 3.73k | return(xsltDefaultSecurityPrefs); |
182 | 3.73k | } |
183 | | |
184 | | /** |
185 | | * xsltSetCtxtSecurityPrefs: |
186 | | * @sec: the security block to use |
187 | | * @ctxt: an XSLT transformation context |
188 | | * |
189 | | * Set the security preference for a specific transformation |
190 | | * |
191 | | * Returns -1 in case of error, 0 otherwise |
192 | | */ |
193 | | int |
194 | | xsltSetCtxtSecurityPrefs(xsltSecurityPrefsPtr sec, |
195 | 3.73k | xsltTransformContextPtr ctxt) { |
196 | 3.73k | if (ctxt == NULL) |
197 | 0 | return(-1); |
198 | 3.73k | ctxt->sec = (void *) sec; |
199 | 3.73k | return(0); |
200 | 3.73k | } |
201 | | |
202 | | |
203 | | /** |
204 | | * xsltSecurityAllow: |
205 | | * @sec: the security block to use |
206 | | * @ctxt: an XSLT transformation context |
207 | | * @value: unused |
208 | | * |
209 | | * Function used to always allow an operation |
210 | | * |
211 | | * Returns 1 always |
212 | | */ |
213 | | int |
214 | | xsltSecurityAllow(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED, |
215 | | xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, |
216 | 0 | const char *value ATTRIBUTE_UNUSED) { |
217 | 0 | return(1); |
218 | 0 | } |
219 | | |
220 | | /** |
221 | | * xsltSecurityForbid: |
222 | | * @sec: the security block to use |
223 | | * @ctxt: an XSLT transformation context |
224 | | * @value: unused |
225 | | * |
226 | | * Function used to always forbid an operation |
227 | | * |
228 | | * Returns 0 always |
229 | | */ |
230 | | int |
231 | | xsltSecurityForbid(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED, |
232 | | xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, |
233 | 5.31M | const char *value ATTRIBUTE_UNUSED) { |
234 | 5.31M | return(0); |
235 | 5.31M | } |
236 | | |
237 | | /************************************************************************ |
238 | | * * |
239 | | * Internal interfaces * |
240 | | * * |
241 | | ************************************************************************/ |
242 | | |
243 | | /** |
244 | | * xsltCheckFilename |
245 | | * @path: the path to check |
246 | | * |
247 | | * function checks to see if @path is a valid source |
248 | | * (file, socket...) for XML. |
249 | | * |
250 | | * TODO: remove at some point !!! |
251 | | * Local copy of xmlCheckFilename to avoid a hard dependency on |
252 | | * a new version of libxml2 |
253 | | * |
254 | | * if stat is not available on the target machine, |
255 | | * returns 1. if stat fails, returns 0 (if calling |
256 | | * stat on the filename fails, it can't be right). |
257 | | * if stat succeeds and the file is a directory, |
258 | | * returns 2. otherwise returns 1. |
259 | | */ |
260 | | |
261 | | static int |
262 | | xsltCheckFilename (const char *path) |
263 | 0 | { |
264 | 0 | #ifdef HAVE_STAT |
265 | 0 | struct stat stat_buffer; |
266 | | #if defined(_WIN32) |
267 | | DWORD dwAttrs; |
268 | | |
269 | | dwAttrs = GetFileAttributesA(path); |
270 | | if (dwAttrs != INVALID_FILE_ATTRIBUTES) { |
271 | | if (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) { |
272 | | return 2; |
273 | | } |
274 | | } |
275 | | #endif |
276 | |
|
277 | 0 | if (stat(path, &stat_buffer) == -1) |
278 | 0 | return 0; |
279 | | |
280 | 0 | #ifdef S_ISDIR |
281 | 0 | if (S_ISDIR(stat_buffer.st_mode)) { |
282 | 0 | return 2; |
283 | 0 | } |
284 | 0 | #endif |
285 | 0 | #endif |
286 | 0 | return 1; |
287 | 0 | } |
288 | | |
289 | | static int |
290 | | xsltCheckWritePath(xsltSecurityPrefsPtr sec, |
291 | | xsltTransformContextPtr ctxt, |
292 | | const char *path) |
293 | 0 | { |
294 | 0 | int ret; |
295 | 0 | xsltSecurityCheck check; |
296 | 0 | char *directory; |
297 | |
|
298 | 0 | check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_WRITE_FILE); |
299 | 0 | if (check != NULL) { |
300 | 0 | ret = check(sec, ctxt, path); |
301 | 0 | if (ret == 0) { |
302 | 0 | xsltTransformError(ctxt, NULL, NULL, |
303 | 0 | "File write for %s refused\n", path); |
304 | 0 | return(0); |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | 0 | directory = xmlParserGetDirectory (path); |
309 | |
|
310 | 0 | if (directory != NULL) { |
311 | 0 | ret = xsltCheckFilename(directory); |
312 | 0 | if (ret == 0) { |
313 | | /* |
314 | | * The directory doesn't exist check for creation |
315 | | */ |
316 | 0 | check = xsltGetSecurityPrefs(sec, |
317 | 0 | XSLT_SECPREF_CREATE_DIRECTORY); |
318 | 0 | if (check != NULL) { |
319 | 0 | ret = check(sec, ctxt, directory); |
320 | 0 | if (ret == 0) { |
321 | 0 | xsltTransformError(ctxt, NULL, NULL, |
322 | 0 | "Directory creation for %s refused\n", |
323 | 0 | path); |
324 | 0 | xmlFree(directory); |
325 | 0 | return(0); |
326 | 0 | } |
327 | 0 | } |
328 | 0 | ret = xsltCheckWritePath(sec, ctxt, directory); |
329 | 0 | if (ret == 1) |
330 | 0 | ret = mkdir(directory, 0755); |
331 | 0 | } |
332 | 0 | xmlFree(directory); |
333 | 0 | if (ret < 0) |
334 | 0 | return(ret); |
335 | 0 | } |
336 | | |
337 | 0 | return(1); |
338 | 0 | } |
339 | | |
340 | | /** |
341 | | * xsltCheckWrite: |
342 | | * @sec: the security options |
343 | | * @ctxt: an XSLT transformation context |
344 | | * @URL: the resource to be written |
345 | | * |
346 | | * Check if the resource is allowed to be written, if necessary makes |
347 | | * some preliminary work like creating directories |
348 | | * |
349 | | * Return 1 if write is allowed, 0 if not and -1 in case or error. |
350 | | */ |
351 | | int |
352 | | xsltCheckWrite(xsltSecurityPrefsPtr sec, |
353 | 0 | xsltTransformContextPtr ctxt, const xmlChar *URL) { |
354 | 0 | int ret; |
355 | 0 | xmlURIPtr uri; |
356 | 0 | xsltSecurityCheck check; |
357 | |
|
358 | 0 | uri = xmlParseURI((const char *)URL); |
359 | 0 | if (uri == NULL) { |
360 | 0 | uri = xmlCreateURI(); |
361 | 0 | if (uri == NULL) { |
362 | 0 | xsltTransformError(ctxt, NULL, NULL, |
363 | 0 | "xsltCheckWrite: out of memory for %s\n", URL); |
364 | 0 | return(-1); |
365 | 0 | } |
366 | 0 | uri->path = (char *)xmlStrdup(URL); |
367 | 0 | } |
368 | 0 | if ((uri->scheme == NULL) || |
369 | 0 | (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) { |
370 | |
|
371 | | #if defined(_WIN32) |
372 | | if ((uri->path)&&(uri->path[0]=='/')&& |
373 | | (uri->path[1]!='\0')&&(uri->path[2]==':')) |
374 | | ret = xsltCheckWritePath(sec, ctxt, uri->path+1); |
375 | | else |
376 | | #endif |
377 | 0 | { |
378 | | /* |
379 | | * Check if we are allowed to write this file |
380 | | */ |
381 | 0 | ret = xsltCheckWritePath(sec, ctxt, uri->path); |
382 | 0 | } |
383 | |
|
384 | 0 | if (ret <= 0) { |
385 | 0 | xmlFreeURI(uri); |
386 | 0 | return(ret); |
387 | 0 | } |
388 | 0 | } else { |
389 | | /* |
390 | | * Check if we are allowed to write this network resource |
391 | | */ |
392 | 0 | check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_WRITE_NETWORK); |
393 | 0 | if (check != NULL) { |
394 | 0 | ret = check(sec, ctxt, (const char *)URL); |
395 | 0 | if (ret == 0) { |
396 | 0 | xsltTransformError(ctxt, NULL, NULL, |
397 | 0 | "File write for %s refused\n", URL); |
398 | 0 | xmlFreeURI(uri); |
399 | 0 | return(0); |
400 | 0 | } |
401 | 0 | } |
402 | 0 | } |
403 | 0 | xmlFreeURI(uri); |
404 | 0 | return(1); |
405 | 0 | } |
406 | | |
407 | | |
408 | | /** |
409 | | * xsltCheckRead: |
410 | | * @sec: the security options |
411 | | * @ctxt: an XSLT transformation context |
412 | | * @URL: the resource to be read |
413 | | * |
414 | | * Check if the resource is allowed to be read |
415 | | * |
416 | | * Return 1 if read is allowed, 0 if not and -1 in case or error. |
417 | | */ |
418 | | int |
419 | | xsltCheckRead(xsltSecurityPrefsPtr sec, |
420 | 5.40M | xsltTransformContextPtr ctxt, const xmlChar *URL) { |
421 | 5.40M | int ret; |
422 | 5.40M | xmlURIPtr uri; |
423 | 5.40M | xsltSecurityCheck check; |
424 | | |
425 | 5.40M | uri = xmlParseURI((const char *)URL); |
426 | 5.40M | if (uri == NULL) { |
427 | 90.2k | xsltTransformError(ctxt, NULL, NULL, |
428 | 90.2k | "xsltCheckRead: URL parsing failed for %s\n", |
429 | 90.2k | URL); |
430 | 90.2k | return(-1); |
431 | 90.2k | } |
432 | 5.31M | if ((uri->scheme == NULL) || |
433 | 5.31M | (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) { |
434 | | |
435 | | /* |
436 | | * Check if we are allowed to read this file |
437 | | */ |
438 | 4.62M | check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_READ_FILE); |
439 | 4.62M | if (check != NULL) { |
440 | 4.62M | ret = check(sec, ctxt, uri->path); |
441 | 4.62M | if (ret == 0) { |
442 | 4.62M | xsltTransformError(ctxt, NULL, NULL, |
443 | 4.62M | "Local file read for %s refused\n", URL); |
444 | 4.62M | xmlFreeURI(uri); |
445 | 4.62M | return(0); |
446 | 4.62M | } |
447 | 4.62M | } |
448 | 4.62M | } else { |
449 | | /* |
450 | | * Check if we are allowed to write this network resource |
451 | | */ |
452 | 692k | check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_READ_NETWORK); |
453 | 692k | if (check != NULL) { |
454 | 692k | ret = check(sec, ctxt, (const char *)URL); |
455 | 692k | if (ret == 0) { |
456 | 692k | xsltTransformError(ctxt, NULL, NULL, |
457 | 692k | "Network file read for %s refused\n", URL); |
458 | 692k | xmlFreeURI(uri); |
459 | 692k | return(0); |
460 | 692k | } |
461 | 692k | } |
462 | 692k | } |
463 | 0 | xmlFreeURI(uri); |
464 | 0 | return(1); |
465 | 5.31M | } |
466 | | |