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