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