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