Coverage Report

Created: 2023-12-18 13:37

/src/libxslt/libxslt/security.c
Line
Count
Source (jump to first uncovered line)
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
#if defined(_WIN32)
22
#include <windows.h>
23
#ifndef INVALID_FILE_ATTRIBUTES
24
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
25
#endif
26
#endif
27
28
#ifndef HAVE_STAT
29
#  ifdef HAVE__STAT
30
     /* MS C library seems to define stat and _stat. The definition
31
      *         is identical. Still, mapping them to each other causes a warning. */
32
#    ifndef _MSC_VER
33
#      define stat(x,y) _stat(x,y)
34
#    endif
35
#    define HAVE_STAT
36
#  endif
37
#endif
38
39
#include <libxml/xmlmemory.h>
40
#include <libxml/tree.h>
41
#include <libxml/uri.h>
42
#include "xslt.h"
43
#include "xsltInternals.h"
44
#include "xsltutils.h"
45
#include "extensions.h"
46
#include "security.h"
47
48
49
struct _xsltSecurityPrefs {
50
    xsltSecurityCheck readFile;
51
    xsltSecurityCheck createFile;
52
    xsltSecurityCheck createDir;
53
    xsltSecurityCheck readNet;
54
    xsltSecurityCheck writeNet;
55
};
56
57
static xsltSecurityPrefsPtr xsltDefaultSecurityPrefs = NULL;
58
59
/************************************************************************
60
 *                  *
61
 *      Module interfaces       *
62
 *                  *
63
 ************************************************************************/
64
65
/**
66
 * xsltNewSecurityPrefs:
67
 *
68
 * Create a new security preference block
69
 *
70
 * Returns a pointer to the new block or NULL in case of error
71
 */
72
xsltSecurityPrefsPtr
73
3.73k
xsltNewSecurityPrefs(void) {
74
3.73k
    xsltSecurityPrefsPtr ret;
75
76
3.73k
    xsltInitGlobals();
77
78
3.73k
    ret = (xsltSecurityPrefsPtr) xmlMalloc(sizeof(xsltSecurityPrefs));
79
3.73k
    if (ret == NULL) {
80
0
  xsltTransformError(NULL, NULL, NULL,
81
0
    "xsltNewSecurityPrefs : malloc failed\n");
82
0
  return(NULL);
83
0
    }
84
3.73k
    memset(ret, 0, sizeof(xsltSecurityPrefs));
85
3.73k
    return(ret);
86
3.73k
}
87
88
/**
89
 * xsltFreeSecurityPrefs:
90
 * @sec:  the security block to free
91
 *
92
 * Free up a security preference block
93
 */
94
void
95
0
xsltFreeSecurityPrefs(xsltSecurityPrefsPtr sec) {
96
0
    if (sec == NULL)
97
0
  return;
98
0
    xmlFree(sec);
99
0
}
100
101
/**
102
 * xsltSetSecurityPrefs:
103
 * @sec:  the security block to update
104
 * @option:  the option to update
105
 * @func:  the user callback to use for this option
106
 *
107
 * Update the security option to use the new callback checking function
108
 *
109
 * Returns -1 in case of error, 0 otherwise
110
 */
111
int
112
xsltSetSecurityPrefs(xsltSecurityPrefsPtr sec, xsltSecurityOption option,
113
18.6k
                     xsltSecurityCheck func) {
114
18.6k
    xsltInitGlobals();
115
18.6k
    if (sec == NULL)
116
0
  return(-1);
117
18.6k
    switch (option) {
118
3.73k
        case XSLT_SECPREF_READ_FILE:
119
3.73k
            sec->readFile = func; return(0);
120
3.73k
        case XSLT_SECPREF_WRITE_FILE:
121
3.73k
            sec->createFile = func; return(0);
122
3.73k
        case XSLT_SECPREF_CREATE_DIRECTORY:
123
3.73k
            sec->createDir = func; return(0);
124
3.73k
        case XSLT_SECPREF_READ_NETWORK:
125
3.73k
            sec->readNet = func; return(0);
126
3.73k
        case XSLT_SECPREF_WRITE_NETWORK:
127
3.73k
            sec->writeNet = func; return(0);
128
18.6k
    }
129
0
    return(-1);
130
18.6k
}
131
132
/**
133
 * xsltGetSecurityPrefs:
134
 * @sec:  the security block to update
135
 * @option:  the option to lookup
136
 *
137
 * Lookup the security option to get the callback checking function
138
 *
139
 * Returns NULL if not found, the function otherwise
140
 */
141
xsltSecurityCheck
142
5.31M
xsltGetSecurityPrefs(xsltSecurityPrefsPtr sec, xsltSecurityOption option) {
143
5.31M
    if (sec == NULL)
144
0
  return(NULL);
145
5.31M
    switch (option) {
146
4.62M
        case XSLT_SECPREF_READ_FILE:
147
4.62M
            return(sec->readFile);
148
0
        case XSLT_SECPREF_WRITE_FILE:
149
0
            return(sec->createFile);
150
0
        case XSLT_SECPREF_CREATE_DIRECTORY:
151
0
            return(sec->createDir);
152
692k
        case XSLT_SECPREF_READ_NETWORK:
153
692k
            return(sec->readNet);
154
0
        case XSLT_SECPREF_WRITE_NETWORK:
155
0
            return(sec->writeNet);
156
5.31M
    }
157
0
    return(NULL);
158
5.31M
}
159
160
/**
161
 * xsltSetDefaultSecurityPrefs:
162
 * @sec:  the security block to use
163
 *
164
 * Set the default security preference application-wide
165
 */
166
void
167
0
xsltSetDefaultSecurityPrefs(xsltSecurityPrefsPtr sec) {
168
169
0
    xsltDefaultSecurityPrefs = sec;
170
0
}
171
172
/**
173
 * xsltGetDefaultSecurityPrefs:
174
 *
175
 * Get the default security preference application-wide
176
 *
177
 * Returns the current xsltSecurityPrefsPtr in use or NULL if none
178
 */
179
xsltSecurityPrefsPtr
180
3.73k
xsltGetDefaultSecurityPrefs(void) {
181
3.73k
    return(xsltDefaultSecurityPrefs);
182
3.73k
}
183
184
/**
185
 * xsltSetCtxtSecurityPrefs:
186
 * @sec:  the security block to use
187
 * @ctxt:  an XSLT transformation context
188
 *
189
 * Set the security preference for a specific transformation
190
 *
191
 * Returns -1 in case of error, 0 otherwise
192
 */
193
int
194
xsltSetCtxtSecurityPrefs(xsltSecurityPrefsPtr sec,
195
3.73k
                   xsltTransformContextPtr ctxt) {
196
3.73k
    if (ctxt == NULL)
197
0
  return(-1);
198
3.73k
    ctxt->sec = (void *) sec;
199
3.73k
    return(0);
200
3.73k
}
201
202
203
/**
204
 * xsltSecurityAllow:
205
 * @sec:  the security block to use
206
 * @ctxt:  an XSLT transformation context
207
 * @value:  unused
208
 *
209
 * Function used to always allow an operation
210
 *
211
 * Returns 1 always
212
 */
213
int
214
xsltSecurityAllow(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED,
215
            xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
216
0
      const char *value ATTRIBUTE_UNUSED) {
217
0
    return(1);
218
0
}
219
220
/**
221
 * xsltSecurityForbid:
222
 * @sec:  the security block to use
223
 * @ctxt:  an XSLT transformation context
224
 * @value:  unused
225
 *
226
 * Function used to always forbid an operation
227
 *
228
 * Returns 0 always
229
 */
230
int
231
xsltSecurityForbid(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED,
232
            xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
233
5.31M
      const char *value ATTRIBUTE_UNUSED) {
234
5.31M
    return(0);
235
5.31M
}
236
237
/************************************************************************
238
 *                  *
239
 *      Internal interfaces       *
240
 *                  *
241
 ************************************************************************/
242
243
/**
244
 * xsltCheckFilename
245
 * @path:  the path to check
246
 *
247
 * function checks to see if @path is a valid source
248
 * (file, socket...) for XML.
249
 *
250
 * TODO: remove at some point !!!
251
 * Local copy of xmlCheckFilename to avoid a hard dependency on
252
 * a new version of libxml2
253
 *
254
 * if stat is not available on the target machine,
255
 * returns 1.  if stat fails, returns 0 (if calling
256
 * stat on the filename fails, it can't be right).
257
 * if stat succeeds and the file is a directory,
258
 * returns 2.  otherwise returns 1.
259
 */
260
261
static int
262
xsltCheckFilename (const char *path)
263
0
{
264
0
#ifdef HAVE_STAT
265
0
    struct stat stat_buffer;
266
#if defined(_WIN32)
267
    DWORD dwAttrs;
268
269
    dwAttrs = GetFileAttributesA(path);
270
    if (dwAttrs != INVALID_FILE_ATTRIBUTES) {
271
        if (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) {
272
            return 2;
273
    }
274
    }
275
#endif
276
277
0
    if (stat(path, &stat_buffer) == -1)
278
0
        return 0;
279
280
0
#ifdef S_ISDIR
281
0
    if (S_ISDIR(stat_buffer.st_mode)) {
282
0
        return 2;
283
0
    }
284
0
#endif
285
0
#endif
286
0
    return 1;
287
0
}
288
289
static int
290
xsltCheckWritePath(xsltSecurityPrefsPtr sec,
291
       xsltTransformContextPtr ctxt,
292
       const char *path)
293
0
{
294
0
    int ret;
295
0
    xsltSecurityCheck check;
296
0
    char *directory;
297
298
0
    check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_WRITE_FILE);
299
0
    if (check != NULL) {
300
0
  ret = check(sec, ctxt, path);
301
0
  if (ret == 0) {
302
0
      xsltTransformError(ctxt, NULL, NULL,
303
0
             "File write for %s refused\n", path);
304
0
      return(0);
305
0
  }
306
0
    }
307
308
0
    directory = xmlParserGetDirectory (path);
309
310
0
    if (directory != NULL) {
311
0
  ret = xsltCheckFilename(directory);
312
0
  if (ret == 0) {
313
      /*
314
       * The directory doesn't exist check for creation
315
       */
316
0
      check = xsltGetSecurityPrefs(sec,
317
0
           XSLT_SECPREF_CREATE_DIRECTORY);
318
0
      if (check != NULL) {
319
0
    ret = check(sec, ctxt, directory);
320
0
    if (ret == 0) {
321
0
        xsltTransformError(ctxt, NULL, NULL,
322
0
               "Directory creation for %s refused\n",
323
0
               path);
324
0
        xmlFree(directory);
325
0
        return(0);
326
0
    }
327
0
      }
328
0
      ret = xsltCheckWritePath(sec, ctxt, directory);
329
0
      if (ret == 1)
330
0
    ret = mkdir(directory, 0755);
331
0
  }
332
0
  xmlFree(directory);
333
0
  if (ret < 0)
334
0
      return(ret);
335
0
    }
336
337
0
    return(1);
338
0
}
339
340
/**
341
 * xsltCheckWrite:
342
 * @sec:  the security options
343
 * @ctxt:  an XSLT transformation context
344
 * @URL:  the resource to be written
345
 *
346
 * Check if the resource is allowed to be written, if necessary makes
347
 * some preliminary work like creating directories
348
 *
349
 * Return 1 if write is allowed, 0 if not and -1 in case or error.
350
 */
351
int
352
xsltCheckWrite(xsltSecurityPrefsPtr sec,
353
0
         xsltTransformContextPtr ctxt, const xmlChar *URL) {
354
0
    int ret;
355
0
    xmlURIPtr uri;
356
0
    xsltSecurityCheck check;
357
358
0
    uri = xmlParseURI((const char *)URL);
359
0
    if (uri == NULL) {
360
0
        uri = xmlCreateURI();
361
0
  if (uri == NULL) {
362
0
      xsltTransformError(ctxt, NULL, NULL,
363
0
       "xsltCheckWrite: out of memory for %s\n", URL);
364
0
      return(-1);
365
0
  }
366
0
  uri->path = (char *)xmlStrdup(URL);
367
0
    }
368
0
    if ((uri->scheme == NULL) ||
369
0
  (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) {
370
371
#if defined(_WIN32)
372
        if ((uri->path)&&(uri->path[0]=='/')&&
373
            (uri->path[1]!='\0')&&(uri->path[2]==':'))
374
            ret = xsltCheckWritePath(sec, ctxt, uri->path+1);
375
        else
376
#endif
377
0
        {
378
            /*
379
             * Check if we are allowed to write this file
380
             */
381
0
      ret = xsltCheckWritePath(sec, ctxt, uri->path);
382
0
        }
383
384
0
  if (ret <= 0) {
385
0
      xmlFreeURI(uri);
386
0
      return(ret);
387
0
  }
388
0
    } else {
389
  /*
390
   * Check if we are allowed to write this network resource
391
   */
392
0
  check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_WRITE_NETWORK);
393
0
  if (check != NULL) {
394
0
      ret = check(sec, ctxt, (const char *)URL);
395
0
      if (ret == 0) {
396
0
    xsltTransformError(ctxt, NULL, NULL,
397
0
           "File write for %s refused\n", URL);
398
0
    xmlFreeURI(uri);
399
0
    return(0);
400
0
      }
401
0
  }
402
0
    }
403
0
    xmlFreeURI(uri);
404
0
    return(1);
405
0
}
406
407
408
/**
409
 * xsltCheckRead:
410
 * @sec:  the security options
411
 * @ctxt: an XSLT transformation context
412
 * @URL:  the resource to be read
413
 *
414
 * Check if the resource is allowed to be read
415
 *
416
 * Return 1 if read is allowed, 0 if not and -1 in case or error.
417
 */
418
int
419
xsltCheckRead(xsltSecurityPrefsPtr sec,
420
5.40M
        xsltTransformContextPtr ctxt, const xmlChar *URL) {
421
5.40M
    int ret;
422
5.40M
    xmlURIPtr uri;
423
5.40M
    xsltSecurityCheck check;
424
425
5.40M
    uri = xmlParseURI((const char *)URL);
426
5.40M
    if (uri == NULL) {
427
90.2k
  xsltTransformError(ctxt, NULL, NULL,
428
90.2k
   "xsltCheckRead: URL parsing failed for %s\n",
429
90.2k
       URL);
430
90.2k
  return(-1);
431
90.2k
    }
432
5.31M
    if ((uri->scheme == NULL) ||
433
5.31M
  (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) {
434
435
  /*
436
   * Check if we are allowed to read this file
437
   */
438
4.62M
  check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_READ_FILE);
439
4.62M
  if (check != NULL) {
440
4.62M
      ret = check(sec, ctxt, uri->path);
441
4.62M
      if (ret == 0) {
442
4.62M
    xsltTransformError(ctxt, NULL, NULL,
443
4.62M
           "Local file read for %s refused\n", URL);
444
4.62M
    xmlFreeURI(uri);
445
4.62M
    return(0);
446
4.62M
      }
447
4.62M
  }
448
4.62M
    } else {
449
  /*
450
   * Check if we are allowed to write this network resource
451
   */
452
692k
  check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_READ_NETWORK);
453
692k
  if (check != NULL) {
454
692k
      ret = check(sec, ctxt, (const char *)URL);
455
692k
      if (ret == 0) {
456
692k
    xsltTransformError(ctxt, NULL, NULL,
457
692k
           "Network file read for %s refused\n", URL);
458
692k
    xmlFreeURI(uri);
459
692k
    return(0);
460
692k
      }
461
692k
  }
462
692k
    }
463
0
    xmlFreeURI(uri);
464
0
    return(1);
465
5.31M
}
466