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 = GetFileAttributes(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