Home | History | Annotate | Download | only in libxslt
      1 /*
      2  * security.c: Implementation of the XSLT security framework
      3  *
      4  * See Copyright for the status of this software.
      5  *
      6  * daniel (at) 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 
    481