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
xsltNewSecurityPrefs(void)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
xsltFreeSecurityPrefs(xsltSecurityPrefsPtr sec)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
xsltSetSecurityPrefs(xsltSecurityPrefsPtr sec,xsltSecurityOption option,xsltSecurityCheck func)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
xsltGetSecurityPrefs(xsltSecurityPrefsPtr sec,xsltSecurityOption option)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
xsltSetDefaultSecurityPrefs(xsltSecurityPrefsPtr sec)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
xsltGetDefaultSecurityPrefs(void)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
xsltSetCtxtSecurityPrefs(xsltSecurityPrefsPtr sec,xsltTransformContextPtr ctxt)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
xsltSecurityAllow(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED,xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,const char * value ATTRIBUTE_UNUSED)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
xsltSecurityForbid(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED,xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,const char * value ATTRIBUTE_UNUSED)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
xsltCheckFilename(const char * path)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
xsltCheckWritePath(xsltSecurityPrefsPtr sec,xsltTransformContextPtr ctxt,const char * path)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
xsltCheckWrite(xsltSecurityPrefsPtr sec,xsltTransformContextPtr ctxt,const xmlChar * URL)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
xsltCheckRead(xsltSecurityPrefsPtr sec,xsltTransformContextPtr ctxt,const xmlChar * URL)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
481