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