| /* |
| * security.c: Implementation of the XSLT security framework |
| * |
| * See Copyright for the status of this software. |
| * |
| * daniel@veillard.com |
| */ |
| |
| #define IN_LIBXSLT |
| #include "libxslt.h" |
| |
| #include <string.h> |
| |
| #ifdef HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| #ifdef HAVE_SYS_STAT_H |
| #include <sys/stat.h> |
| #endif |
| |
| #ifdef HAVE_MATH_H |
| #include <math.h> |
| #endif |
| #ifdef HAVE_FLOAT_H |
| #include <float.h> |
| #endif |
| #ifdef HAVE_IEEEFP_H |
| #include <ieeefp.h> |
| #endif |
| #ifdef HAVE_NAN_H |
| #include <nan.h> |
| #endif |
| #ifdef HAVE_CTYPE_H |
| #include <ctype.h> |
| #endif |
| |
| #if defined(WIN32) && !defined(__CYGWIN__) |
| #include <windows.h> |
| #ifndef INVALID_FILE_ATTRIBUTES |
| #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) |
| #endif |
| #endif |
| |
| #ifndef HAVE_STAT |
| # ifdef HAVE__STAT |
| /* MS C library seems to define stat and _stat. The definition |
| * is identical. Still, mapping them to each other causes a warning. */ |
| # ifndef _MSC_VER |
| # define stat(x,y) _stat(x,y) |
| # endif |
| # define HAVE_STAT |
| # endif |
| #endif |
| |
| #include <libxml/xmlmemory.h> |
| #include <libxml/tree.h> |
| #include <libxml/uri.h> |
| #include "xslt.h" |
| #include "xsltInternals.h" |
| #include "xsltutils.h" |
| #include "extensions.h" |
| #include "security.h" |
| |
| |
| struct _xsltSecurityPrefs { |
| xsltSecurityCheck readFile; |
| xsltSecurityCheck createFile; |
| xsltSecurityCheck createDir; |
| xsltSecurityCheck readNet; |
| xsltSecurityCheck writeNet; |
| }; |
| |
| static xsltSecurityPrefsPtr xsltDefaultSecurityPrefs = NULL; |
| |
| /************************************************************************ |
| * * |
| * Module interfaces * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltNewSecurityPrefs: |
| * |
| * Create a new security preference block |
| * |
| * Returns a pointer to the new block or NULL in case of error |
| */ |
| xsltSecurityPrefsPtr |
| xsltNewSecurityPrefs(void) { |
| xsltSecurityPrefsPtr ret; |
| |
| xsltInitGlobals(); |
| |
| ret = (xsltSecurityPrefsPtr) xmlMalloc(sizeof(xsltSecurityPrefs)); |
| if (ret == NULL) { |
| xsltTransformError(NULL, NULL, NULL, |
| "xsltNewSecurityPrefs : malloc failed\n"); |
| return(NULL); |
| } |
| memset(ret, 0, sizeof(xsltSecurityPrefs)); |
| return(ret); |
| } |
| |
| /** |
| * xsltFreeSecurityPrefs: |
| * @sec: the security block to free |
| * |
| * Free up a security preference block |
| */ |
| void |
| xsltFreeSecurityPrefs(xsltSecurityPrefsPtr sec) { |
| if (sec == NULL) |
| return; |
| xmlFree(sec); |
| } |
| |
| /** |
| * xsltSetSecurityPrefs: |
| * @sec: the security block to update |
| * @option: the option to update |
| * @func: the user callback to use for this option |
| * |
| * Update the security option to use the new callback checking function |
| * |
| * Returns -1 in case of error, 0 otherwise |
| */ |
| int |
| xsltSetSecurityPrefs(xsltSecurityPrefsPtr sec, xsltSecurityOption option, |
| xsltSecurityCheck func) { |
| xsltInitGlobals(); |
| if (sec == NULL) |
| return(-1); |
| switch (option) { |
| case XSLT_SECPREF_READ_FILE: |
| sec->readFile = func; return(0); |
| case XSLT_SECPREF_WRITE_FILE: |
| sec->createFile = func; return(0); |
| case XSLT_SECPREF_CREATE_DIRECTORY: |
| sec->createDir = func; return(0); |
| case XSLT_SECPREF_READ_NETWORK: |
| sec->readNet = func; return(0); |
| case XSLT_SECPREF_WRITE_NETWORK: |
| sec->writeNet = func; return(0); |
| } |
| return(-1); |
| } |
| |
| /** |
| * xsltGetSecurityPrefs: |
| * @sec: the security block to update |
| * @option: the option to lookup |
| * |
| * Lookup the security option to get the callback checking function |
| * |
| * Returns NULL if not found, the function otherwise |
| */ |
| xsltSecurityCheck |
| xsltGetSecurityPrefs(xsltSecurityPrefsPtr sec, xsltSecurityOption option) { |
| if (sec == NULL) |
| return(NULL); |
| switch (option) { |
| case XSLT_SECPREF_READ_FILE: |
| return(sec->readFile); |
| case XSLT_SECPREF_WRITE_FILE: |
| return(sec->createFile); |
| case XSLT_SECPREF_CREATE_DIRECTORY: |
| return(sec->createDir); |
| case XSLT_SECPREF_READ_NETWORK: |
| return(sec->readNet); |
| case XSLT_SECPREF_WRITE_NETWORK: |
| return(sec->writeNet); |
| } |
| return(NULL); |
| } |
| |
| /** |
| * xsltSetDefaultSecurityPrefs: |
| * @sec: the security block to use |
| * |
| * Set the default security preference application-wide |
| */ |
| void |
| xsltSetDefaultSecurityPrefs(xsltSecurityPrefsPtr sec) { |
| |
| xsltDefaultSecurityPrefs = sec; |
| } |
| |
| /** |
| * xsltGetDefaultSecurityPrefs: |
| * |
| * Get the default security preference application-wide |
| * |
| * Returns the current xsltSecurityPrefsPtr in use or NULL if none |
| */ |
| xsltSecurityPrefsPtr |
| xsltGetDefaultSecurityPrefs(void) { |
| return(xsltDefaultSecurityPrefs); |
| } |
| |
| /** |
| * xsltSetCtxtSecurityPrefs: |
| * @sec: the security block to use |
| * @ctxt: an XSLT transformation context |
| * |
| * Set the security preference for a specific transformation |
| * |
| * Returns -1 in case of error, 0 otherwise |
| */ |
| int |
| xsltSetCtxtSecurityPrefs(xsltSecurityPrefsPtr sec, |
| xsltTransformContextPtr ctxt) { |
| if (ctxt == NULL) |
| return(-1); |
| ctxt->sec = (void *) sec; |
| return(0); |
| } |
| |
| |
| /** |
| * xsltSecurityAllow: |
| * @sec: the security block to use |
| * @ctxt: an XSLT transformation context |
| * @value: unused |
| * |
| * Function used to always allow an operation |
| * |
| * Returns 1 always |
| */ |
| int |
| xsltSecurityAllow(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED, |
| xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, |
| const char *value ATTRIBUTE_UNUSED) { |
| return(1); |
| } |
| |
| /** |
| * xsltSecurityForbid: |
| * @sec: the security block to use |
| * @ctxt: an XSLT transformation context |
| * @value: unused |
| * |
| * Function used to always forbid an operation |
| * |
| * Returns 0 always |
| */ |
| int |
| xsltSecurityForbid(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED, |
| xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, |
| const char *value ATTRIBUTE_UNUSED) { |
| return(0); |
| } |
| |
| /************************************************************************ |
| * * |
| * Internal interfaces * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltCheckFilename |
| * @path: the path to check |
| * |
| * function checks to see if @path is a valid source |
| * (file, socket...) for XML. |
| * |
| * TODO: remove at some point !!! |
| * Local copy of xmlCheckFilename to avoid a hard dependency on |
| * a new version of libxml2 |
| * |
| * if stat is not available on the target machine, |
| * returns 1. if stat fails, returns 0 (if calling |
| * stat on the filename fails, it can't be right). |
| * if stat succeeds and the file is a directory, |
| * returns 2. otherwise returns 1. |
| */ |
| |
| static int |
| xsltCheckFilename (const char *path) |
| { |
| #ifdef HAVE_STAT |
| struct stat stat_buffer; |
| #if defined(WIN32) && !defined(__CYGWIN__) |
| DWORD dwAttrs; |
| |
| dwAttrs = GetFileAttributes(path); |
| if (dwAttrs != INVALID_FILE_ATTRIBUTES) { |
| if (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) { |
| return 2; |
| } |
| } |
| #endif |
| |
| if (stat(path, &stat_buffer) == -1) |
| return 0; |
| |
| #ifdef S_ISDIR |
| if (S_ISDIR(stat_buffer.st_mode)) { |
| return 2; |
| } |
| #endif |
| #endif |
| return 1; |
| } |
| |
| static int |
| xsltCheckWritePath(xsltSecurityPrefsPtr sec, |
| xsltTransformContextPtr ctxt, |
| const char *path) |
| { |
| int ret; |
| xsltSecurityCheck check; |
| char *directory; |
| |
| check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_WRITE_FILE); |
| if (check != NULL) { |
| ret = check(sec, ctxt, path); |
| if (ret == 0) { |
| xsltTransformError(ctxt, NULL, NULL, |
| "File write for %s refused\n", path); |
| return(0); |
| } |
| } |
| |
| directory = xmlParserGetDirectory (path); |
| |
| if (directory != NULL) { |
| ret = xsltCheckFilename(directory); |
| if (ret == 0) { |
| /* |
| * The directory doesn't exist check for creation |
| */ |
| check = xsltGetSecurityPrefs(sec, |
| XSLT_SECPREF_CREATE_DIRECTORY); |
| if (check != NULL) { |
| ret = check(sec, ctxt, directory); |
| if (ret == 0) { |
| xsltTransformError(ctxt, NULL, NULL, |
| "Directory creation for %s refused\n", |
| path); |
| xmlFree(directory); |
| return(0); |
| } |
| } |
| ret = xsltCheckWritePath(sec, ctxt, directory); |
| if (ret == 1) |
| ret = mkdir(directory, 0755); |
| } |
| xmlFree(directory); |
| if (ret < 0) |
| return(ret); |
| } |
| |
| return(1); |
| } |
| |
| /** |
| * xsltCheckWrite: |
| * @sec: the security options |
| * @ctxt: an XSLT transformation context |
| * @URL: the resource to be written |
| * |
| * Check if the resource is allowed to be written, if necessary makes |
| * some preliminary work like creating directories |
| * |
| * Return 1 if write is allowed, 0 if not and -1 in case or error. |
| */ |
| int |
| xsltCheckWrite(xsltSecurityPrefsPtr sec, |
| xsltTransformContextPtr ctxt, const xmlChar *URL) { |
| int ret; |
| xmlURIPtr uri; |
| xsltSecurityCheck check; |
| |
| uri = xmlParseURI((const char *)URL); |
| if (uri == NULL) { |
| uri = xmlCreateURI(); |
| if (uri == NULL) { |
| xsltTransformError(ctxt, NULL, NULL, |
| "xsltCheckWrite: out of memory for %s\n", URL); |
| return(-1); |
| } |
| uri->path = (char *)xmlStrdup(URL); |
| } |
| if ((uri->scheme == NULL) || |
| (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) { |
| |
| #if defined(WIN32) && !defined(__CYGWIN__) |
| if ((uri->path)&&(uri->path[0]=='/')&& |
| (uri->path[1]!='\0')&&(uri->path[2]==':')) |
| ret = xsltCheckWritePath(sec, ctxt, uri->path+1); |
| else |
| #endif |
| |
| /* |
| * Check if we are allowed to write this file |
| */ |
| ret = xsltCheckWritePath(sec, ctxt, uri->path); |
| if (ret <= 0) { |
| xmlFreeURI(uri); |
| return(ret); |
| } |
| } else { |
| /* |
| * Check if we are allowed to write this network resource |
| */ |
| check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_WRITE_NETWORK); |
| if (check != NULL) { |
| ret = check(sec, ctxt, (const char *)URL); |
| if (ret == 0) { |
| xsltTransformError(ctxt, NULL, NULL, |
| "File write for %s refused\n", URL); |
| xmlFreeURI(uri); |
| return(0); |
| } |
| } |
| } |
| xmlFreeURI(uri); |
| return(1); |
| } |
| |
| |
| /** |
| * xsltCheckRead: |
| * @sec: the security options |
| * @ctxt: an XSLT transformation context |
| * @URL: the resource to be read |
| * |
| * Check if the resource is allowed to be read |
| * |
| * Return 1 if read is allowed, 0 if not and -1 in case or error. |
| */ |
| int |
| xsltCheckRead(xsltSecurityPrefsPtr sec, |
| xsltTransformContextPtr ctxt, const xmlChar *URL) { |
| int ret; |
| xmlURIPtr uri; |
| xsltSecurityCheck check; |
| |
| uri = xmlParseURI((const char *)URL); |
| if (uri == NULL) { |
| xsltTransformError(ctxt, NULL, NULL, |
| "xsltCheckRead: URL parsing failed for %s\n", |
| URL); |
| return(-1); |
| } |
| if ((uri->scheme == NULL) || |
| (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) { |
| |
| /* |
| * Check if we are allowed to read this file |
| */ |
| check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_READ_FILE); |
| if (check != NULL) { |
| ret = check(sec, ctxt, uri->path); |
| if (ret == 0) { |
| xsltTransformError(ctxt, NULL, NULL, |
| "Local file read for %s refused\n", URL); |
| xmlFreeURI(uri); |
| return(0); |
| } |
| } |
| } else { |
| /* |
| * Check if we are allowed to write this network resource |
| */ |
| check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_READ_NETWORK); |
| if (check != NULL) { |
| ret = check(sec, ctxt, (const char *)URL); |
| if (ret == 0) { |
| xsltTransformError(ctxt, NULL, NULL, |
| "Network file read for %s refused\n", URL); |
| xmlFreeURI(uri); |
| return(0); |
| } |
| } |
| } |
| xmlFreeURI(uri); |
| return(1); |
| } |
| |