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