Coverage Report

Created: 2026-07-20 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/MagickCore/policy.c
Line
Count
Source
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                  PPPP    OOO   L      IIIII   CCCC  Y   Y                   %
6
%                  P   P  O   O  L        I    C       Y Y                    %
7
%                  PPPP   O   O  L        I    C        Y                     %
8
%                  P      O   O  L        I    C        Y                     %
9
%                  P       OOO   LLLLL  IIIII   CCCC    Y                     %
10
%                                                                             %
11
%                                                                             %
12
%                         MagickCore Policy Methods                           %
13
%                                                                             %
14
%                              Software Design                                %
15
%                                   Cristy                                    %
16
%                                 July 1992                                   %
17
%                                                                             %
18
%                                                                             %
19
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
20
%  dedicated to making software imaging solutions freely available.           %
21
%                                                                             %
22
%  You may not use this file except in compliance with the License.  You may  %
23
%  obtain a copy of the License at                                            %
24
%                                                                             %
25
%    https://imagemagick.org/license/                                         %
26
%                                                                             %
27
%  Unless required by applicable law or agreed to in writing, software        %
28
%  distributed under the License is distributed on an "AS IS" BASIS,          %
29
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
30
%  See the License for the specific language governing permissions and        %
31
%  limitations under the License.                                             %
32
%                                                                             %
33
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34
%
35
*/
36

37
/*
38
  Include declarations.
39
*/
40
#include "MagickCore/studio.h"
41
#include "MagickCore/cache-private.h"
42
#include "MagickCore/client.h"
43
#include "MagickCore/configure.h"
44
#include "MagickCore/configure-private.h"
45
#include "MagickCore/exception.h"
46
#include "MagickCore/exception-private.h"
47
#include "MagickCore/linked-list-private.h"
48
#include "MagickCore/magick-private.h"
49
#include "MagickCore/memory_.h"
50
#include "MagickCore/memory-private.h"
51
#include "MagickCore/monitor.h"
52
#include "MagickCore/monitor-private.h"
53
#include "MagickCore/option.h"
54
#include "MagickCore/policy.h"
55
#include "MagickCore/policy-private.h"
56
#include "MagickCore/resource_.h"
57
#include "MagickCore/resource-private.h"
58
#include "MagickCore/semaphore.h"
59
#include "MagickCore/stream-private.h"
60
#include "MagickCore/string_.h"
61
#include "MagickCore/string-private.h"
62
#include "MagickCore/token.h"
63
#include "MagickCore/timer-private.h"
64
#include "MagickCore/utility.h"
65
#include "MagickCore/utility-private.h"
66
#include "MagickCore/xml-tree.h"
67
#include "MagickCore/xml-tree-private.h"
68
#if defined(MAGICKCORE_XML_DELEGATE)
69
#  include <libxml/parser.h>
70
#  include <libxml/tree.h>
71
#endif
72

73
/*
74
  Define declarations.
75
*/
76
295
#define PolicyFilename  "policy.xml"
77

78
/*
79
  Typedef declarations.
80
*/
81
struct _PolicyInfo
82
{
83
  char
84
    *path;
85
86
  PolicyDomain
87
    domain;
88
89
  PolicyRights
90
    rights;
91
92
  char
93
    *name,
94
    *pattern,
95
    *value;
96
97
  MagickBooleanType
98
    exempt,
99
    stealth,
100
    debug;
101
102
  SemaphoreInfo
103
    *semaphore;
104
105
  size_t
106
    signature;
107
};
108
109
typedef struct _PolicyMapInfo
110
{
111
  const PolicyDomain
112
    domain;
113
114
  const PolicyRights
115
    rights;
116
117
  const char
118
    *name,
119
    *pattern,
120
    *value;
121
} PolicyMapInfo;
122

123
/*
124
  Static declarations.
125
*/
126
static const PolicyMapInfo
127
  PolicyMap[] =
128
  {
129
    { UndefinedPolicyDomain, UndefinedPolicyRights, (const char *) NULL,
130
      (const char *) NULL, (const char *) NULL }
131
  };
132
133
static LinkedListInfo
134
  *policy_cache = (LinkedListInfo *) NULL;
135
136
static SemaphoreInfo
137
  *policy_semaphore = (SemaphoreInfo *) NULL;
138

139
/*
140
  Forward declarations.
141
*/
142
static MagickBooleanType
143
  IsPolicyCacheInstantiated(ExceptionInfo *),
144
  LoadPolicyCache(LinkedListInfo *,const char *,const char *,const size_t,
145
    ExceptionInfo *);
146

147
/*
148
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
149
%                                                                             %
150
%                                                                             %
151
%                                                                             %
152
%  A c q u i r e P o l i c y C a c h e                                        %
153
%                                                                             %
154
%                                                                             %
155
%                                                                             %
156
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
157
%
158
%  AcquirePolicyCache() caches one or more policy configurations which provides
159
%  a mapping between policy attributes and a policy name.
160
%
161
%  The format of the AcquirePolicyCache method is:
162
%
163
%      LinkedListInfo *AcquirePolicyCache(const char *filename,
164
%        ExceptionInfo *exception)
165
%
166
%  A description of each parameter follows:
167
%
168
%    o filename: the policy configuration file name.
169
%
170
%    o exception: return any errors or warnings in this structure.
171
%
172
*/
173
static LinkedListInfo *AcquirePolicyCache(const char *filename,
174
  ExceptionInfo *exception)
175
295
{
176
295
  LinkedListInfo
177
295
    *cache;
178
179
295
  MagickBooleanType
180
295
    status;
181
182
295
  ssize_t
183
295
    i;
184
185
  /*
186
    Load external policy map.
187
  */
188
295
  cache=NewLinkedList(0);
189
295
  status=MagickTrue;
190
#if MAGICKCORE_ZERO_CONFIGURATION_SUPPORT
191
  magick_unreferenced(filename);
192
  status=LoadPolicyCache(cache,ZeroConfigurationPolicy,"[zero-configuration]",0,
193
    exception);
194
  if (status == MagickFalse)
195
    CatchException(exception);
196
#else
197
295
  {
198
295
    const StringInfo
199
295
      *option;
200
201
295
    LinkedListInfo
202
295
      *options;
203
204
295
    options=GetConfigureOptions(filename,exception);
205
295
    option=(const StringInfo *) GetNextValueInLinkedList(options);
206
295
    while (option != (const StringInfo *) NULL)
207
0
    {
208
0
      status=LoadPolicyCache(cache,(const char *) GetStringInfoDatum(option),
209
0
        GetStringInfoPath(option),0,exception);
210
0
      if (status == MagickFalse)
211
0
        CatchException(exception);
212
0
      option=(const StringInfo *) GetNextValueInLinkedList(options);
213
0
    }
214
295
    options=DestroyConfigureOptions(options);
215
295
  }
216
295
#endif
217
  /*
218
    Load built-in policy map.
219
  */
220
590
  for (i=0; i < (ssize_t) (sizeof(PolicyMap)/sizeof(*PolicyMap)); i++)
221
295
  {
222
295
    const PolicyMapInfo
223
295
      *p;
224
225
295
    PolicyInfo
226
295
      *policy_info;
227
228
295
    p=PolicyMap+i;
229
295
    policy_info=(PolicyInfo *) AcquireMagickMemory(sizeof(*policy_info));
230
295
    if (policy_info == (PolicyInfo *) NULL)
231
0
      {
232
0
        (void) ThrowMagickException(exception,GetMagickModule(),
233
0
          ResourceLimitError,"MemoryAllocationFailed","`%s'",
234
0
          p->name == (char *) NULL ? "" : p->name);
235
0
        CatchException(exception);
236
0
        continue;
237
0
      }
238
295
    (void) memset(policy_info,0,sizeof(*policy_info));
239
295
    policy_info->path=(char *) "[built-in]";
240
295
    policy_info->domain=p->domain;
241
295
    policy_info->rights=p->rights;
242
295
    policy_info->name=(char *) p->name;
243
295
    policy_info->pattern=(char *) p->pattern;
244
295
    policy_info->value=(char *) p->value;
245
295
    policy_info->exempt=MagickTrue;
246
295
    policy_info->signature=MagickCoreSignature;
247
295
    status=AppendValueToLinkedList(cache,policy_info);
248
295
    if (status == MagickFalse)
249
0
      {
250
0
        (void) ThrowMagickException(exception,GetMagickModule(),
251
0
          ResourceLimitError,"MemoryAllocationFailed","`%s'",
252
0
          p->name == (char *) NULL ? "" : p->name);
253
0
        CatchException(exception);
254
0
      }
255
295
  }
256
295
  return(cache);
257
295
}
258

259
/*
260
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261
%                                                                             %
262
%                                                                             %
263
%                                                                             %
264
+   G e t P o l i c y I n f o                                                 %
265
%                                                                             %
266
%                                                                             %
267
%                                                                             %
268
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269
%
270
%  GetPolicyInfo() searches the policy list for the specified name and if found
271
%  returns attributes for that policy.
272
%
273
%  The format of the GetPolicyInfo method is:
274
%
275
%      PolicyInfo *GetPolicyInfo(const char *name,ExceptionInfo *exception)
276
%
277
%  A description of each parameter follows:
278
%
279
%    o name: the policy name.
280
%
281
%    o exception: return any errors or warnings in this structure.
282
%
283
*/
284
static PolicyInfo *GetPolicyInfo(const char *name,ExceptionInfo *exception)
285
3.44M
{
286
3.44M
  char
287
3.44M
    policyname[MagickPathExtent],
288
3.44M
    *q;
289
290
3.44M
  ElementInfo
291
3.44M
    *p;
292
293
3.44M
  PolicyDomain
294
3.44M
    domain;
295
296
3.44M
  PolicyInfo
297
3.44M
    *policy;
298
299
3.44M
  assert(exception != (ExceptionInfo *) NULL);
300
3.44M
  if (IsPolicyCacheInstantiated(exception) == MagickFalse)
301
0
    return((PolicyInfo *) NULL);
302
  /*
303
    Strip names of whitespace.
304
  */
305
3.44M
  *policyname='\0';
306
3.44M
  if (name != (const char *) NULL)
307
3.44M
    (void) CopyMagickString(policyname,name,MagickPathExtent);
308
63.4M
  for (q=policyname; *q != '\0'; q++)
309
60.0M
  {
310
60.0M
    if (isspace((int) ((unsigned char) *q)) == 0)
311
60.0M
      continue;
312
0
    (void) CopyMagickString(q,q+1,MagickPathExtent);
313
0
    q--;
314
0
  }
315
  /*
316
    Strip domain from policy name (e.g. resource:map).
317
  */
318
3.44M
  domain=UndefinedPolicyDomain;
319
21.6M
  for (q=policyname; *q != '\0'; q++)
320
21.6M
  {
321
21.6M
    if (*q != ':')
322
18.1M
      continue;
323
3.44M
    *q='\0';
324
3.44M
    domain=(PolicyDomain) ParseCommandOption(MagickPolicyDomainOptions,
325
3.44M
      MagickTrue,policyname);
326
3.44M
    (void) CopyMagickString(policyname,q+1,MagickPathExtent);
327
3.44M
    break;
328
21.6M
  }
329
  /*
330
    Search for policy tag.
331
  */
332
3.44M
  policy=(PolicyInfo *) NULL;
333
3.44M
  LockSemaphoreInfo(policy_semaphore);
334
3.44M
  ResetLinkedListIterator(policy_cache);
335
3.44M
  p=GetHeadElementInLinkedList(policy_cache);
336
3.44M
  if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
337
0
    {
338
0
      UnlockSemaphoreInfo(policy_semaphore);
339
0
      if (p != (ElementInfo *) NULL)
340
0
        policy=(PolicyInfo *) p->value;
341
0
      return(policy);
342
0
    }
343
6.89M
  while (p != (ElementInfo *) NULL)
344
3.44M
  {
345
3.44M
    policy=(PolicyInfo *) p->value;
346
3.44M
    if ((domain == UndefinedPolicyDomain) || (policy->domain == domain))
347
0
      if (LocaleCompare(policyname,policy->name) == 0)
348
0
        break;
349
3.44M
    p=p->next;
350
3.44M
  }
351
3.44M
  if (p == (ElementInfo *) NULL)
352
3.44M
    policy=(PolicyInfo *) NULL;
353
0
  else
354
0
    (void) SetHeadElementInLinkedList(policy_cache,p);
355
3.44M
  UnlockSemaphoreInfo(policy_semaphore);
356
3.44M
  return(policy);
357
3.44M
}
358

359
/*
360
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
361
%                                                                             %
362
%                                                                             %
363
%                                                                             %
364
%   G e t P o l i c y I n f o L i s t                                         %
365
%                                                                             %
366
%                                                                             %
367
%                                                                             %
368
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
369
%
370
%  GetPolicyInfoList() returns any policies that match the specified pattern.
371
%
372
%  The format of the GetPolicyInfoList function is:
373
%
374
%      const PolicyInfo **GetPolicyInfoList(const char *pattern,
375
%        size_t *number_policies,ExceptionInfo *exception)
376
%
377
%  A description of each parameter follows:
378
%
379
%    o pattern: Specifies a pointer to a text string containing a pattern.
380
%
381
%    o number_policies:  returns the number of policies in the list.
382
%
383
%    o exception: return any errors or warnings in this structure.
384
%
385
*/
386
MagickExport const PolicyInfo **GetPolicyInfoList(const char *pattern,
387
  size_t *number_policies,ExceptionInfo *exception)
388
0
{
389
0
  const PolicyInfo
390
0
    **policies;
391
392
0
  ElementInfo
393
0
    *p;
394
395
0
  ssize_t
396
0
    i;
397
398
0
  assert(pattern != (char *) NULL);
399
0
  assert(number_policies != (size_t *) NULL);
400
0
  if (IsEventLogging() != MagickFalse)
401
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
402
0
  *number_policies=0;
403
0
  if (IsPolicyCacheInstantiated(exception) == MagickFalse)
404
0
    return((const PolicyInfo **) NULL);
405
0
  policies=(const PolicyInfo **) AcquireQuantumMemory((size_t)
406
0
    GetNumberOfElementsInLinkedList(policy_cache)+1UL,sizeof(*policies));
407
0
  if (policies == (const PolicyInfo **) NULL)
408
0
    return((const PolicyInfo **) NULL);
409
0
  LockSemaphoreInfo(policy_semaphore);
410
0
  p=GetHeadElementInLinkedList(policy_cache);
411
0
  for (i=0; p != (ElementInfo *) NULL; )
412
0
  {
413
0
    const PolicyInfo
414
0
      *policy;
415
416
0
    policy=(const PolicyInfo *) p->value;
417
0
    if ((policy->stealth == MagickFalse) &&
418
0
        (GlobExpression(policy->name,pattern,MagickFalse) != MagickFalse))
419
0
      policies[i++]=policy;
420
0
    p=p->next;
421
0
  }
422
0
  UnlockSemaphoreInfo(policy_semaphore);
423
0
  if (i == 0)
424
0
    policies=(const PolicyInfo **) RelinquishMagickMemory((void*) policies);
425
0
  else
426
0
    policies[i]=(PolicyInfo *) NULL;
427
0
  *number_policies=(size_t) i;
428
0
  return(policies);
429
0
}
430

431
/*
432
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
433
%                                                                             %
434
%                                                                             %
435
%                                                                             %
436
%   G e t P o l i c y L i s t                                                 %
437
%                                                                             %
438
%                                                                             %
439
%                                                                             %
440
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441
%
442
%  GetPolicyList() returns any policies that match the specified pattern.
443
%
444
%  The format of the GetPolicyList function is:
445
%
446
%      char **GetPolicyList(const char *pattern,size_t *number_policies,
447
%        ExceptionInfo *exception)
448
%
449
%  A description of each parameter follows:
450
%
451
%    o pattern: a pointer to a text string containing a pattern.
452
%
453
%    o number_policies:  returns the number of policies in the list.
454
%
455
%    o exception: return any errors or warnings in this structure.
456
%
457
*/
458
459
static char *AcquirePolicyString(const char *source,const size_t pad)
460
0
{
461
0
  char
462
0
    *destination;
463
464
0
  size_t
465
0
    length;
466
467
0
  length=0;
468
0
  if (source != (char *) NULL)
469
0
    length+=strlen(source);
470
0
  destination=(char *) NULL;
471
  /* AcquireMagickMemory needs to be used here to avoid an omp deadlock */
472
0
  if (~length >= pad)
473
0
    destination=(char *) AcquireMagickMemory((length+pad)*sizeof(*destination));
474
0
  if (destination == (char *) NULL)
475
0
    ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
476
0
  if (source != (char *) NULL)
477
0
    (void) memcpy(destination,source,length*sizeof(*destination));
478
0
  destination[length]='\0';
479
0
  return(destination);
480
0
}
481
482
MagickExport char **GetPolicyList(const char *pattern,size_t *number_policies,
483
  ExceptionInfo *exception)
484
0
{
485
0
  char
486
0
    **policies;
487
488
0
  const ElementInfo
489
0
    *p;
490
491
0
  ssize_t
492
0
    i;
493
494
0
  assert(pattern != (char *) NULL);
495
0
  assert(number_policies != (size_t *) NULL);
496
0
  if (IsEventLogging() != MagickFalse)
497
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
498
0
  *number_policies=0;
499
0
  if (IsPolicyCacheInstantiated(exception) == MagickFalse)
500
0
    return((char **) NULL);
501
0
  policies=(char **) AcquireQuantumMemory((size_t)
502
0
    GetNumberOfElementsInLinkedList(policy_cache)+1UL,sizeof(*policies));
503
0
  if (policies == (char **) NULL)
504
0
    return((char **) NULL);
505
0
  LockSemaphoreInfo(policy_semaphore);
506
0
  p=GetHeadElementInLinkedList(policy_cache);
507
0
  for (i=0; p != (ElementInfo *) NULL; )
508
0
  {
509
0
    const PolicyInfo
510
0
      *policy;
511
512
0
    policy=(const PolicyInfo *) p->value;
513
0
    if ((policy->stealth == MagickFalse) &&
514
0
        (GlobExpression(policy->name,pattern,MagickFalse) != MagickFalse))
515
0
      policies[i++]=AcquirePolicyString(policy->name,1);
516
0
    p=p->next;
517
0
  }
518
0
  UnlockSemaphoreInfo(policy_semaphore);
519
0
  if (i == 0)
520
0
    policies=(char **) RelinquishMagickMemory(policies);
521
0
  else
522
0
    policies[i]=(char *) NULL;
523
0
  *number_policies=(size_t) i;
524
0
  return(policies);
525
0
}
526

527
/*
528
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
529
%                                                                             %
530
%                                                                             %
531
%                                                                             %
532
%   G e t P o l i c y V a l u e                                               %
533
%                                                                             %
534
%                                                                             %
535
%                                                                             %
536
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
537
%
538
%  GetPolicyValue() returns the value associated with the named policy.
539
%
540
%  The format of the GetPolicyValue method is:
541
%
542
%      char *GetPolicyValue(const char *name)
543
%
544
%  A description of each parameter follows:
545
%
546
%    o name:  The name of the policy.
547
%
548
*/
549
MagickExport char *GetPolicyValue(const char *name)
550
3.44M
{
551
3.44M
  const char
552
3.44M
    *value;
553
554
3.44M
  const PolicyInfo
555
3.44M
    *policy_info;
556
557
3.44M
  ExceptionInfo
558
3.44M
    *exception;
559
560
3.44M
  assert(name != (const char *) NULL);
561
3.44M
  if (IsEventLogging() != MagickFalse)
562
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",name);
563
3.44M
  exception=AcquireExceptionInfo();
564
3.44M
  policy_info=GetPolicyInfo(name,exception);
565
3.44M
  exception=DestroyExceptionInfo(exception);
566
3.44M
  if (policy_info == (PolicyInfo *) NULL)
567
3.44M
    return((char *) NULL);
568
0
  value=policy_info->value;
569
0
  if ((value == (const char *) NULL) || (*value == '\0'))
570
0
    return((char *) NULL);
571
0
  return(AcquirePolicyString(value,1));
572
0
}
573

574
/*
575
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
576
%                                                                             %
577
%                                                                             %
578
%                                                                             %
579
+   I s P a t h A u t h o r i z e d                                           %
580
%                                                                             %
581
%                                                                             %
582
%                                                                             %
583
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
584
%
585
%  IsPathAuthorized() determines if the specified path is authorized based on
586
%  the current policy settings.
587
%
588
%  The format of the IsPathAuthorized method is:
589
%
590
%      MagickBooleanType IsPathAuthorized(const PolicyRights rights, 
591
%        const char *path)
592
%
593
%  A description of each parameter follows.
594
%
595
%    o rights: The policy rights to check.
596
%
597
%    o path: The path to check.
598
%
599
*/
600
static inline MagickBooleanType IsPathContainsSymlink(const char *path)
601
0
{
602
0
  char
603
0
    partial[MagickPathExtent];
604
605
0
  const char
606
0
    *p;
607
608
0
  ssize_t
609
0
    offset = 0;
610
611
0
  if (path == (const char *) NULL)
612
0
    return(MagickFalse);
613
0
  *partial='\0';
614
0
  p=path;
615
0
  if (*p == *DirectorySeparator)
616
0
    {
617
      /*
618
        Path starts with a directory separator, include it.
619
      */
620
0
      if ((offset+1) >= (ssize_t) sizeof(partial))
621
0
        return(MagickFalse);
622
0
      partial[offset++]=(*p++);
623
0
      partial[offset]='\0';
624
0
    }
625
0
  while (*p != '\0')
626
0
  {
627
0
    char
628
0
      component[MagickPathExtent];
629
630
0
    ssize_t
631
0
      i = 0;
632
633
    /*
634
      Copy next component into a temporary buffer.
635
    */
636
0
    while ((*p != '\0') && (*p != *DirectorySeparator) &&
637
0
           ((i+1) < (ssize_t) sizeof(component)))
638
0
      component[i++]=(*p++);
639
0
    component[i]='\0';
640
0
    if (i == 0)
641
0
      {
642
        /*
643
          skip repeated separators.
644
        */
645
0
        if (*p == *DirectorySeparator)
646
0
          p++;
647
0
        continue;
648
0
      }
649
0
    if ((offset > 0) && (partial[offset-1] != *DirectorySeparator))
650
0
      {
651
        /*
652
          Append separator if needed.
653
        */
654
0
        if ((offset+1) >= (ssize_t) sizeof(partial))
655
0
          return MagickFalse;
656
0
        partial[offset++]=(*DirectorySeparator);
657
0
        partial[offset]='\0';
658
0
      }
659
    /*
660
      Append component.
661
    */
662
0
    if ((offset+i) >= (ssize_t) sizeof(partial))
663
0
      return(MagickFalse);
664
0
    (void) memcpy(partial+offset,component,i);
665
0
    offset+=i;
666
0
    partial[offset]='\0';
667
0
    if (*p != '\0')
668
0
      {
669
        /*
670
          Check whether this prefix is a symlink.
671
        */
672
0
        if (is_symlink_utf8(partial) != MagickFalse)
673
0
          return(MagickTrue);
674
0
      }
675
    /*
676
      Skip separator.
677
    */
678
0
    if (*p == *DirectorySeparator)
679
0
      p++;
680
0
  }
681
0
  return(MagickFalse);
682
0
}
683
684
MagickExport MagickBooleanType IsPathAuthorized(const PolicyRights rights,
685
  const char *path)
686
621k
{
687
621k
  MagickBooleanType symlink_follow_allowed = IsRightsAuthorizedByName(
688
621k
    SystemPolicyDomain,"symlink",rights,"follow");
689
621k
  MagickBooleanType status =
690
621k
   ((IsRightsAuthorized(PathPolicyDomain,rights,path) != MagickFalse) &&
691
621k
   ((symlink_follow_allowed != MagickFalse) ||
692
621k
    (is_symlink_utf8(path) == MagickFalse))) ? MagickTrue : MagickFalse;
693
621k
  if ((status != MagickFalse) && (symlink_follow_allowed == MagickFalse))
694
0
    {
695
0
      if ((is_symlink_utf8(path) != MagickFalse) ||
696
0
          (IsPathContainsSymlink(path) != MagickFalse))
697
0
        status=MagickFalse;
698
0
    }
699
621k
  if (status != MagickFalse)
700
621k
    status=IsFileResourceIdentityValid(path);
701
621k
  return(status);
702
621k
}
703

704
/*
705
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
706
%                                                                             %
707
%                                                                             %
708
%                                                                             %
709
+   I s P o l i c y C a c h e I n s t a n t i a t e d                         %
710
%                                                                             %
711
%                                                                             %
712
%                                                                             %
713
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
714
%
715
%  IsPolicyCacheInstantiated() determines if the policy list is instantiated.
716
%  If not, it instantiates the list and returns it.
717
%
718
%  The format of the IsPolicyInstantiated method is:
719
%
720
%      MagickBooleanType IsPolicyCacheInstantiated(ExceptionInfo *exception)
721
%
722
%  A description of each parameter follows.
723
%
724
%    o exception: return any errors or warnings in this structure.
725
%
726
*/
727
static MagickBooleanType IsPolicyCacheInstantiated(ExceptionInfo *exception)
728
6.42M
{
729
6.42M
  if (policy_cache == (LinkedListInfo *) NULL)
730
295
    {
731
295
      (void) GetMaxMemoryRequest();  /* avoid OMP deadlock */
732
295
      if (policy_semaphore == (SemaphoreInfo *) NULL)
733
295
        ActivateSemaphoreInfo(&policy_semaphore);
734
295
      LockSemaphoreInfo(policy_semaphore);
735
295
      if (policy_cache == (LinkedListInfo *) NULL)
736
295
        policy_cache=AcquirePolicyCache(PolicyFilename,exception);
737
295
      UnlockSemaphoreInfo(policy_semaphore);
738
295
    }
739
6.42M
  return(policy_cache != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
740
6.42M
}
741

742
/*
743
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
744
%                                                                             %
745
%                                                                             %
746
%                                                                             %
747
%   I s R i g h t s A u t h o r i z e d                                       %
748
%                                                                             %
749
%                                                                             %
750
%                                                                             %
751
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
752
%
753
%  IsRightsAuthorized() returns MagickTrue if the policy authorizes the
754
%  requested rights for the specified domain.
755
%
756
%  Policy evaluation uses a “last match wins” model.  Be careful when adding
757
%  new rules: any later policy can override earlier denies or allows. Place
758
%  broad deny rules first, followed by specific exceptions, and review
759
%  ordering to avoid accidental authorization.
760
%
761
%  The format of the IsRightsAuthorized method is:
762
%
763
%      MagickBooleanType IsRightsAuthorized(const PolicyDomain domain,
764
%        const PolicyRights rights,const char *pattern)
765
%
766
%  A description of each parameter follows:
767
%
768
%    o domain: the policy domain.
769
%
770
%    o rights: the policy rights.
771
%
772
%    o pattern: the pattern.
773
%
774
*/
775
776
MagickExport MagickBooleanType IsRightsAuthorizedByName(
777
  const PolicyDomain domain,const char *name,const PolicyRights rights,
778
  const char *pattern)
779
2.98M
{
780
2.98M
  char
781
2.98M
    *canonical_directory = (char *) NULL,
782
2.98M
    *canonical_path = (char *) NULL,
783
2.98M
    *canonical_candidate = (char *) NULL,
784
2.98M
    directory[MagickPathExtent],
785
2.98M
    filename[MagickPathExtent];
786
787
2.98M
  ElementInfo
788
2.98M
    *p;
789
790
2.98M
  ExceptionInfo
791
2.98M
    *exception;
792
793
2.98M
  MagickBooleanType
794
2.98M
    canonical_matched_any = MagickFalse,
795
2.98M
    matched_any = MagickFalse,
796
2.98M
    paths_provisioned = MagickFalse,
797
2.98M
    status;
798
799
2.98M
  PolicyRights
800
2.98M
    canonical_allowed_accumulator = AllPolicyRights,
801
2.98M
    effective_rights = AllPolicyRights;
802
803
  /*
804
    Load policies.
805
  */
806
2.98M
  if ((GetLogEventMask() & PolicyEvent) != 0)
807
0
    (void) LogMagickEvent(PolicyEvent,GetMagickModule(),
808
0
      "Domain: %s; name: %s; rights=%s; pattern=\"%s\"; ...",
809
0
      CommandOptionToMnemonic(MagickPolicyDomainOptions,domain),
810
0
      name == (const char *) NULL ? "undefined" : name,
811
0
      CommandOptionToMnemonic(MagickPolicyRightsOptions,rights),
812
0
      pattern == (const char *) NULL ? "undefined" : pattern);
813
2.98M
  exception=AcquireExceptionInfo();
814
2.98M
  status=IsPolicyCacheInstantiated(exception);
815
2.98M
  exception=DestroyExceptionInfo(exception);
816
2.98M
  if (status == MagickFalse)
817
0
    {
818
0
      if ((GetLogEventMask() & PolicyEvent) != 0)
819
0
        (void) LogMagickEvent(PolicyEvent,GetMagickModule(),
820
0
          "  authorized: true (no security policies found)");
821
0
      return(MagickTrue);
822
0
    }
823
  /*
824
    Evaluate policies in order; last match wins, however, canonical denies are
825
    enforced after evaluation.
826
  */
827
2.98M
  LockSemaphoreInfo(policy_semaphore);
828
2.98M
  ResetLinkedListIterator(policy_cache);
829
2.98M
  p=GetHeadElementInLinkedList(policy_cache);
830
5.96M
  for ( ; p != (ElementInfo *) NULL; p=p->next)
831
2.98M
  {
832
2.98M
    const PolicyInfo
833
2.98M
      *policy = (PolicyInfo *) p->value;
834
835
2.98M
    MagickBooleanType
836
2.98M
      match = MagickFalse,
837
2.98M
      matched_canonical = MagickFalse;
838
839
2.98M
    if (policy->domain != domain)
840
2.98M
      continue;
841
0
    if ((name != (char *) NULL) && (LocaleCompare(name,policy->name) != 0))
842
0
      continue;
843
0
    match=GlobExpression(pattern,policy->pattern,MagickFalse);
844
0
    if (policy->domain == PathPolicyDomain)
845
0
      {
846
0
        if (paths_provisioned == MagickFalse)
847
0
          {
848
            /*
849
              Generate directory, basename, and canonical path.
850
            */
851
0
            paths_provisioned=MagickTrue;
852
0
            GetPathComponent(pattern,HeadPath,directory);
853
0
            GetPathComponent(pattern,TailPath,filename);
854
0
            canonical_directory=realpath_utf8(directory);
855
0
            if ((canonical_directory != (char *) NULL) && (*filename != '\0'))
856
0
              {
857
0
                size_t
858
0
                  length;
859
860
0
                length=strlen(canonical_directory)+strlen(filename)+2;
861
0
                canonical_candidate=(char *) AcquireCriticalMemory(length*
862
0
                  sizeof(*canonical_candidate));
863
0
                if (canonical_candidate != (char *) NULL)
864
0
                  (void) FormatLocaleString(canonical_candidate,length,"%s%s%s",
865
0
                    canonical_directory,DirectorySeparator,filename);
866
0
              }
867
0
            canonical_path=realpath_utf8(pattern);
868
0
          }
869
        /*
870
          Match against directory, basename, and canonical path.
871
        */
872
0
        if ((canonical_directory != (char *) NULL) && (match == MagickFalse))
873
0
          match=GlobExpression(canonical_directory,policy->pattern,MagickFalse);
874
0
        if ((canonical_candidate != (char *) NULL) && (match == MagickFalse))
875
0
          match=GlobExpression(canonical_candidate,policy->pattern,MagickFalse);
876
0
        if ((canonical_path != (char *) NULL) && (match == MagickFalse))
877
0
          match=GlobExpression(canonical_path,policy->pattern,MagickFalse);
878
0
        if ((canonical_path != (char *) NULL) &&
879
0
            (GlobExpression(canonical_path,policy->pattern,MagickFalse) != MagickFalse))
880
0
          matched_canonical=MagickTrue;
881
0
        else
882
0
          if ((canonical_candidate != (char *) NULL) &&
883
0
              (GlobExpression(canonical_candidate,policy->pattern,MagickFalse) != MagickFalse))
884
0
            matched_canonical=MagickTrue;
885
0
          else
886
0
           if ((canonical_directory != (char *) NULL) &&
887
0
               (GlobExpression(canonical_directory,policy->pattern,MagickFalse) != MagickFalse))
888
0
             matched_canonical=MagickTrue;
889
0
      }
890
0
    if (match == MagickFalse)
891
0
      continue;
892
0
    matched_any=MagickTrue;
893
0
    effective_rights=policy->rights;
894
0
    if (matched_canonical != MagickFalse)
895
0
      {
896
        /*
897
          If this match was against a canonical form, accumulate allowed rights.
898
        */
899
0
        canonical_matched_any=MagickTrue;
900
0
        canonical_allowed_accumulator=(PolicyRights) ((int)
901
0
          canonical_allowed_accumulator & (int) policy->rights);
902
0
      }
903
0
  }
904
2.98M
  UnlockSemaphoreInfo(policy_semaphore);
905
2.98M
  if (canonical_directory != (char *) NULL)
906
0
    canonical_directory=DestroyString(canonical_directory);
907
2.98M
  if (canonical_candidate != (char *) NULL)
908
0
    canonical_candidate=DestroyString(canonical_candidate);
909
2.98M
  if (canonical_path != (char *) NULL)
910
0
    canonical_path=DestroyString(canonical_path);
911
  /*
912
    Is rights authorized?
913
  */
914
2.98M
  status=MagickTrue;
915
2.98M
  if (matched_any != MagickFalse)
916
0
    {
917
0
      if (((rights & ReadPolicyRights) != 0) &&
918
0
          ((effective_rights & ReadPolicyRights) == 0))
919
0
        status=MagickFalse;
920
0
      if (((rights & WritePolicyRights) != 0) &&
921
0
          ((effective_rights & WritePolicyRights) == 0))
922
0
        status=MagickFalse;
923
0
      if (((rights & ExecutePolicyRights) != 0) &&
924
0
          ((effective_rights & ExecutePolicyRights) == 0))
925
0
        status=MagickFalse;
926
0
    }
927
  /*
928
    Enforce sticky canonical denies.
929
  */
930
2.98M
  if (canonical_matched_any != MagickFalse)
931
0
    {
932
0
      PolicyRights canonical_denied_mask = (PolicyRights) ((int)
933
0
        AllPolicyRights & (int) ~canonical_allowed_accumulator);
934
0
      if ((canonical_denied_mask & rights) != 0)
935
0
        status=MagickFalse;
936
0
    }
937
2.98M
  if ((GetLogEventMask() & PolicyEvent) != 0)
938
0
    (void) LogMagickEvent(PolicyEvent,GetMagickModule(),
939
0
      "  authorized: %s",status == MagickFalse ? "false" : "true");
940
2.98M
  return(status);
941
2.98M
}
942
943
MagickExport MagickBooleanType IsRightsAuthorized(const PolicyDomain domain,
944
  const PolicyRights rights,const char *pattern)
945
2.33M
{
946
2.33M
  return(IsRightsAuthorizedByName(domain,(const char *) NULL,rights,pattern));
947
2.33M
}
948

949
/*
950
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
951
%                                                                             %
952
%                                                                             %
953
%                                                                             %
954
%  L i s t P o l i c y I n f o                                                %
955
%                                                                             %
956
%                                                                             %
957
%                                                                             %
958
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
959
%
960
%  ListPolicyInfo() lists policies to the specified file.
961
%
962
%  The format of the ListPolicyInfo method is:
963
%
964
%      MagickBooleanType ListPolicyInfo(FILE *file,ExceptionInfo *exception)
965
%
966
%  A description of each parameter follows.
967
%
968
%    o file:  List policy names to this file handle.
969
%
970
%    o exception: return any errors or warnings in this structure.
971
%
972
*/
973
MagickExport MagickBooleanType ListPolicyInfo(FILE *file,
974
  ExceptionInfo *exception)
975
0
{
976
0
  const char
977
0
    *path,
978
0
    *domain;
979
980
0
  const PolicyInfo
981
0
    **policy_info;
982
983
0
  ssize_t
984
0
    i;
985
986
0
  size_t
987
0
    number_policies;
988
989
  /*
990
    List name and attributes of each policy in the list.
991
  */
992
0
  if (file == (const FILE *) NULL)
993
0
    file=stdout;
994
0
  policy_info=GetPolicyInfoList("*",&number_policies,exception);
995
0
  if (policy_info == (const PolicyInfo **) NULL)
996
0
    return(MagickFalse);
997
0
  path=(const char *) NULL;
998
0
  for (i=0; i < (ssize_t) number_policies; i++)
999
0
  {
1000
0
    if (policy_info[i]->stealth != MagickFalse)
1001
0
      continue;
1002
0
    if (((path == (const char *) NULL) ||
1003
0
         (LocaleCompare(path,policy_info[i]->path) != 0)) &&
1004
0
         (policy_info[i]->path != (char *) NULL))
1005
0
      (void) FormatLocaleFile(file,"\nPath: %s\n",policy_info[i]->path);
1006
0
    path=policy_info[i]->path;
1007
0
    domain=CommandOptionToMnemonic(MagickPolicyDomainOptions,
1008
0
      policy_info[i]->domain);
1009
0
    (void) FormatLocaleFile(file,"  Policy: %s\n",domain);
1010
0
    if ((policy_info[i]->domain == CachePolicyDomain) ||
1011
0
        (policy_info[i]->domain == ResourcePolicyDomain) ||
1012
0
        (policy_info[i]->domain == SystemPolicyDomain))
1013
0
      {
1014
0
        if (policy_info[i]->name != (char *) NULL)
1015
0
          (void) FormatLocaleFile(file,"    name: %s\n",policy_info[i]->name);
1016
0
        if (policy_info[i]->value != (char *) NULL)
1017
0
          (void) FormatLocaleFile(file,"    value: %s\n",policy_info[i]->value);
1018
0
      }
1019
0
    else
1020
0
      {
1021
0
        (void) FormatLocaleFile(file,"    rights: ");
1022
0
        if (policy_info[i]->rights == NoPolicyRights)
1023
0
          (void) FormatLocaleFile(file,"None ");
1024
0
        if ((policy_info[i]->rights & ReadPolicyRights) != 0)
1025
0
          (void) FormatLocaleFile(file,"Read ");
1026
0
        if ((policy_info[i]->rights & WritePolicyRights) != 0)
1027
0
          (void) FormatLocaleFile(file,"Write ");
1028
0
        if ((policy_info[i]->rights & ExecutePolicyRights) != 0)
1029
0
          (void) FormatLocaleFile(file,"Execute ");
1030
0
        (void) FormatLocaleFile(file,"\n");
1031
0
        if (policy_info[i]->pattern != (char *) NULL)
1032
0
          (void) FormatLocaleFile(file,"    pattern: %s\n",
1033
0
            policy_info[i]->pattern);
1034
0
      }
1035
0
  }
1036
0
  policy_info=(const PolicyInfo **) RelinquishMagickMemory((void *)
1037
0
    policy_info);
1038
0
  (void) fflush(file);
1039
0
  return(MagickTrue);
1040
0
}
1041

1042
/*
1043
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1044
%                                                                             %
1045
%                                                                             %
1046
%                                                                             %
1047
+   L o a d P o l i c y C a c h e                                             %
1048
%                                                                             %
1049
%                                                                             %
1050
%                                                                             %
1051
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1052
%
1053
%  LoadPolicyCache() loads the policy configurations which provides a mapping
1054
%  between policy attributes and a policy domain.
1055
%
1056
%  The format of the LoadPolicyCache method is:
1057
%
1058
%      MagickBooleanType LoadPolicyCache(LinkedListInfo *cache,const char *xml,
1059
%        const char *filename,const size_t depth,ExceptionInfo *exception)
1060
%
1061
%  A description of each parameter follows:
1062
%
1063
%    o xml:  The policy list in XML format.
1064
%
1065
%    o filename:  The policy list filename.
1066
%
1067
%    o depth: depth of <include /> statements.
1068
%
1069
%    o exception: return any errors or warnings in this structure.
1070
%
1071
*/
1072
static MagickBooleanType LoadPolicyCache(LinkedListInfo *cache,
1073
  const char *policy,const char *filename,const size_t depth,
1074
  ExceptionInfo *exception)
1075
0
{
1076
0
  char
1077
0
    keyword[MagickPathExtent],
1078
0
    *token;
1079
1080
0
  const char
1081
0
    *q;
1082
1083
0
  MagickStatusType
1084
0
    status;
1085
1086
0
  PolicyInfo
1087
0
    *policy_info;
1088
1089
0
  size_t
1090
0
    extent;
1091
1092
  /*
1093
    Load the policy map file.
1094
  */
1095
0
  (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1096
0
    "Loading policy file \"%s\" ...",filename);
1097
0
  if (policy == (char *) NULL)
1098
0
    return(MagickFalse);
1099
0
  status=MagickTrue;
1100
0
  policy_info=(PolicyInfo *) NULL;
1101
0
  token=AcquirePolicyString(policy,MagickPathExtent);
1102
0
  extent=strlen(token)+MagickPathExtent;
1103
0
  for (q=policy; *q != '\0'; )
1104
0
  {
1105
    /*
1106
      Interpret XML.
1107
    */
1108
0
    (void) GetNextToken(q,&q,extent,token);
1109
0
    if (*token == '\0')
1110
0
      break;
1111
0
    (void) CopyMagickString(keyword,token,MagickPathExtent);
1112
0
    if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
1113
0
      {
1114
        /*
1115
          Docdomain element.
1116
        */
1117
0
        while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
1118
0
          (void) GetNextToken(q,&q,extent,token);
1119
0
        continue;
1120
0
      }
1121
0
    if (LocaleNCompare(keyword,"<!--",4) == 0)
1122
0
      {
1123
        /*
1124
          Comment element.
1125
        */
1126
0
        while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
1127
0
          (void) GetNextToken(q,&q,extent,token);
1128
0
        continue;
1129
0
      }
1130
0
    if (LocaleCompare(keyword,"<include") == 0)
1131
0
      {
1132
        /*
1133
          Include element.
1134
        */
1135
0
        while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
1136
0
        {
1137
0
          (void) CopyMagickString(keyword,token,MagickPathExtent);
1138
0
          (void) GetNextToken(q,&q,extent,token);
1139
0
          if (*token != '=')
1140
0
            continue;
1141
0
          (void) GetNextToken(q,&q,extent,token);
1142
0
          if (LocaleCompare(keyword,"file") == 0)
1143
0
            {
1144
0
              if (depth > MagickMaxRecursionDepth)
1145
0
                (void) ThrowMagickException(exception,GetMagickModule(),
1146
0
                  ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
1147
0
              else
1148
0
                {
1149
0
                  char
1150
0
                    path[MagickPathExtent],
1151
0
                    *file_xml;
1152
1153
0
                  GetPathComponent(filename,HeadPath,path);
1154
0
                  if (*path != '\0')
1155
0
                    (void) ConcatenateMagickString(path,DirectorySeparator,
1156
0
                      MagickPathExtent);
1157
0
                  if (*token == *DirectorySeparator)
1158
0
                    (void) CopyMagickString(path,token,MagickPathExtent);
1159
0
                  else
1160
0
                    (void) ConcatenateMagickString(path,token,MagickPathExtent);
1161
0
                  file_xml=FileToXML(path,~0UL);
1162
0
                  if (file_xml != (char *) NULL)
1163
0
                    {
1164
0
                      status&=(MagickStatusType) LoadPolicyCache(cache,file_xml,
1165
0
                        path,depth+1,exception);
1166
0
                      file_xml=DestroyString(file_xml);
1167
0
                    }
1168
0
                }
1169
0
            }
1170
0
        }
1171
0
        continue;
1172
0
      }
1173
0
    if (LocaleCompare(keyword,"<policy") == 0)
1174
0
      {
1175
        /*
1176
          Policy element.
1177
        */
1178
0
        policy_info=(PolicyInfo *) AcquireCriticalMemory(sizeof(*policy_info));
1179
0
        (void) memset(policy_info,0,sizeof(*policy_info));
1180
0
        policy_info->path=AcquirePolicyString(filename,1);
1181
0
        policy_info->exempt=MagickFalse;
1182
0
        policy_info->signature=MagickCoreSignature;
1183
0
        continue;
1184
0
      }
1185
0
    if (policy_info == (PolicyInfo *) NULL)
1186
0
      continue;
1187
0
    if ((LocaleCompare(keyword,"/>") == 0) ||
1188
0
        (LocaleCompare(keyword,"</policy>") == 0))
1189
0
      {
1190
0
        status=AppendValueToLinkedList(cache,policy_info);
1191
0
        if (status == MagickFalse)
1192
0
          (void) ThrowMagickException(exception,GetMagickModule(),
1193
0
            ResourceLimitError,"MemoryAllocationFailed","`%s'",
1194
0
            policy_info->name);
1195
0
        policy_info=(PolicyInfo *) NULL;
1196
0
        continue;
1197
0
      }
1198
0
    (void) GetNextToken(q,(const char **) NULL,extent,token);
1199
0
    if (*token != '=')
1200
0
      continue;
1201
0
    (void) GetNextToken(q,&q,extent,token);
1202
0
    (void) GetNextToken(q,&q,extent,token);
1203
0
    switch (*keyword)
1204
0
    {
1205
0
      case 'D':
1206
0
      case 'd':
1207
0
      {
1208
0
        if (LocaleCompare((char *) keyword,"domain") == 0)
1209
0
          {
1210
0
            policy_info->domain=(PolicyDomain) ParseCommandOption(
1211
0
              MagickPolicyDomainOptions,MagickTrue,token);
1212
0
            break;
1213
0
          }
1214
0
        break;
1215
0
      }
1216
0
      case 'N':
1217
0
      case 'n':
1218
0
      {
1219
0
        if (LocaleCompare((char *) keyword,"name") == 0)
1220
0
          {
1221
0
            policy_info->name=AcquirePolicyString(token,1);
1222
0
            break;
1223
0
          }
1224
0
        break;
1225
0
      }
1226
0
      case 'P':
1227
0
      case 'p':
1228
0
      {
1229
0
        if (LocaleCompare((char *) keyword,"pattern") == 0)
1230
0
          {
1231
0
            policy_info->pattern=AcquirePolicyString(token,1);
1232
0
            break;
1233
0
          }
1234
0
        break;
1235
0
      }
1236
0
      case 'R':
1237
0
      case 'r':
1238
0
      {
1239
0
        if (LocaleCompare((char *) keyword,"rights") == 0)
1240
0
          {
1241
0
            policy_info->rights=(PolicyRights) ParseCommandOption(
1242
0
              MagickPolicyRightsOptions,MagickTrue,token);
1243
0
            break;
1244
0
          }
1245
0
        break;
1246
0
      }
1247
0
      case 'S':
1248
0
      case 's':
1249
0
      {
1250
0
        if (LocaleCompare((char *) keyword,"stealth") == 0)
1251
0
          {
1252
0
            policy_info->stealth=IsStringTrue(token);
1253
0
            break;
1254
0
          }
1255
0
        break;
1256
0
      }
1257
0
      case 'V':
1258
0
      case 'v':
1259
0
      {
1260
0
        if (LocaleCompare((char *) keyword,"value") == 0)
1261
0
          {
1262
0
            policy_info->value=AcquirePolicyString(token,1);
1263
0
            break;
1264
0
          }
1265
0
        break;
1266
0
      }
1267
0
      default:
1268
0
        break;
1269
0
    }
1270
0
  }
1271
0
  token=(char *) RelinquishMagickMemory(token);
1272
0
  return(status != 0 ? MagickTrue : MagickFalse);
1273
0
}
1274

1275
/*
1276
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277
%                                                                             %
1278
%                                                                             %
1279
%                                                                             %
1280
+   P o l i c y C o m p o n e n t G e n e s i s                               %
1281
%                                                                             %
1282
%                                                                             %
1283
%                                                                             %
1284
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1285
%
1286
%  PolicyComponentGenesis() instantiates the policy component.
1287
%
1288
%  The format of the PolicyComponentGenesis method is:
1289
%
1290
%      MagickBooleanType PolicyComponentGenesis(void)
1291
%
1292
*/
1293
MagickPrivate MagickBooleanType PolicyComponentGenesis(void)
1294
295
{
1295
295
  if (policy_semaphore == (SemaphoreInfo *) NULL)
1296
0
    policy_semaphore=AcquireSemaphoreInfo();
1297
295
  return(MagickTrue);
1298
295
}
1299

1300
/*
1301
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1302
%                                                                             %
1303
%                                                                             %
1304
%                                                                             %
1305
+   P o l i c y C o m p o n e n t T e r m i n u s                             %
1306
%                                                                             %
1307
%                                                                             %
1308
%                                                                             %
1309
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1310
%
1311
%  PolicyComponentTerminus() destroys the policy component.
1312
%
1313
%  The format of the PolicyComponentTerminus method is:
1314
%
1315
%      PolicyComponentTerminus(void)
1316
%
1317
*/
1318
1319
static void *DestroyPolicyElement(void *policy_info)
1320
0
{
1321
0
  PolicyInfo
1322
0
    *p;
1323
1324
0
  p=(PolicyInfo *) policy_info;
1325
0
  if (p->exempt == MagickFalse)
1326
0
    {
1327
0
      if (p->value != (char *) NULL)
1328
0
        p->value=DestroyString(p->value);
1329
0
      if (p->pattern != (char *) NULL)
1330
0
        p->pattern=DestroyString(p->pattern);
1331
0
      if (p->name != (char *) NULL)
1332
0
        p->name=DestroyString(p->name);
1333
0
      if (p->path != (char *) NULL)
1334
0
        p->path=DestroyString(p->path);
1335
0
    }
1336
0
  p=(PolicyInfo *) RelinquishMagickMemory(p);
1337
0
  return((void *) NULL);
1338
0
}
1339
1340
MagickPrivate void PolicyComponentTerminus(void)
1341
0
{
1342
0
  if (policy_semaphore == (SemaphoreInfo *) NULL)
1343
0
    ActivateSemaphoreInfo(&policy_semaphore);
1344
0
  LockSemaphoreInfo(policy_semaphore);
1345
0
  if (policy_cache != (LinkedListInfo *) NULL)
1346
0
    policy_cache=DestroyLinkedList(policy_cache,DestroyPolicyElement);
1347
0
  UnlockSemaphoreInfo(policy_semaphore);
1348
0
  RelinquishSemaphoreInfo(&policy_semaphore);
1349
0
}
1350

1351
/*
1352
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353
%                                                                             %
1354
%                                                                             %
1355
%                                                                             %
1356
%  S e t M a g i c k S e c u r i t y P o l i c y                              %
1357
%                                                                             %
1358
%                                                                             %
1359
%                                                                             %
1360
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1361
%
1362
%  SetMagickSecurityPolicy() sets or restricts the ImageMagick security policy.
1363
%  It returns MagickFalse if the policy the policy does not parse.
1364
%
1365
%  The format of the SetMagickSecurityPolicy method is:
1366
%
1367
%      MagickBooleanType SetMagickSecurityPolicy(const char *policy,
1368
%        ExceptionInfo *exception)
1369
%
1370
%  A description of each parameter follows:
1371
%
1372
%    o policy: the security policy in the XML format.
1373
%
1374
%    o exception: return any errors or warnings in this structure.
1375
%
1376
*/
1377
1378
static MagickBooleanType ValidateSecurityPolicy(const char *policy,
1379
  const char *url,ExceptionInfo *exception)
1380
0
{
1381
#if defined(MAGICKCORE_XML_DELEGATE)
1382
  xmlDocPtr
1383
    document;
1384
1385
  /*
1386
    Parse security policy.
1387
  */
1388
  document=xmlReadMemory(policy,(int) strlen(policy),url,NULL,
1389
    XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
1390
  if (document == (xmlDocPtr) NULL)
1391
    {
1392
      (void) ThrowMagickException(exception,GetMagickModule(),ConfigureError,
1393
        "PolicyValidationException","'%s'",url);
1394
      return(MagickFalse);
1395
    }
1396
  xmlFreeDoc(document);
1397
#else
1398
0
  (void) policy;
1399
0
  (void) url;
1400
0
  (void) exception;
1401
0
#endif
1402
0
  return(MagickTrue);
1403
0
}
1404
1405
MagickExport MagickBooleanType SetMagickSecurityPolicy(const char *policy,
1406
  ExceptionInfo *exception)
1407
0
{
1408
0
  MagickBooleanType
1409
0
    status;
1410
1411
0
  LinkedListInfo
1412
0
    *user_policies;
1413
1414
0
  PolicyInfo
1415
0
    *p;
1416
1417
  /*
1418
    Load user policies.
1419
  */
1420
0
  assert(exception != (ExceptionInfo *) NULL);
1421
0
  if (policy == (const char *) NULL)
1422
0
    return(MagickFalse);
1423
0
  if (ValidateSecurityPolicy(policy,PolicyFilename,exception) == MagickFalse)
1424
0
    return(MagickFalse);
1425
0
  status=LoadPolicyCache(policy_cache,policy,"[user-policy]",0,exception);
1426
0
  if (status == MagickFalse)
1427
0
    return(status);
1428
  /*
1429
    Synchronize user policies.
1430
  */
1431
0
  user_policies=NewLinkedList(0);
1432
0
  status=LoadPolicyCache(user_policies,policy,"[user-policy]",0,exception);
1433
0
  if (status == MagickFalse)
1434
0
    {
1435
0
      user_policies=DestroyLinkedList(user_policies,DestroyPolicyElement);
1436
0
      return(MagickFalse);
1437
0
    }
1438
0
  ResetLinkedListIterator(user_policies);
1439
0
  p=(PolicyInfo *) GetNextValueInLinkedList(user_policies);
1440
0
  while (p != (PolicyInfo *) NULL)
1441
0
  {
1442
0
    if ((p->name != (char *) NULL) && (p->value != (char *) NULL))
1443
0
      (void) SetMagickSecurityPolicyValue(p->domain,p->name,p->value,exception);
1444
0
    p=(PolicyInfo *) GetNextValueInLinkedList(user_policies);
1445
0
  }
1446
0
  user_policies=DestroyLinkedList(user_policies,DestroyPolicyElement);
1447
0
  return(status);
1448
0
}
1449

1450
/*
1451
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1452
%                                                                             %
1453
%                                                                             %
1454
%                                                                             %
1455
%  S e t M a g i c k S e c u r i t y P o l i c y V a l u e                    %
1456
%                                                                             %
1457
%                                                                             %
1458
%                                                                             %
1459
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1460
%
1461
%  SetMagickSecurityPolicyValue() sets a value associated with an ImageMagick
1462
%  security policy.  For most policies, the value must be less than any value
1463
%  set by the security policy configuration file (i.e. policy.xml).  It returns
1464
%  MagickFalse if the policy cannot be modified or if the policy does not parse.
1465
%
1466
%  The format of the SetMagickSecurityPolicyValue method is:
1467
%
1468
%      MagickBooleanType SetMagickSecurityPolicyValue(
1469
%        const PolicyDomain domain,const char *name,const char *value,
1470
%        ExceptionInfo *exception)
1471
%
1472
%  A description of each parameter follows:
1473
%
1474
%    o domain: the domain of the policy (e.g. system, resource).
1475
%
1476
%    o name: the name of the policy.
1477
%
1478
%    o value: the value to set the policy to.
1479
%
1480
%    o exception: return any errors or warnings in this structure.
1481
%
1482
*/
1483
MagickExport MagickBooleanType SetMagickSecurityPolicyValue(
1484
  const PolicyDomain domain,const char *name,const char *value,
1485
  ExceptionInfo *exception)
1486
1.18k
{
1487
1.18k
  magick_unreferenced(exception);
1488
1.18k
  assert(exception != (ExceptionInfo *) NULL);
1489
1.18k
  if ((name == (const char *) NULL) || (value == (const char *) NULL))
1490
0
    return(MagickFalse);
1491
1.18k
  switch (domain)
1492
1.18k
  {
1493
295
    case CachePolicyDomain:
1494
295
    {
1495
295
      if (LocaleCompare(name,"memory-map") == 0)
1496
295
        {
1497
295
          if (LocaleCompare(value,"anonymous") != 0)
1498
0
            return(MagickFalse);
1499
295
          ResetCacheAnonymousMemory();
1500
295
          ResetStreamAnonymousMemory();
1501
295
          return(MagickTrue);
1502
295
        }
1503
0
      break;
1504
295
    }
1505
0
    case ResourcePolicyDomain:
1506
0
    {
1507
0
      ssize_t
1508
0
        type;
1509
1510
0
      type=ParseCommandOption(MagickResourceOptions,MagickFalse,name);
1511
0
      if (type >= 0)
1512
0
        {
1513
0
          MagickSizeType
1514
0
            limit;
1515
1516
0
          limit=MagickResourceInfinity;
1517
0
          if (LocaleCompare("unlimited",value) != 0)
1518
0
            limit=StringToMagickSizeType(value,100.0);
1519
0
          if ((ResourceType) type == TimeResource)
1520
0
            limit=(MagickSizeType) ParseMagickTimeToLive(value);
1521
0
          return(SetMagickResourceLimit((ResourceType) type,limit));
1522
0
        }
1523
0
      break;
1524
0
    }
1525
885
    case SystemPolicyDomain:
1526
885
    {
1527
885
      if (LocaleCompare(name,"max-memory-request") == 0)
1528
295
        {
1529
295
          MagickSizeType
1530
295
            limit;
1531
1532
295
          limit=MagickResourceInfinity;
1533
295
          if (LocaleCompare("unlimited",value) != 0)
1534
295
            limit=StringToMagickSizeType(value,100.0);
1535
295
          SetMaxMemoryRequest(limit);
1536
295
          return(MagickTrue);
1537
295
        }
1538
590
      if (LocaleCompare(name,"max-profile-size") == 0)
1539
295
        {
1540
295
          MagickSizeType
1541
295
            limit;
1542
1543
295
          limit=MagickResourceInfinity;
1544
295
          if (LocaleCompare("unlimited",value) != 0)
1545
295
            limit=StringToMagickSizeType(value,100.0);
1546
295
          SetMaxProfileSize(limit);
1547
295
          return(MagickTrue);
1548
295
        }
1549
295
      if (LocaleCompare(name,"memory-map") == 0)
1550
295
        {
1551
295
          if (LocaleCompare(value,"anonymous") != 0)
1552
0
            return(MagickFalse);
1553
295
          ResetVirtualAnonymousMemory();
1554
295
          return(MagickTrue);
1555
295
        }
1556
0
      if (LocaleCompare(name,"precision") == 0)
1557
0
        {
1558
0
          int
1559
0
            limit;
1560
1561
0
          limit=StringToInteger(value);
1562
0
          SetMagickPrecision(limit);
1563
0
          return(MagickTrue);
1564
0
        }
1565
0
      break;
1566
0
    }
1567
0
    case CoderPolicyDomain:
1568
0
    case DelegatePolicyDomain:
1569
0
    case FilterPolicyDomain:
1570
0
    case ModulePolicyDomain:
1571
0
    case PathPolicyDomain:
1572
0
    default:
1573
0
      break;
1574
1.18k
  }
1575
0
  return(MagickFalse);
1576
1.18k
}