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