| OLD | NEW |
| (Empty) |
| 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 = 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 | |
| OLD | NEW |