• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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