--- /dev/null
+/*
+ * security.c: Implementation of the XSLT security framework
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#include "precomp.h"
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if defined(WIN32) && !defined(__CYGWIN__)
+#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
+
+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);
+}
+