summaryrefslogtreecommitdiff
path: root/policy.c
diff options
context:
space:
mode:
authortumagonx2017-08-08 10:54:53 +0700
committertumagonx2017-08-08 10:54:53 +0700
commit2acec63b2ed75bf4b71ad257db573c4b8f9639e7 (patch)
treea8bea139ddd26116d44ea182b0b8436f2162e6e3 /policy.c
initial commit
Diffstat (limited to 'policy.c')
-rw-r--r--policy.c3243
1 files changed, 3243 insertions, 0 deletions
diff --git a/policy.c b/policy.c
new file mode 100644
index 0000000..be08a81
--- /dev/null
+++ b/policy.c
@@ -0,0 +1,3243 @@
1/*
2 * Copyright (c) 2004 Security Architects Corporation. All rights reserved.
3 *
4 * Module Name:
5 *
6 * policy.c
7 *
8 * Abstract:
9 *
10 * This module implements various security policy parsing and enforcement routines.
11 *
12 * Author:
13 *
14 * Eugene Tsyrklevich 16-Feb-2004
15 *
16 * Revision History:
17 *
18 * None.
19 */
20
21// XXX rename all funcs as SpYYY ? (same for other modules?)
22
23#include <NTDDK.h>
24#include "policy.h"
25#include "pathproc.h"
26#include "procname.h"
27#include "hookproc.h"
28#include "media.h"
29#include "learn.h"
30#include "misc.h"
31#include "i386.h"
32
33
34#include "process.h"
35#include "log.h"
36
37
38BOOLEAN PolicyParseRule(OUT PSECURITY_POLICY pSecPolicy, IN PCHAR rule, OUT BOOLEAN *Critical);
39BOOLEAN PolicyParsePolicyRule(OUT PSECURITY_POLICY pSecPolicy, IN PCHAR Operation, IN PCHAR rule, OUT BOOLEAN *Critical);
40BOOLEAN PolicyParseObjectRule(PSECURITY_POLICY pSecPolicy, RULE_TYPE RuleType, PCHAR Operation, PCHAR rule);
41BOOLEAN PolicyParseSyscallRule(PSECURITY_POLICY pSecPolicy, PCHAR SyscallName, PCHAR rule);
42BOOLEAN PolicyParseProtectionRule(PSECURITY_POLICY pSecPolicy, PCHAR Operation, PCHAR rule);
43BOOLEAN PolicyParseMediaRule(PSECURITY_POLICY pSecPolicy, PCHAR Operation, PCHAR rule);
44
45
46#ifdef ALLOC_PRAGMA
47#pragma alloc_text (INIT, InitPolicy)
48#pragma alloc_text (PAGE, PolicyPostBootup)
49#pragma alloc_text (PAGE, PolicyRemove)
50#endif
51
52
53/*
54 * example:
55 *
56 * SystemRoot - \device\harddisk1\windows
57 * SystemRootUnresolved - c:\windows
58 * SystemRootDirectory - \windows
59 * CDrive - \device\harddisk1
60 */
61
62CHAR SystemDrive, SystemRoot[MAX_PATH], SystemRootUnresolved[MAX_PATH], *SystemRootDirectory, CDrive[MAX_PATH];
63USHORT SystemRootLength = 0, SystemRootUnresolvedLength = 0, SystemRootDirectoryLength = 0, CDriveLength = 0;
64
65USHORT gPolicyLineNumber;
66PWSTR gPolicyFilename, gFilePath;
67
68// to be portable on 32 & 64 bit platforms
69ULONG NumberOfBitsInUlong, UlongBitShift;
70
71/* LoadPolicy() can be used by only one thread at a time due to use of global variables */
72KMUTEX LoadPolicyMutex;
73
74/* Global Security Policy */
75SECURITY_POLICY gSecPolicy;
76
77
78
79/*
80 * InitPolicy()
81 *
82 * Description:
83 * Initialize the policy engine. Load the global policy.
84 *
85 * NOTE: Called once during driver initialization (DriverEntry()).
86 *
87 * Parameters:
88 * None.
89 *
90 * Returns:
91 * TRUE if everything is OK, FALSE if failed.
92 */
93
94BOOLEAN
95InitPolicy()
96{
97 NumberOfBitsInUlong = sizeof(ULONG) * 8;
98 UlongBitShift = sizeof(ULONG) == 4 ? 5 : 6;
99
100
101 /* gets reinitialized correctly once bootup is complete (see PolicyPostBootup) */
102 SystemDrive = 'C';
103
104 if (ReadSymlinkValue(L"\\SystemRoot", SystemRootUnresolved, MAX_PATH) == FALSE)
105 {
106 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("InitPolicy: ReadSymlinkValue failed\n"));
107 return FALSE;
108 }
109
110 SystemRootUnresolvedLength = (USHORT) strlen(SystemRootUnresolved);
111
112 ResolveFilename(SystemRootUnresolved, SystemRoot, MAX_PATH);
113
114
115 /* extract the system directory name by itself (i.e. \windows) */
116 SystemRootDirectory = strrchr(SystemRoot, '\\');
117
118 if (SystemRootDirectory == NULL)
119 {
120 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("InitPolicy: SystemRootDirectory is NULL\n"));
121 return FALSE;
122 }
123
124 SystemRootDirectoryLength = (USHORT) strlen(SystemRootDirectory);
125
126
127 if (ReadSymlinkValue(L"\\??\\C:", CDrive, MAX_PATH) == FALSE)
128 {
129 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("InitPolicy: Failed to open C: symbolic link\n"));
130 return FALSE;
131 }
132
133 CDriveLength = (USHORT) strlen(CDrive);
134
135
136 if (PolicyPostBootup() == FALSE)
137 {
138 /*
139 * if boot process is not complete yet then we cannot get SystemRootUnresolved (i.e. c:\windows)
140 * because parts of registry are not initialized yet (see PolicyPostBootup)
141 *
142 * In that case, try to assemble SystemRootUnresolved manually
143 */
144
145 SystemRootUnresolved[0] = SystemDrive;
146 SystemRootUnresolved[1] = ':';
147
148 strcpy(SystemRootUnresolved + 2, SystemRootDirectory);
149 }
150
151
152 LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("InitPolicy: SystemRoot=%s (%s, %s)\n", SystemRoot, SystemRootUnresolved, SystemRootDirectory));
153
154
155 KeInitializeMutex(&LoadPolicyMutex, 0);
156
157 RtlZeroMemory(&gSecPolicy, sizeof(gSecPolicy));
158
159 KeInitializeSpinLock(&gSecPolicy.SpinLock);
160
161
162 if (LearningMode == TRUE)
163
164 return TRUE;
165
166
167 if (FindAndLoadSecurityPolicy(&gSecPolicy, L"computer", NULL) == FALSE)
168 {
169 LOG(LOG_SS_POLICY, LOG_PRIORITY_WARNING, ("InitPolicy: LoadSecurityPolicy(computer.policy) failed\n"));
170 gSecPolicy.DefaultPolicyAction = ACTION_PERMIT_DEFAULT;
171 }
172
173
174 if (gSecPolicy.DefaultPolicyAction != ACTION_PERMIT_DEFAULT)
175 {
176 LOG(LOG_SS_POLICY, LOG_PRIORITY_WARNING, ("InitPolicy: Global policy default action must be permit\n"));
177 gSecPolicy.DefaultPolicyAction = ACTION_PERMIT_DEFAULT;
178 }
179
180
181 return TRUE;
182}
183
184
185
186/*
187 * PolicyPostBootup()
188 *
189 * Description:
190 * Finish initializing system variables once the bootup process is complete.
191 * We are unable to read the SystemRoot registry value before the bootup is complete since
192 * that part of the registry has not been initialized yet.
193 *
194 * Parameters:
195 * None.
196 *
197 * Returns:
198 * TRUE to indicate success, FALSE if failed.
199 */
200
201BOOLEAN
202PolicyPostBootup()
203{
204 ASSERT(BootingUp == FALSE);
205
206
207 if (ReadStringRegistryValueA(L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion", L"SystemRoot", SystemRootUnresolved, MAX_PATH) == FALSE)
208 {
209 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("PolicyPostBootup: Failed to open SystemRoot registry key\n"));
210 return FALSE;
211 }
212
213 SystemRootUnresolvedLength = (USHORT) strlen(SystemRootUnresolved);
214
215 SystemDrive = (CHAR) toupper(SystemRootUnresolved[0]);
216
217
218 return TRUE;
219}
220
221
222
223/*
224 * PolicyRemove()
225 *
226 * Description:
227 * Shutdown the policy engine. Delete the global policy
228 *
229 * Parameters:
230 * None.
231 *
232 * Returns:
233 * Nothing.
234 */
235
236void
237PolicyRemove()
238{
239 PolicyDelete(&gSecPolicy);
240}
241
242
243
244/*
245 * PolicyDelete()
246 *
247 * Description:
248 * Delete a security policy. Free all the rules associated with a policy.
249 *
250 * Parameters:
251 * pSecPolicy - pointer to a security policy to delete.
252 *
253 * Returns:
254 * Nothing.
255 */
256
257void
258PolicyDelete(IN PSECURITY_POLICY pSecPolicy)
259{
260 PPOLICY_RULE r, tmp;
261 KIRQL irql;
262 UCHAR i;
263
264
265 if (pSecPolicy == NULL)
266 {
267 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("PolicyDelete: pSecPolicy is NULL\n"));
268 return;
269 }
270
271
272 if (pSecPolicy->Initialized == FALSE)
273 {
274 LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("PolicyDelete: pSecPolicy is not initialized\n"));
275 return;
276 }
277
278
279 KeAcquireSpinLock(&pSecPolicy->SpinLock, &irql);
280
281 for (i = 0; i < RULE_LASTONE; i++)
282 {
283 r = pSecPolicy->RuleList[i];
284
285 while (r)
286 {
287 tmp = r;
288 r = (PPOLICY_RULE) r->Next;
289
290 ExFreePoolWithTag(tmp, _POOL_TAG);
291 }
292 }
293
294 if (pSecPolicy->Name)
295 {
296 ExFreePoolWithTag(pSecPolicy->Name, _POOL_TAG);
297 pSecPolicy->Name = NULL;
298 }
299
300 pSecPolicy->Initialized = FALSE;
301
302 RtlZeroMemory(pSecPolicy->RuleList, sizeof(pSecPolicy->RuleList));
303
304
305 KeReleaseSpinLock(&pSecPolicy->SpinLock, irql);
306}
307
308
309
310/*
311 * LoadSecurityPolicy()
312 *
313 * Description:
314 * Parses and loads a security policy.
315 *
316 * Parameters:
317 * pSecPolicy - pointer to a security policy to initialize.
318 * PolicyFile - string containing the policy filename to parse
319 * FilePath - string containing full program path of the file we are loading policy for
320 *
321 * Returns:
322 * TRUE if security policy was successfully parsed and loaded, FALSE otherwise.
323 */
324
325BOOLEAN
326LoadSecurityPolicy(OUT PSECURITY_POLICY pSecPolicy, IN PWSTR PolicyFile, IN PWSTR FilePath)
327{
328 OBJECT_ATTRIBUTES oa;
329 HANDLE hFile = 0;
330 UNICODE_STRING usPolicyFile;
331 ULONG size;
332 NTSTATUS status;
333 IO_STATUS_BLOCK isb;
334 CHAR *p, buffer[POLICY_MAX_RULE_LENGTH];
335 INT64 offset;
336 BOOLEAN ret = TRUE, Critical = FALSE;
337
338
339 if (pSecPolicy == NULL || PolicyFile == NULL)
340 {
341 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("LoadSecurityPolicy(%x, %x, %x): NULL parameter\n", pSecPolicy, PolicyFile, FilePath));
342 return FALSE;
343 }
344
345
346 pSecPolicy->Initialized = FALSE;
347 pSecPolicy->DefaultPolicyAction = ACTION_NONE;
348 pSecPolicy->ProtectionFlags = BootingUp ? PROTECTION_ALL_OFF : PROTECTION_ALL_ON;
349
350
351 RtlInitUnicodeString(&usPolicyFile, PolicyFile);
352
353 LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("LoadSecurityPolicy: Parsing %S\n", usPolicyFile.Buffer));
354
355
356 InitializeObjectAttributes(&oa, &usPolicyFile, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
357
358 if (!NT_SUCCESS(ZwCreateFile(&hFile, GENERIC_READ, &oa, &isb,
359 NULL, 0, FILE_SHARE_READ, FILE_OPEN,
360 FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0)))
361 {
362 LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("LoadSecurityPolicy: Failed to open file %S\n", usPolicyFile.Buffer));
363 return FALSE;
364 }
365
366
367 offset = 0;
368 buffer[0] = 0;
369
370
371 /* only one thread at a time can use LoadPolicyMutex due to use of global variables (PolicyLineNumber, buffer) */
372 KeWaitForMutexObject(&LoadPolicyMutex, Executive, KernelMode, FALSE, NULL);
373
374
375 gPolicyLineNumber = 1;
376 gPolicyFilename = usPolicyFile.Buffer;
377 gFilePath = FilePath;
378
379 while (1)
380 {
381 status = ZwReadFile(hFile, NULL, NULL, NULL, &isb, (PVOID) buffer, sizeof(buffer) - 1,
382 (PLARGE_INTEGER) &offset, NULL);
383
384 if (! NT_SUCCESS(status))
385 {
386 if (status != STATUS_END_OF_FILE)
387 {
388 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("LoadSecurityPolicy: ZwReadFile failed rc=%x\n", status));
389 ret = FALSE;
390 PolicyDelete(pSecPolicy);
391 }
392
393 break;
394 }
395
396 if (isb.Information == 0)
397 break;
398
399 buffer[isb.Information] = '\0';
400
401 /*
402 * strchr() will return NULL when the line we read exceeds the size of the buffer or
403 * the last line was not '\n' terminated
404 */
405
406 if ((p = strchr(buffer, '\n')) == NULL)
407 {
408 /* don't try to parse very long lines */
409
410 if (isb.Information == sizeof(buffer) - 1)
411 {
412 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("LoadSecurityPolicy(%s:%d): Rule is too long\n", gPolicyFilename, gPolicyLineNumber));
413
414 PolicyDelete(pSecPolicy);
415
416 ret = FALSE;
417 break;
418 }
419
420
421 /* the last rule was not '\n' terminated */
422
423 if (PolicyParseRule(pSecPolicy, buffer, &Critical) == FALSE)
424 {
425 if (Critical == TRUE)
426 {
427 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_DEBUG, ("LoadSecurityPolicy(%S:%d): Encountered a critical error. Aborting.\n", gPolicyFilename, gPolicyLineNumber));
428
429 PolicyDelete(pSecPolicy);
430
431 ret = FALSE;
432 break;
433 }
434 }
435
436 ret = TRUE;
437
438 break;
439 }
440
441 *p = 0;
442
443 if (p != buffer && *(p - 1) == '\r')
444 *(p - 1) = 0;
445
446
447 if (PolicyParseRule(pSecPolicy, buffer, &Critical) == FALSE)
448 {
449 if (Critical == TRUE)
450 {
451 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_DEBUG, ("LoadSecurityPolicy(%S:%d): Encountered a critical error. Aborting.\n", gPolicyFilename, gPolicyLineNumber));
452
453 PolicyDelete(pSecPolicy);
454
455 ret = FALSE;
456 break;
457 }
458 }
459
460
461 offset += p - buffer + 1;
462
463
464 if (++gPolicyLineNumber > 10000)
465 {
466 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("LoadSecurityPolicy: Policy '%S' is too long. Maximum number of lines is 10000.\n", gPolicyFilename));
467
468 PolicyDelete(pSecPolicy);
469
470 ret = FALSE;
471 break;
472 }
473 }
474
475 ZwClose(hFile);
476
477
478 if (ret != FALSE)
479 {
480 pSecPolicy->Initialized = TRUE;
481
482 if (pSecPolicy->DefaultPolicyAction == ACTION_NONE)
483 pSecPolicy->DefaultPolicyAction = DEFAULT_POLICY_ACTION;
484 }
485
486
487 LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("LoadSecurityPolicy: Done Parsing %S. Total number of lines %d. (ret=%d)\n", usPolicyFile.Buffer, gPolicyLineNumber, ret));
488
489
490 KeReleaseMutex(&LoadPolicyMutex, FALSE);
491
492
493 return ret;
494}
495
496
497
498/*
499 * FindAndLoadSecurityPolicy()
500 *
501 * Description:
502 * Finds and loads a security policy associated with a specified executable filename.
503 *
504 * Parameters:
505 * pSecPolicy - pointer to a security policy to initialize.
506 * FilePath - string specifying the complete path to an executable
507 * UserName - optional username, if specified check for a policy in "username" directory first
508 *
509 * Returns:
510 * TRUE if security policy was successfully parsed and loaded, FALSE otherwise.
511 */
512
513BOOLEAN
514FindAndLoadSecurityPolicy(OUT PSECURITY_POLICY pSecPolicy, IN PWSTR FilePath, IN PWSTR UserName)
515{
516 PWSTR filename;
517 WCHAR PolicyPath[MAX_PATH];
518 BOOLEAN ret;
519 int len;
520
521
522 if (pSecPolicy == NULL || FilePath == NULL)
523 {
524 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("FindAndLoadSecurityPolicy: NULL argument %x %x\n", pSecPolicy, FilePath));
525 return FALSE;
526 }
527
528
529 if (KeGetCurrentIrql() != 0)
530 {
531 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("FindAndLoadSecurityPolicy(): irql=%d\n", KeGetCurrentIrql()));
532 return FALSE;
533 }
534
535
536 filename = wcsrchr(FilePath, L'\\');
537
538 if (filename == NULL)
539 filename = FilePath;
540 else
541 ++filename;
542
543
544 /* if user policy load fails, we loop here again to load the global policy */
545ReloadPolicy:
546
547 if (UserName != NULL)
548 _snwprintf(PolicyPath, MAX_PATH, L"\\??\\%s\\policy\\%s\\%s.policy", OzoneInstallPath, UserName, filename);
549 else
550 _snwprintf(PolicyPath, MAX_PATH, L"\\??\\%s\\policy\\%s.policy", OzoneInstallPath, filename);
551
552 PolicyPath[MAX_PATH - 1] = 0;
553
554
555 LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("FindAndLoadSecurityPolicy: Loading policy for %S (%S)\n", PolicyPath, FilePath));
556
557
558 ret = LoadSecurityPolicy(pSecPolicy, PolicyPath, FilePath);
559 if (ret == FALSE)
560 {
561 /* If we can't find a policy specific to a user, try to load a global policy instead */
562 if (UserName != NULL)
563 {
564 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("FindAndLoadSecurityPolicy: Cannot find '%S' policy for user '%S'. Looking for a global policy..\n", filename, UserName));
565
566 UserName = NULL;
567
568 goto ReloadPolicy;
569 }
570
571 return FALSE;
572 }
573
574
575 /* allocate extra space for ".policy" string */
576 len = wcslen(filename) + 7 + 1;
577
578 pSecPolicy->Name = ExAllocatePoolWithTag(NonPagedPool, len * sizeof(WCHAR), _POOL_TAG);
579
580 if (pSecPolicy->Name != NULL)
581 {
582 _snwprintf(pSecPolicy->Name, len, L"%s.policy", filename);
583 }
584 else
585 {
586 PolicyDelete(pSecPolicy);
587 ret = FALSE;
588 }
589
590 return ret;
591}
592
593
594
595/*
596 * PolicyParseRule()
597 *
598 * Description:
599 * Parses a specified rule.
600 *
601 * Parameters:
602 * pSecPolicy - pointer to a security policy that will contain the parsed rule.
603 * rule - string buffer containing a rule to parse.
604 * Critical - Boolean indicating whether the parser should abort parsing the policy due to a critical error.
605 *
606 * Returns:
607 * TRUE if the policy rule was successfully parsed and loaded, FALSE otherwise.
608 */
609
610#define SKIP_WHITESPACE(str) do { while(*(str) == ' ' || *(str) == '\t') ++(str); } while(0)
611#define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\t')
612
613/* macro shortcut for bailing out of PolicyParseRule() in case of an error */
614
615#define ABORT_PolicyParseRule(msg) \
616 do { \
617 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Encountered an error while parsing %S:%d :\n", gPolicyFilename, gPolicyLineNumber)); \
618 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, msg); \
619 return FALSE; \
620 } while (0)
621
622
623static BOOLEAN
624PolicyParseRule(OUT PSECURITY_POLICY pSecPolicy, IN PCHAR rule, OUT BOOLEAN *Critical)
625{
626 CHAR ServiceName[POLICY_MAX_SERVICE_NAME_LENGTH];
627 PCHAR OriginalRule = rule;
628 int i = 0, SawSpace = 0, SawUnderscore = 0;
629
630
631 *Critical = FALSE;
632
633
634 SKIP_WHITESPACE(rule);
635
636
637 /* skip empty lines */
638
639 if (*rule == 0)
640 return TRUE;
641
642
643 /* comments start with '#' */
644
645 if (*rule == '#')
646 return TRUE;
647
648
649 /*
650 * Parse the service name. Format: "ServiceName:" or "ServiceName_OperationType:"
651 * where ServiceName can be "file", "registry", "event", "memory", etc
652 * and OperationType can be "read", "write", "rw"
653 */
654
655 while (*rule != 0 && *rule != ':')
656 {
657 /* is the specified syscall name too long? */
658
659 if (i > POLICY_MAX_SERVICE_NAME_LENGTH - 1)
660 ABORT_PolicyParseRule(("Rule type specification is too long. Maximum rule type specification length is %d characters.\n", POLICY_MAX_SERVICE_NAME_LENGTH));
661
662
663 /* allow whitespace before the colon */
664
665 if (IS_WHITESPACE(*rule))
666 {
667 ++rule;
668
669 SawSpace = 1;
670
671 continue;
672 }
673
674
675 /* Service Names are not allowed to contain a space */
676
677 if (SawSpace)
678 ABORT_PolicyParseRule(("Rule type specification cannot contain a space\n"));
679
680
681 /* Expecting to see 1 underscore '_' */
682
683 if (*rule == '_')
684 {
685 /* There can be only be 1 underscore char. and it cannot be the first char. */
686 if (i == 0 || SawUnderscore)
687 ABORT_PolicyParseRule(("Rule type specification cannot contain multiple underscore characters\n"));
688
689 /* remember the underscore position */
690 SawUnderscore = i;
691 }
692
693
694 ServiceName[i++] = *rule++;
695 }
696
697
698 /* Expecting to have read more than 1 character, finishing with a ':' */
699 if (i == 0 || *rule++ != ':')
700 ABORT_PolicyParseRule(("Colon not found. Rule type specification must end with a colon ':'.\n"));
701
702
703 ServiceName[i] = 0;
704
705
706 SKIP_WHITESPACE(rule);
707
708
709 /* didn't see any underscores. assume "system_call_name:" format */
710
711 if (SawUnderscore == 0)
712 ABORT_PolicyParseRule(("Underscore not found. Rule type specification must contain an underscore.\n"));
713
714
715 ServiceName[SawUnderscore] = 0;
716
717
718 // file operation
719 if (strlen(ServiceName) == 4 && _stricmp(ServiceName, "file") == 0)
720 return PolicyParseObjectRule(pSecPolicy, RULE_FILE, ServiceName + SawUnderscore + 1, rule);
721
722 // directory operation
723 if (strlen(ServiceName) == 9 && _stricmp(ServiceName, "directory") == 0)
724 return PolicyParseObjectRule(pSecPolicy, RULE_DIRECTORY, ServiceName + SawUnderscore + 1, rule);
725
726 // registry operation
727 if (strlen(ServiceName) == 8 && _stricmp(ServiceName, "registry") == 0)
728 return PolicyParseObjectRule(pSecPolicy, RULE_REGISTRY, ServiceName + SawUnderscore + 1, rule);
729
730 // memory section operation
731 if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "section") == 0)
732 return PolicyParseObjectRule(pSecPolicy, RULE_SECTION, ServiceName + SawUnderscore + 1, rule);
733
734 // memory section / dll operation
735 if (strlen(ServiceName) == 3 && _stricmp(ServiceName, "dll") == 0)
736 return PolicyParseObjectRule(pSecPolicy, RULE_DLL, ServiceName + SawUnderscore + 1, rule);
737
738 // event operation
739 if (strlen(ServiceName) == 5 && _stricmp(ServiceName, "event") == 0)
740 return PolicyParseObjectRule(pSecPolicy, RULE_EVENT, ServiceName + SawUnderscore + 1, rule);
741
742 // semaphore operation
743 if (strlen(ServiceName) == 9 && _stricmp(ServiceName, "semaphore") == 0)
744 return PolicyParseObjectRule(pSecPolicy, RULE_SEMAPHORE, ServiceName + SawUnderscore + 1, rule);
745
746 // mailslot operation
747 if (strlen(ServiceName) == 8 && _stricmp(ServiceName, "mailslot") == 0)
748 return PolicyParseObjectRule(pSecPolicy, RULE_MAILSLOT, ServiceName + SawUnderscore + 1, rule);
749
750 // named pipe operation
751 if (strlen(ServiceName) == 9 && _stricmp(ServiceName, "namedpipe") == 0)
752 return PolicyParseObjectRule(pSecPolicy, RULE_NAMEDPIPE, ServiceName + SawUnderscore + 1, rule);
753
754 // job object operation
755 if (strlen(ServiceName) == 3 && _stricmp(ServiceName, "job") == 0)
756 return PolicyParseObjectRule(pSecPolicy, RULE_JOB, ServiceName + SawUnderscore + 1, rule);
757
758 // mutant operation
759 if (strlen(ServiceName) == 5 && _stricmp(ServiceName, "mutex") == 0)
760 return PolicyParseObjectRule(pSecPolicy, RULE_MUTANT, ServiceName + SawUnderscore + 1, rule);
761
762 // port operation
763 if (strlen(ServiceName) == 4 && _stricmp(ServiceName, "port") == 0)
764 return PolicyParseObjectRule(pSecPolicy, RULE_PORT, ServiceName + SawUnderscore + 1, rule);
765
766 // symlink operation
767 if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "symlink") == 0)
768 return PolicyParseObjectRule(pSecPolicy, RULE_SYMLINK, ServiceName + SawUnderscore + 1, rule);
769
770 // timer operation
771 if (strlen(ServiceName) == 5 && _stricmp(ServiceName, "timer") == 0)
772 return PolicyParseObjectRule(pSecPolicy, RULE_TIMER, ServiceName + SawUnderscore + 1, rule);
773
774 // process operation
775 if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "process") == 0)
776 return PolicyParseObjectRule(pSecPolicy, RULE_PROCESS, ServiceName + SawUnderscore + 1, rule);
777
778 // driver operation
779 if (strlen(ServiceName) == 6 && _stricmp(ServiceName, "driver") == 0)
780 return PolicyParseObjectRule(pSecPolicy, RULE_DRIVER, ServiceName + SawUnderscore + 1, rule);
781
782 // object directory operation
783 if (strlen(ServiceName) == 6 && _stricmp(ServiceName, "dirobj") == 0)
784 return PolicyParseObjectRule(pSecPolicy, RULE_DIROBJ, ServiceName + SawUnderscore + 1, rule);
785
786 // atom operation
787 if (strlen(ServiceName) == 4 && _stricmp(ServiceName, "atom") == 0)
788 return PolicyParseObjectRule(pSecPolicy, RULE_ATOM, ServiceName + SawUnderscore + 1, rule);
789
790 // network operation
791 if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "network") == 0)
792 return PolicyParseObjectRule(pSecPolicy, RULE_NETWORK, ServiceName + SawUnderscore + 1, rule);
793
794 // service operation
795 if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "service") == 0)
796 return PolicyParseObjectRule(pSecPolicy, RULE_SERVICE, ServiceName + SawUnderscore + 1, rule);
797
798 // time operation
799 if (strlen(ServiceName) == 4 && _stricmp(ServiceName, "time") == 0)
800 return PolicyParseObjectRule(pSecPolicy, RULE_TIME, ServiceName + SawUnderscore + 1, rule);
801
802 // token operation
803 if (strlen(ServiceName) == 5 && _stricmp(ServiceName, "token") == 0)
804 return PolicyParseObjectRule(pSecPolicy, RULE_TOKEN, ServiceName + SawUnderscore + 1, rule);
805
806
807 /*
808 * non object rules
809 */
810
811 // syscall
812 if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "syscall") == 0)
813 return PolicyParseSyscallRule(pSecPolicy, ServiceName + SawUnderscore + 1, rule);
814
815 // policy
816 if (strlen(ServiceName) == 6 && _stricmp(ServiceName, "policy") == 0)
817 return PolicyParsePolicyRule(pSecPolicy, ServiceName + SawUnderscore + 1, rule, Critical);
818
819 // protection
820 if (strlen(ServiceName) == 10 && _stricmp(ServiceName, "protection") == 0)
821 return PolicyParseProtectionRule(pSecPolicy, ServiceName + SawUnderscore + 1, rule);
822
823 // media
824 if (strlen(ServiceName) == 5 && _stricmp(ServiceName, "media") == 0)
825 return PolicyParseMediaRule(pSecPolicy, ServiceName + SawUnderscore + 1, rule);
826
827
828
829 ABORT_PolicyParseRule(("Invalid rule type specification: '%s'.\n", ServiceName));
830}
831
832
833
834/*
835 * ParseDllOperation()
836 *
837 * Description:
838 * Parses an operation (i.e. "load" in "dll_load") specified for a DLL object rule.
839 *
840 * Parameters:
841 * Operation - specified operation.
842 *
843 * Returns:
844 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
845 */
846
847UCHAR
848ParseDllOperation(IN PCHAR Operation)
849{
850 if (strlen(Operation) == 4 && _stricmp(Operation, "load") == 0)
851 return OP_LOAD;
852
853 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
854 return OP_ALL;
855
856 return OP_INVALID;
857}
858
859
860
861/*
862 * ParseTimeOperation()
863 *
864 * Description:
865 * Parses an operation (i.e. "change" in "time_change") specified for a Time object rule.
866 *
867 * Parameters:
868 * Operation - specified operation.
869 *
870 * Returns:
871 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
872 */
873
874UCHAR
875ParseTimeOperation(IN PCHAR Operation)
876{
877 if (strlen(Operation) == 6 && _stricmp(Operation, "change") == 0)
878 return OP_LOAD;
879
880 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
881 return OP_ALL;
882
883 return OP_INVALID;
884}
885
886
887
888/*
889 * ParseTokenOperation()
890 *
891 * Description:
892 * Parses an operation (i.e. "modify" in "token_modify") specified for a Token object rule.
893 *
894 * Parameters:
895 * Operation - specified operation.
896 *
897 * Returns:
898 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
899 */
900
901UCHAR
902ParseTokenOperation(IN PCHAR Operation)
903{
904 if (strlen(Operation) == 6 && _stricmp(Operation, "modify") == 0)
905 return OP_TOKEN_MODIFY;
906
907 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
908 return OP_ALL;
909
910 return OP_INVALID;
911}
912
913
914
915/*
916 * ParsePortOperation()
917 *
918 * Description:
919 * Parses an operation (i.e. "create" in "port_create") specified for a port object rule.
920 *
921 * Parameters:
922 * Operation - specified operation.
923 *
924 * Returns:
925 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
926 */
927
928UCHAR
929ParsePortOperation(IN PCHAR Operation)
930{
931 if (strlen(Operation) == 6 && _stricmp(Operation, "create") == 0)
932 return OP_PORT_CREATE;
933
934 if (strlen(Operation) == 7 && _stricmp(Operation, "connect") == 0)
935 return OP_PORT_CONNECT;
936
937 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
938 return OP_ALL;
939
940
941 return OP_INVALID;
942}
943
944
945
946/*
947 * ParseCreateOpenOperation()
948 *
949 * Description:
950 * Parses a create or an open operation (i.e. "create" in "dirobj_create").
951 *
952 * Parameters:
953 * Operation - specified operation.
954 *
955 * Returns:
956 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
957 */
958
959UCHAR
960ParseCreateOpenOperation(IN PCHAR Operation)
961{
962 if (strlen(Operation) == 6 && _stricmp(Operation, "create") == 0)
963 return OP_CREATE;
964
965 if (strlen(Operation) == 4 && _stricmp(Operation, "open") == 0)
966 return OP_OPEN;
967
968 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
969 return OP_ALL;
970
971
972 return OP_INVALID;
973}
974
975
976
977/*
978 * ParseAtomOperation()
979 *
980 * Description:
981 * Parses an operation (i.e. "find" in "atom_find") specified for an atom object rule.
982 *
983 * Parameters:
984 * Operation - specified operation.
985 *
986 * Returns:
987 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
988 */
989
990UCHAR
991ParseAtomOperation(IN PCHAR Operation)
992{
993 if (strlen(Operation) == 4 && _stricmp(Operation, "find") == 0)
994 return OP_FIND;
995
996 if (strlen(Operation) == 3 && _stricmp(Operation, "add") == 0)
997 return OP_ADD;
998
999 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
1000 return OP_ALL;
1001
1002
1003 return OP_INVALID;
1004}
1005
1006
1007
1008/*
1009 * ParseDriverOperation()
1010 *
1011 * Description:
1012 * Parses an operation (i.e. "load" in "driver_load") specified for a driver object rule.
1013 *
1014 * Parameters:
1015 * Operation - specified operation.
1016 *
1017 * Returns:
1018 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
1019 */
1020
1021UCHAR
1022ParseDriverOperation(IN PCHAR Operation)
1023{
1024 if (strlen(Operation) == 4 && _stricmp(Operation, "load") == 0)
1025 return OP_LOAD;
1026
1027 if (strlen(Operation) == 7 && _stricmp(Operation, "regload") == 0)
1028 return OP_REGLOAD;
1029
1030 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
1031 return OP_ALL;
1032
1033 return OP_INVALID;
1034}
1035
1036
1037
1038/*
1039 * ParseDirectoryOperation()
1040 *
1041 * Description:
1042 * Parses an operation (i.e. "create" in "directory_create") specified for a directory object rule.
1043 *
1044 * Parameters:
1045 * Operation - specified operation.
1046 *
1047 * Returns:
1048 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
1049 */
1050
1051UCHAR
1052ParseDirectoryOperation(IN PCHAR Operation)
1053{
1054 if (strlen(Operation) == 6 && _stricmp(Operation, "create") == 0)
1055 return OP_DIR_CREATE;
1056
1057 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
1058 return OP_ALL;
1059
1060 return OP_INVALID;
1061}
1062
1063
1064
1065/*
1066 * ParseObjectOperation()
1067 *
1068 * Description:
1069 * Parses an operation (i.e. "read" in "file_read") specified for an object (file, registry, etc) rule.
1070 *
1071 * Parameters:
1072 * Operation - specified operation.
1073 *
1074 * Returns:
1075 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
1076 */
1077
1078UCHAR
1079ParseObjectOperation(IN PCHAR Operation)
1080{
1081 if (strlen(Operation) == 4 && _stricmp(Operation, "read") == 0)
1082 return OP_READ;
1083
1084 if (strlen(Operation) == 5 && _stricmp(Operation, "write") == 0)
1085 return OP_WRITE;
1086
1087 if (strlen(Operation) == 2 && _stricmp(Operation, "rw") == 0)
1088 return (OP_READ | OP_WRITE);
1089
1090 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
1091 return OP_ALL;
1092
1093 //XXX valid for files only
1094// if (strlen(Operation) == 6 && _stricmp(Operation, "append") == 0)
1095// return OP_APPEND;
1096
1097 if (strlen(Operation) == 7 && _stricmp(Operation, "execute") == 0)
1098 return OP_EXECUTE;
1099
1100 if (strlen(Operation) == 6 && _stricmp(Operation, "delete") == 0)
1101 return OP_DELETE;
1102
1103
1104 return OP_INVALID;
1105}
1106
1107
1108
1109/*
1110 * ParseProcessOperation()
1111 *
1112 * Description:
1113 * Parses an operation (i.e. "execute" in "process_execute") specified for a process rule.
1114 *
1115 * Parameters:
1116 * Operation - specified operation.
1117 *
1118 * Returns:
1119 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
1120 */
1121
1122UCHAR
1123ParseProcessOperation(IN PCHAR Operation)
1124{
1125 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
1126 return OP_ALL;
1127
1128 if (strlen(Operation) == 7 && _stricmp(Operation, "execute") == 0)
1129 return OP_PROC_EXECUTE;
1130
1131 if (strlen(Operation) == 4 && _stricmp(Operation, "open") == 0)
1132 return OP_PROC_OPEN;
1133
1134
1135 return OP_INVALID;
1136}
1137
1138
1139
1140/*
1141 * ParseServiceOperation()
1142 *
1143 * Description:
1144 * Parses an operation (i.e. "start" in "service_start") specified for a service object rule.
1145 *
1146 * Parameters:
1147 * Operation - specified operation.
1148 *
1149 * Returns:
1150 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
1151 */
1152
1153UCHAR
1154ParseServiceOperation(IN PCHAR Operation)
1155{
1156 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
1157 return OP_ALL;
1158
1159 /*
1160 * We cannot distinguish between various service operations since service rules are actually
1161 * enforced by the registry rules. Thus convert all operations to OP_ALL for now.
1162 */
1163
1164 if (strlen(Operation) == 5 && _stricmp(Operation, "start") == 0)
1165 return OP_ALL;//OP_SERVICE_START;
1166
1167 if (strlen(Operation) == 4 && _stricmp(Operation, "stop") == 0)
1168 return OP_ALL;//OP_SERVICE_STOP;
1169
1170 if (strlen(Operation) == 6 && _stricmp(Operation, "create") == 0)
1171 return OP_ALL;//OP_SERVICE_CREATE;
1172
1173 if (strlen(Operation) == 6 && _stricmp(Operation, "delete") == 0)
1174 return OP_ALL;//OP_SERVICE_DELETE;
1175
1176 return OP_INVALID;
1177}
1178
1179
1180
1181/*
1182 * ParseNetworkOperation()
1183 *
1184 * Description:
1185 * Parses an operation (i.e. "bind" in "network_bind") specified for a network object rule.
1186 *
1187 * Parameters:
1188 * Operation - specified operation.
1189 *
1190 * Returns:
1191 * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation.
1192 */
1193
1194UCHAR
1195ParseNetworkOperation(IN PCHAR Operation)
1196{
1197 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
1198 return OP_ALL;
1199
1200 if (strlen(Operation) == 10 && _stricmp(Operation, "tcpconnect") == 0)
1201 return OP_TCPCONNECT;
1202
1203 if (strlen(Operation) == 10 && _stricmp(Operation, "udpconnect") == 0)
1204 return OP_UDPCONNECT;
1205
1206 if (strlen(Operation) == 7 && _stricmp(Operation, "connect") == 0)
1207 return OP_CONNECT;
1208
1209 if (strlen(Operation) == 4 && _stricmp(Operation, "bind") == 0)
1210 return OP_BIND;
1211
1212 return OP_INVALID;
1213}
1214
1215
1216
1217
1218/*****************************************************************************/
1219
1220
1221
1222
1223/*
1224 * ParseNetworkObject()
1225 *
1226 * Description:
1227 * Parses the specified network address (i.e. "127.0.0.1:443").
1228 *
1229 * Parameters:
1230 * name - string value to parse.
1231 * Object - pointer to an Object where the final result will be saved
1232 * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions.
1233 *
1234 * Returns:
1235 * INVALID_OBJECT_SIZE if the specified network address is invalid. 0 to indicate SUCCESS.
1236 * Network addresses do not require any additional memory to be allocated thus the returned size is 0.
1237 */
1238
1239size_t
1240ParseNetworkObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard)
1241{
1242 PCHAR colon;
1243
1244
1245 //XXX
1246 // for now connect format is "ipaddr" while bind format is "ipaddr:port"
1247
1248 colon = strchr(name, ':');
1249
1250 if (colon)
1251 {
1252 *Object = colon + 1;
1253// if ((*Object = (PVOID) atoi(colon + 1)) == 0)
1254// return INVALID_OBJECT_SIZE;
1255 }
1256 else
1257 {
1258 *Object = name;
1259// if ((*Object = (PVOID) inet_addr(name)) == 0)
1260// return INVALID_OBJECT_SIZE;
1261 }
1262
1263
1264 return strlen(*Object);
1265}
1266
1267
1268
1269/*
1270 * ParseStub()
1271 *
1272 * Description:
1273 * Parse stub for strings that do no require any further parsing.
1274 *
1275 * Parameters:
1276 * name - string value to parse.
1277 * Object - pointer to an Object where the final result will be saved.
1278 * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions.
1279 *
1280 * Returns:
1281 * Length of the specified string value.
1282 */
1283
1284size_t
1285ParseStub(IN PCHAR name, OUT PCHAR *ObjectName, OUT BOOLEAN *wildcard)
1286{
1287 *ObjectName = name;
1288
1289 return strlen(name);
1290}
1291
1292
1293
1294/*
1295 * ParseRegistryObject()
1296 *
1297 * Description:
1298 * Convert user land registry object names into their kernel land equivalents.
1299 *
1300 * Parameters:
1301 * name - string value to parse.
1302 * Object - pointer to an Object where the final result will be saved.
1303 * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions.
1304 *
1305 * Returns:
1306 * Length of the specified string value.
1307 */
1308
1309size_t
1310ParseRegistryObject(IN PCHAR name, OUT PCHAR *ObjectName, OUT BOOLEAN *wildcard)
1311{
1312 PCHAR key;
1313 static CHAR buffer[MAX_PATH] = { 0 };
1314
1315
1316 if (_strnicmp(name, "HKEY_LOCAL_MACHINE\\", 19) == 0)
1317 {
1318 /* replace HKEY_LOCAL_MACHINE\ with kernel equivalent of \REGISTRY\MACHINE\ */
1319
1320 strcpy(name + 1, "\\REGISTRY\\MACHINE");
1321 name[18] = '\\';
1322
1323 key = name + 1;
1324 }
1325 else if (_strnicmp(name, "HKEY_USERS\\", 11) == 0)
1326 {
1327 /* replace HKEY_USERS\ with kernel equivalent of \REGISTRY\USER\ */
1328
1329 strcpy(buffer, "\\REGISTRY\\USER\\");
1330 strncat(buffer, name + 11, MAX_PATH - 12);
1331
1332 key = buffer;
1333 }
1334 else
1335 {
1336 key = name;
1337 }
1338
1339
1340 *ObjectName = key;
1341
1342
1343 return strlen(key);
1344}
1345
1346
1347
1348/*
1349 * ParseFileObject()
1350 *
1351 * Description:
1352 * Convert user land file object names into their kernel land equivalents.
1353 *
1354 * Parameters:
1355 * name - string value to parse.
1356 * Object - pointer to an Object where the final result will be saved.
1357 * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions.
1358 *
1359 * Returns:
1360 * Length of the specified string value.
1361 */
1362
1363size_t
1364ParseFileObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard)
1365{
1366 PCHAR filename;
1367 static CHAR buffer[MAX_PATH] = { 0 }; //XXX not SMP safe
1368
1369
1370 if (_strnicmp(name, "%systemdrive%:", 14) == 0)
1371 {
1372 name[12] = SystemDrive;
1373
1374 name += 12;
1375 }
1376
1377
1378 // match drive wildcards such as "?:" and "*:" with "\Device\*"
1379 if (name[1] == ':' && name[2] == '\\' && (name[0] == '?' || name[0] == '*'))
1380 {
1381#if 0
1382 ConvertLongFileNameToShort(name, buffer + 7, MAX_PATH - 7);
1383
1384 strcpy(buffer, "\\Device\\*");
1385 buffer[9] = '\\'; /* replace the zero terminator */
1386
1387 filename = buffer;
1388#endif
1389
1390 strcpy(name - 7, "\\Device\\*");
1391
1392 /*
1393 * replace "\Device\*" terminating zero with a '\'
1394 * since name is just a pointer to FullName+7, FullName now contains
1395 * \Device\*\<user specified path>
1396 */
1397
1398 name[2] = '\\';
1399
1400 filename = name - 7;
1401
1402
1403 // mark the rule as wildcard even if the user (mistakenly) used "eq"
1404 // XXX or should we throw an error if wildcard==0?
1405 *wildcard = TRUE;
1406 }
1407 else if (isalpha(name[0]) && name[1] == ':')
1408 {
1409#if 0
1410 CHAR buffer2[MAX_PATH];
1411
1412
1413 ConvertLongFileNameToShort(name, buffer2 + 4, MAX_PATH - 4);
1414
1415 buffer2[0] = '\\';
1416 buffer2[1] = '?';
1417 buffer2[2] = '?';
1418 buffer2[3] = '\\';
1419
1420 if (ResolveFilename(buffer2, buffer, MAX_PATH) == FALSE)
1421 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("ParseFileObject: ResolveFilename(%s) failed\n", name - 4));
1422#endif
1423
1424
1425 // match <letter>: drive specifications and prepend "\??\" to them
1426
1427 *(name - 4) = '\\';
1428 *(name - 3) = '?';
1429 *(name - 2) = '?';
1430 *(name - 1) = '\\';
1431
1432 if (ResolveFilename(name - 4, buffer, MAX_PATH) == FALSE)
1433 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("ParseFileObject: ResolveFilename(%s) failed\n", name - 4));
1434
1435 filename = buffer;
1436 }
1437 else if (_strnicmp(name, "%systemroot%\\", 13) == 0)
1438 {
1439 strcpy(buffer, SystemRoot);
1440 strcat(buffer, name + 12);
1441
1442 filename = buffer;
1443 }
1444 else if (_strnicmp(name, "\\pipe\\", 6) == 0)
1445 {
1446 strcpy(buffer, "\\device\\namedpipe");
1447 strcat(buffer, name + 5);
1448
1449 filename = buffer;
1450 }
1451 else
1452 {
1453 filename = name;
1454 }
1455
1456
1457 *Object = filename;
1458
1459 return strlen(filename);
1460}
1461
1462
1463
1464/*
1465 * ParseProcessObject()
1466 *
1467 * Description:
1468 * Convert user land process object names into their kernel land equivalents (strip the drive specification).
1469 *
1470 * Parameters:
1471 * name - string value to parse.
1472 * Object - pointer to an Object where the final result will be saved.
1473 * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions.
1474 *
1475 * Returns:
1476 * Length of the specified string value.
1477 */
1478
1479size_t
1480ParseProcessObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard)
1481{
1482 static CHAR buffer[MAX_PATH] = { 0 };
1483
1484
1485 if ((name = StripFileMacros(name, buffer, MAX_PATH)) == NULL)
1486 return INVALID_OBJECT_SIZE;
1487
1488
1489 *Object = name;
1490
1491 return strlen(name);
1492}
1493
1494
1495
1496/*
1497 * ParseBaseNamedObjectsObject()
1498 *
1499 * Description:
1500 * Convert user land object names into their kernel land equivalents.
1501 *
1502 * Parameters:
1503 * name - string value to parse.
1504 * Object - pointer to an Object where the final result will be saved.
1505 * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions.
1506 *
1507 * Returns:
1508 * Length of the specified string value.
1509 */
1510
1511size_t
1512ParseBaseNamedObjectsObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard)
1513{
1514 PCHAR ObjectName;
1515
1516
1517 /*
1518 * if an object name does not start with a slash '\' then prepend '\BaseNamedObjects\' to it
1519 */
1520
1521 if (name[0] != '\\')
1522 {
1523 //XXX this is a hack, we are prepending to our buffer, knowing that there is space there
1524 strcpy(name - 18, "\\BaseNamedObjects");
1525
1526 *(name - 1) = '\\';
1527
1528 ObjectName = name - 18;
1529 }
1530 else
1531 {
1532 ObjectName = name;
1533 }
1534
1535
1536 *Object = ObjectName;
1537
1538 return strlen(ObjectName);
1539}
1540
1541
1542
1543/*
1544 * ParseMailslotObject()
1545 *
1546 * Description:
1547 * Convert user land mailslot object names into their kernel land equivalents.
1548 *
1549 * Parameters:
1550 * name - string value to parse.
1551 * Object - pointer to an Object where the final result will be saved.
1552 * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions.
1553 *
1554 * Returns:
1555 * Length of the specified string value.
1556 */
1557
1558size_t
1559ParseMailslotObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard)
1560{
1561 PCHAR MailslotName;
1562
1563
1564 /*
1565 * if the mailslot name does not start with a slash '\' then prepend '\Device\Mailslot\' to the name
1566 */
1567
1568 if (name[0] != '\\')
1569 {
1570 //XXX this is a hack, we are prepending to our buffer, knowing that there is space there
1571 strcpy(name - 17, "\\Device\\Mailslot");
1572
1573 *(name - 1) = '\\';
1574
1575 MailslotName = name - 17;
1576 }
1577 else
1578 {
1579 MailslotName = name;
1580 }
1581
1582
1583 *Object = MailslotName;
1584
1585 return strlen(MailslotName);
1586}
1587
1588
1589
1590/*
1591 * ParseNamedpipeObject()
1592 *
1593 * Description:
1594 * Convert user land namedpipe object names into their kernel land equivalents.
1595 *
1596 * Parameters:
1597 * name - string value to parse.
1598 * Object - pointer to an Object where the final result will be saved.
1599 * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions.
1600 *
1601 * Returns:
1602 * Length of the specified string value.
1603 */
1604
1605size_t
1606ParseNamedpipeObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard)
1607{
1608 PCHAR NamedpipeName;
1609
1610
1611 /*
1612 * if the namedpipe name does not start with a slash '\' then prepend '\Device\Namedpipe\' to the name
1613 */
1614
1615 if (name[0] != '\\')
1616 {
1617 //XXX this is a hack, we are prepending to our buffer, knowing that there is space there
1618 strcpy(name - 18, "\\Device\\Namedpipe");
1619
1620 *(name - 1) = '\\';
1621
1622 NamedpipeName = name - 18;
1623 }
1624 else
1625 {
1626 NamedpipeName = name;
1627 }
1628
1629
1630 *Object = NamedpipeName;
1631
1632 return strlen(NamedpipeName);
1633}
1634
1635
1636
1637/*
1638 * ParseDllObject()
1639 *
1640 * Description:
1641 * Convert user land DLL object names into their kernel land equivalents.
1642 * Since DLL rules are actually enforced by section rules, we just append DLL names to
1643 * '\KnownDlls\' string which is used by section rules.
1644 *
1645 * Parameters:
1646 * name - string value to parse.
1647 * Object - pointer to an Object where the final result will be saved.
1648 * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions.
1649 *
1650 * Returns:
1651 * Length of the specified string value.
1652 */
1653
1654size_t
1655ParseDllObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard)
1656{
1657 PCHAR DllName;
1658
1659
1660 /*
1661 * if the DLL name does not start with a slash '\' then prepend '\KnownDlls\' to the name
1662 */
1663
1664 if (name[0] != '\\')
1665 {
1666 strcpy(name - 11, "\\KnownDlls");
1667
1668 *(name - 1) = '\\';
1669
1670 DllName = name - 11;
1671 }
1672 else
1673 {
1674 DllName = name;
1675 }
1676
1677
1678 *Object = DllName;
1679
1680 return strlen(DllName);
1681}
1682
1683
1684
1685/*
1686 * ParseTimeObject()
1687 *
1688 * Description:
1689 * Time rule specifications are not supposed to have any object names specified.
1690 * Return an error.
1691 *
1692 * Parameters:
1693 * name - string value to parse.
1694 * Object - pointer to an Object where the final result will be saved.
1695 * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions.
1696 *
1697 * Returns:
1698 * An error.
1699 */
1700
1701size_t
1702ParseTimeObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard)
1703{
1704 return INVALID_OBJECT_SIZE;
1705}
1706
1707
1708
1709/*
1710 * ParseServiceObject()
1711 *
1712 * Description:
1713 * Convert user land service object names into their kernel land equivalents.
1714 * Since service rules are actually enforced by registry rules, we just append service names
1715 * to '\Registry\Machine\System\*ControlSet*\Services\' string which is used by registry rules.
1716 *
1717 * Parameters:
1718 * name - string value to parse.
1719 * Object - pointer to an Object where the final result will be saved.
1720 * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions.
1721 *
1722 * Returns:
1723 * Length of the specified string value.
1724 * INVALID_OBJECT_SIZE is service name is too long.
1725 */
1726
1727size_t
1728ParseServiceObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard)
1729{
1730 static CHAR buffer[MAX_PATH] = { 0 }; //XXX not SMP safe
1731
1732
1733 if (strlen(name) > 64)
1734 {
1735 return INVALID_OBJECT_SIZE;
1736 }
1737
1738 strcpy(buffer, "\\Registry\\Machine\\System\\*ControlSet*\\Services\\");
1739 strcat(buffer, name);
1740
1741 *wildcard = TRUE;
1742
1743 *Object = buffer;
1744
1745
1746 return strlen(buffer);
1747}
1748
1749
1750
1751typedef size_t (*OBJECT_PARSER)(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard);
1752typedef UCHAR (*OPERATION_TYPE_PARSER)(IN PCHAR name);
1753
1754
1755/* in C++ these would be member methods */
1756
1757struct _ObjectParseOps
1758{
1759 RULE_TYPE RuleType;
1760 OBJECT_PARSER ObjectNameParser;
1761 OPERATION_TYPE_PARSER OperationTypeParser;
1762
1763} ObjectParseOps[] =
1764{
1765 { RULE_FILE, ParseFileObject, ParseObjectOperation },
1766 { RULE_DIRECTORY, ParseFileObject, ParseDirectoryOperation },
1767 { RULE_MAILSLOT, ParseMailslotObject, ParseObjectOperation },
1768 { RULE_NAMEDPIPE, ParseNamedpipeObject, ParseObjectOperation },
1769 { RULE_REGISTRY, ParseRegistryObject, ParseObjectOperation },
1770 { RULE_SECTION, ParseBaseNamedObjectsObject, ParseObjectOperation },
1771 { RULE_DLL, ParseDllObject, ParseDllOperation },
1772 { RULE_EVENT, ParseBaseNamedObjectsObject, ParseCreateOpenOperation },
1773 { RULE_SEMAPHORE, ParseBaseNamedObjectsObject, ParseCreateOpenOperation },
1774 { RULE_JOB, ParseBaseNamedObjectsObject, ParseCreateOpenOperation },
1775 { RULE_MUTANT, ParseBaseNamedObjectsObject, ParseCreateOpenOperation },
1776 { RULE_PORT, ParseStub, ParsePortOperation },
1777 { RULE_SYMLINK, ParseStub, ParseCreateOpenOperation },
1778 { RULE_TIMER, ParseBaseNamedObjectsObject, ParseCreateOpenOperation },
1779 { RULE_PROCESS, ParseProcessObject, ParseProcessOperation },
1780 { RULE_DRIVER, ParseProcessObject, ParseDriverOperation },
1781 { RULE_DIROBJ, ParseStub, ParseCreateOpenOperation },
1782 { RULE_ATOM, ParseStub, ParseAtomOperation },
1783 { RULE_NETWORK, ParseNetworkObject, ParseNetworkOperation },
1784 { RULE_SERVICE, ParseServiceObject, ParseServiceOperation },
1785 { RULE_TIME, ParseTimeObject, ParseTimeOperation },
1786 { RULE_TOKEN, ParseTimeObject, ParseTokenOperation },
1787};
1788
1789
1790
1791/*
1792 * PolicyParseActionClause()
1793 *
1794 * Description:
1795 * .
1796 *
1797 * Parameters:
1798 * .
1799 *
1800 * Returns:
1801 * .
1802 */
1803
1804BOOLEAN
1805PolicyParseActionClause(PCHAR rule, ACTION_TYPE *ActionType, UCHAR *RuleNumber)
1806{
1807 UCHAR len = 0, num = 0;
1808
1809
1810 SKIP_WHITESPACE(rule);
1811
1812
1813 if (_strnicmp(rule, "permit", 6) == 0)
1814 {
1815 rule += 6;
1816
1817 *ActionType = ACTION_PERMIT;
1818 }
1819 else if (_strnicmp(rule, "deny", 4) == 0)
1820 {
1821 rule += 4;
1822
1823 *ActionType = ACTION_DENY;
1824 }
1825 else if (_strnicmp(rule, "quietdeny", 9) == 0)
1826 {
1827 rule += 9;
1828
1829 *ActionType = ACTION_QUIETDENY;
1830 }
1831 else if (_strnicmp(rule, "log", 3) == 0)
1832 {
1833 rule += 3;
1834
1835 *ActionType = ACTION_LOG;
1836 }
1837 else if (_strnicmp(rule, "ask", 3) == 0)
1838 {
1839 rule += 3;
1840
1841 *ActionType = ACTION_ASK;
1842 }
1843 else
1844 {
1845 ABORT_PolicyParseRule(("Expecting to see 'permit', 'deny', 'quitedeny', 'log' or 'ask' clause. Got '%s'\n", rule));
1846 }
1847
1848
1849 SKIP_WHITESPACE(rule);
1850
1851
1852 /* EOL? */
1853 if (*rule == 0)
1854 {
1855 if (RuleNumber)
1856 *RuleNumber = 0;
1857
1858 return TRUE;
1859 }
1860
1861
1862 /* if it is not EOL then we expect to see "[rule DIGIT]" clause */
1863 if (_strnicmp(rule, "[rule", 5) == 0)
1864 {
1865 rule += 5;
1866 }
1867 else
1868 {
1869 ABORT_PolicyParseRule(("Expecting to see a rule clause. Got '%s'\n", rule));
1870 }
1871
1872
1873 if (! IS_WHITESPACE(*rule))
1874 {
1875 ABORT_PolicyParseRule(("Expecting to see white space. Got '%s'\n", rule));
1876 }
1877
1878 SKIP_WHITESPACE(rule);
1879
1880
1881 /* parse the rule number (a decimal digit) */
1882 while (*rule >= '0' && *rule <= '9')
1883 {
1884 /* don't overflow UCHAR values */
1885 if (++len > 2)
1886 {
1887 ABORT_PolicyParseRule(("The rule number is too long.\n"));
1888 }
1889
1890 num = num*10 + (*rule - '0');
1891
1892 ++rule;
1893 }
1894
1895
1896 SKIP_WHITESPACE(rule);
1897
1898
1899 if (*rule != ']')
1900 {
1901 ABORT_PolicyParseRule(("Invalid rule clause: '%s'\n", rule));
1902 }
1903
1904
1905 ++rule;
1906 SKIP_WHITESPACE(rule);
1907
1908
1909 /* expecting an EOL */
1910 if (*rule != 0)
1911 {
1912 ABORT_PolicyParseRule(("Expecting to see end of line. Got '%s'\n", rule));
1913 }
1914
1915
1916 if (RuleNumber)
1917 *RuleNumber = num;
1918
1919
1920 return TRUE;
1921}
1922
1923
1924
1925/*
1926 * PolicyParseOnOffClause()
1927 *
1928 * Description:
1929 * .
1930 *
1931 * Parameters:
1932 * .
1933 *
1934 * Returns:
1935 * .
1936 */
1937
1938BOOLEAN
1939PolicyParseOnOffClause(PCHAR rule, BOOLEAN *OnOff)
1940{
1941 SKIP_WHITESPACE(rule);
1942
1943
1944 if (_strnicmp(rule, "on", 2) == 0)
1945 {
1946 rule += 2;
1947
1948 *OnOff = TRUE;
1949 }
1950 else if (_strnicmp(rule, "off", 3) == 0)
1951 {
1952 rule += 3;
1953
1954 *OnOff = FALSE;
1955 }
1956 else
1957 {
1958 ABORT_PolicyParseRule(("Expecting to see 'on' or 'off' clause. Got '%s'\n", rule));
1959 }
1960
1961
1962 SKIP_WHITESPACE(rule);
1963
1964 /* expecting an EOL */
1965 if (*rule != 0)
1966 {
1967 ABORT_PolicyParseRule(("Expecting to see end of line. Got '%s'\n", rule));
1968 }
1969
1970 return TRUE;
1971}
1972
1973
1974
1975/*
1976 * VerifyToken2()
1977 *
1978 * Description:
1979 * .
1980 *
1981 * Parameters:
1982 * .
1983 *
1984 * Returns:
1985 * .
1986 */
1987
1988UCHAR
1989VerifyToken2(PCHAR *rule, PCHAR token1, PCHAR token2)
1990{
1991 USHORT Token1Length, Token2Length;
1992
1993
1994 ASSERT(rule && *rule);
1995 ASSERT(token1);
1996 ASSERT(token2);
1997
1998
1999 SKIP_WHITESPACE(*rule);
2000
2001
2002 Token1Length = (USHORT) strlen(token1);
2003 Token2Length = (USHORT) strlen(token2);
2004
2005 if (_strnicmp(*rule, token1, Token1Length) == 0)
2006 {
2007 *rule += Token1Length;
2008
2009 if (! IS_WHITESPACE(**rule))
2010 {
2011// LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Expecting to see whitespace. Got '%s'\n", *rule));
2012 return FALSE;
2013 }
2014
2015 SKIP_WHITESPACE(*rule);
2016
2017 return 1;
2018 }
2019
2020
2021 if (_strnicmp(*rule, token2, Token2Length) == 0)
2022 {
2023 *rule += Token2Length;
2024
2025 if (! IS_WHITESPACE(**rule))
2026 {
2027// LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Expecting to see whitespace. Got '%s'\n", *rule));
2028 return FALSE;
2029 }
2030
2031 SKIP_WHITESPACE(*rule);
2032
2033 return 2;
2034 }
2035
2036
2037 return 0;
2038}
2039
2040
2041
2042/*
2043 * PolicyParsePolicyRule()
2044 *
2045 * Description:
2046 * Parse a policy rule and adjust the policy default action.
2047 *
2048 * Parameters:
2049 * pSecPolicy - security policy that the rule is going to be added to.
2050 * Operation - ASCII operation (valid options are 'default' and 'path').
2051 * Rule - ASCII rule to parse.
2052 * Critical - Boolean indicating whether the parser should abort parsing the policy due to a critical error.
2053 *
2054 * Returns:
2055 * Nothing.
2056 */
2057
2058BOOLEAN
2059PolicyParsePolicyRule(OUT PSECURITY_POLICY pSecPolicy, IN PCHAR Operation, IN PCHAR rule, OUT BOOLEAN *Critical)
2060{
2061 ACTION_TYPE ActionType;
2062 UCHAR RuleNumber;
2063
2064
2065 *Critical = FALSE;
2066
2067
2068 if (strlen(Operation) == 7 && _stricmp(Operation, "default") == 0)
2069 {
2070 if (PolicyParseActionClause(rule, &ActionType, &RuleNumber) == FALSE)
2071
2072 ABORT_PolicyParseRule(("Invalid default policy action specification. Format: \"policy_default: (permit|deny|quietdeny|log|ask)\"\n"));
2073
2074
2075 /* did we initialize default policy action already? */
2076 if (pSecPolicy->DefaultPolicyAction != ACTION_NONE)
2077
2078 ABORT_PolicyParseRule(("Duplicate default policy action specification.\n"));
2079
2080
2081 /* this still allows "policy_default: permit [rule 0]", oh well */
2082 if (RuleNumber != 0)
2083
2084 ABORT_PolicyParseRule(("Rule clause cannot appear in default policy action specification.\n"));
2085
2086
2087 pSecPolicy->DefaultPolicyAction = ActionType | ACTION_DEFAULT;
2088
2089 return TRUE;
2090 }
2091
2092
2093 if (strlen(Operation) == 4 && _stricmp(Operation, "path") == 0)
2094 {
2095 CHAR szPath[MAX_PATH];
2096 CHAR szPolicyPath[MAX_PATH];
2097
2098
2099 _snprintf(szPath, MAX_PATH, "%S", gFilePath);
2100 szPath[MAX_PATH - 1] = 0;
2101
2102
2103 rule = StripFileMacros(rule, szPolicyPath, MAX_PATH);
2104
2105
2106 KdPrint(("%s\n%s\n", szPath, rule));
2107
2108 if (WildcardMatch(szPath, rule) == WILDCARD_MATCH)
2109 {
2110 KdPrint(("paths match\n"));
2111 return TRUE;
2112 }
2113
2114
2115 if (LearningMode == FALSE)
2116 *Critical = TRUE;
2117
2118 KdPrint(("paths do not match\n"));
2119
2120
2121 return FALSE;
2122 }
2123
2124
2125 ABORT_PolicyParseRule(("Invalid policy operation '%s'. Valid options are 'default' and 'path'.\n", Operation));
2126}
2127
2128
2129
2130/*
2131 * PolicyParseProtectionRule()
2132 *
2133 * Description:
2134 * Parse a protection rule and adjust the protection options such as buffer overflow protection
2135 * and userland (dll injection) protection being on and off
2136 *
2137 * Parameters:
2138 * pSecPolicy - security policy that the rule is going to be added to.
2139 * Operation - ASCII operation. Valid options are 'overflow', 'userland', 'debugging', 'dos16', 'keyboard', 'modem', 'sniffer', 'extension' and 'all'.
2140 * Rule - ASCII rule to parse.
2141 *
2142 * Returns:
2143 * Nothing.
2144 */
2145
2146BOOLEAN
2147PolicyParseProtectionRule(PSECURITY_POLICY pSecPolicy, PCHAR Operation, PCHAR rule)
2148{
2149 BOOLEAN OnOff;
2150
2151
2152 if (strlen(Operation) == 8 && _stricmp(Operation, "overflow") == 0)
2153 {
2154 if (PolicyParseOnOffClause(rule, &OnOff) == FALSE)
2155 return FALSE;
2156
2157 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning overflow protection %s\n", OnOff ? "on" : "off"));
2158
2159 if (OnOff)
2160 pSecPolicy->ProtectionFlags |= PROTECTION_OVERFLOW;
2161 else
2162 pSecPolicy->ProtectionFlags &= ~PROTECTION_OVERFLOW;
2163
2164 return TRUE;
2165 }
2166
2167
2168 if (strlen(Operation) == 8 && _stricmp(Operation, "userland") == 0)
2169 {
2170 if (PolicyParseOnOffClause(rule, &OnOff) == FALSE)
2171 return FALSE;
2172
2173 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning userland protection %s\n", OnOff ? "on" : "off"));
2174
2175 if (OnOff)
2176 pSecPolicy->ProtectionFlags |= PROTECTION_USERLAND;
2177 else
2178 pSecPolicy->ProtectionFlags &= ~PROTECTION_USERLAND;
2179
2180 return TRUE;
2181 }
2182
2183
2184 if (strlen(Operation) == 9 && _stricmp(Operation, "debugging") == 0)
2185 {
2186 if (PolicyParseOnOffClause(rule, &OnOff) == FALSE)
2187 return FALSE;
2188
2189 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning debugging protection %s\n", OnOff ? "on" : "off"));
2190
2191 if (OnOff)
2192 pSecPolicy->ProtectionFlags |= PROTECTION_DEBUGGING;
2193 else
2194 pSecPolicy->ProtectionFlags &= ~PROTECTION_DEBUGGING;
2195
2196 return TRUE;
2197 }
2198
2199
2200 if (strlen(Operation) == 5 && _stricmp(Operation, "dos16") == 0)
2201 {
2202 if (PolicyParseOnOffClause(rule, &OnOff) == FALSE)
2203 return FALSE;
2204
2205 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning dos16/vdm protection %s\n", OnOff ? "on" : "off"));
2206
2207 if (OnOff)
2208 pSecPolicy->ProtectionFlags |= PROTECTION_VDM;
2209 else
2210 pSecPolicy->ProtectionFlags &= ~PROTECTION_VDM;
2211
2212 return TRUE;
2213 }
2214
2215
2216 if (strlen(Operation) == 8 && _stricmp(Operation, "keyboard") == 0)
2217 {
2218 if (PolicyParseOnOffClause(rule, &OnOff) == FALSE)
2219 return FALSE;
2220
2221 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning keyboard logger protection %s\n", OnOff ? "on" : "off"));
2222
2223 if (OnOff)
2224 pSecPolicy->ProtectionFlags |= PROTECTION_KEYBOARD;
2225 else
2226 pSecPolicy->ProtectionFlags &= ~PROTECTION_KEYBOARD;
2227
2228 return TRUE;
2229 }
2230
2231
2232 if (strlen(Operation) == 5 && _stricmp(Operation, "modem") == 0)
2233 {
2234 if (PolicyParseOnOffClause(rule, &OnOff) == FALSE)
2235 return FALSE;
2236
2237 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning modem protection %s\n", OnOff ? "on" : "off"));
2238
2239 if (OnOff)
2240 pSecPolicy->ProtectionFlags |= PROTECTION_MODEM;
2241 else
2242 pSecPolicy->ProtectionFlags &= ~PROTECTION_MODEM;
2243
2244 return TRUE;
2245 }
2246
2247
2248 if (strlen(Operation) == 7 && _stricmp(Operation, "sniffer") == 0)
2249 {
2250 if (PolicyParseOnOffClause(rule, &OnOff) == FALSE)
2251 return FALSE;
2252
2253 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning sniffer protection %s\n", OnOff ? "on" : "off"));
2254
2255 if (OnOff)
2256 pSecPolicy->ProtectionFlags |= PROTECTION_SNIFFER;
2257 else
2258 pSecPolicy->ProtectionFlags &= ~PROTECTION_SNIFFER;
2259
2260 return TRUE;
2261 }
2262
2263
2264 if (strlen(Operation) == 9 && _stricmp(Operation, "extension") == 0)
2265 {
2266 if (PolicyParseOnOffClause(rule, &OnOff) == FALSE)
2267 return FALSE;
2268
2269 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning extension protection %s\n", OnOff ? "on" : "off"));
2270
2271 if (OnOff)
2272 pSecPolicy->ProtectionFlags |= PROTECTION_EXTENSION;
2273 else
2274 pSecPolicy->ProtectionFlags &= ~PROTECTION_EXTENSION;
2275
2276 return TRUE;
2277 }
2278
2279
2280 if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0)
2281 {
2282 if (PolicyParseOnOffClause(rule, &OnOff) == FALSE)
2283 return FALSE;
2284
2285 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning all protection %s\n", OnOff ? "on" : "off"));
2286
2287 pSecPolicy->ProtectionFlags = OnOff == TRUE ? PROTECTION_ALL_ON : PROTECTION_ALL_OFF;
2288
2289 return TRUE;
2290 }
2291
2292
2293 ABORT_PolicyParseRule(("Invalid protection operation '%s'. Valid options are 'overflow', 'userland', 'debugging', 'dos16', 'keyboard', 'modem', 'sniffer', 'extension' and 'all'.\n", Operation));
2294}
2295
2296
2297
2298/*
2299 * PolicyParseMediaRule()
2300 *
2301 * Description:
2302 * Parse a media rule.
2303 *
2304 * Parameters:
2305 * pSecPolicy - security policy that the rule is going to be added to.
2306 * Operation - ASCII operation. The only valid option is 'access'.
2307 * Rule - ASCII rule to parse.
2308 *
2309 * Returns:
2310 * Nothing.
2311 */
2312
2313BOOLEAN
2314PolicyParseMediaRule(PSECURITY_POLICY pSecPolicy, PCHAR Operation, PCHAR rule)
2315{
2316 if (pSecPolicy != &gSecPolicy)
2317 {
2318 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("PolicyParseMediaRule: Media rules can be setup only in a global policy\n"));
2319 return TRUE;
2320 }
2321
2322
2323 if (strlen(Operation) != 6 || _stricmp(Operation, "access") != 0)
2324 ABORT_PolicyParseRule(("Invalid media operation '%s'. The only valid option is 'access'.\n", Operation));
2325
2326
2327 if (strlen(rule) == 6 && _stricmp(rule, "permit") == 0)
2328 {
2329 MediaRemovableFlags = MEDIA_REMOVABLE_PERMIT;
2330 return TRUE;
2331 }
2332
2333 if (strlen(rule) == 4 && _stricmp(rule, "deny") == 0)
2334 {
2335 MediaRemovableFlags |= MEDIA_REMOVABLE_DISABLE;
2336 return TRUE;
2337 }
2338
2339 if (strlen(rule) == 8 && _stricmp(rule, "readonly") == 0)
2340 {
2341 MediaRemovableFlags |= MEDIA_REMOVABLE_READONLY;
2342 return TRUE;
2343 }
2344
2345 if (strlen(rule) == 9 && _stricmp(rule, "noexecute") == 0)
2346 {
2347 MediaRemovableFlags |= MEDIA_REMOVABLE_NOEXECUTE;
2348 return TRUE;
2349 }
2350
2351
2352 ABORT_PolicyParseRule(("Expecting to see 'permit', 'deny', 'readonly', or 'noexecute' action. Got '%s'\n", rule));
2353}
2354
2355
2356
2357/*
2358 * InsertPolicyRule()
2359 *
2360 * Description:
2361 * Adds a rule to a specified security policy (FIFO order).
2362 *
2363 * Parameters:
2364 * pSecPolicy - security policy that the rule is going to be added to.
2365 * PolicyRule - rule to add.
2366 * RuleType - rule type (file, network, etc).
2367 *
2368 * Returns:
2369 * Nothing.
2370 */
2371
2372VOID
2373InsertPolicyRule(PSECURITY_POLICY pSecPolicy, PPOLICY_RULE PolicyRule, RULE_TYPE RuleType)
2374{
2375 KIRQL irql;
2376 PPOLICY_RULE tmp;
2377
2378
2379 ASSERT(RuleType < RULE_LASTONE);
2380
2381
2382 KeAcquireSpinLock(&pSecPolicy->SpinLock, &irql);
2383
2384
2385 if (pSecPolicy->RuleList[RuleType] == NULL)
2386 {
2387 pSecPolicy->RuleList[RuleType] = PolicyRule;
2388
2389 KeReleaseSpinLock(&pSecPolicy->SpinLock, irql);
2390
2391 return;
2392 }
2393
2394 /* find the last rule and link the new rule to it */
2395 tmp = pSecPolicy->RuleList[RuleType];
2396
2397 while (tmp->Next)
2398 {
2399 tmp = tmp->Next;
2400 }
2401
2402 tmp->Next = PolicyRule;
2403
2404
2405 KeReleaseSpinLock(&pSecPolicy->SpinLock, irql);
2406}
2407
2408
2409
2410/*
2411 * PolicyParseObjectRule()
2412 *
2413 * Description:
2414 * Parse an object rule of the following format:
2415 * (name|address) (eq|match) "<objectname>" then (deny|quitedeny|permit|log)
2416 * Rule can also consist of just (deny|quitedeny|permit|log)
2417 *
2418 * example1: name match "c:\file*" then deny
2419 * example2: address eq "192.168.0.1" then log
2420 * example3: quietdeny
2421 *
2422 * Parameters:
2423 * pSecPolicy - security policy that the rule is going to be added to.
2424 * RuleType - rule type (file, network, etc).
2425 * Operation - ASCII operation (read, write, etc).
2426 * Rule - ASCII rule to parse. NOTE: this field gets clobbered.
2427 *
2428 * Returns:
2429 * Nothing.
2430 */
2431
2432BOOLEAN
2433PolicyParseObjectRule(PSECURITY_POLICY pSecPolicy, RULE_TYPE RuleType, PCHAR Operation, PCHAR rule)
2434{
2435 PCHAR name = NULL;
2436 PCHAR OriginalRule = rule;
2437 int i, TotalStars;
2438 BOOLEAN wildcard, WildcardWarning = FALSE;
2439 BOOLEAN ParseLastToken = FALSE;
2440 ACTION_TYPE ActionType;
2441 MATCH_TYPE MatchType;
2442 PPOLICY_RULE PolicyRule;
2443 UCHAR OperationType;
2444 PCHAR ObjectName = NULL;
2445 size_t ObjectSize = 0;
2446 UCHAR RuleNumber;
2447
2448
2449 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseObjectRule: Parsing rule '%s': '%s'\n", Operation, rule));
2450
2451
2452 /*
2453 * First token can be "name" or "address" for network rules
2454 * Alternatively, the entire rule can consist of an action clause (such as permit, deny, quietdeny or log)
2455 */
2456
2457 switch (VerifyToken2(&rule, "name", "address"))
2458 {
2459 /* matched token1 - "name" */
2460 case 1: break;
2461
2462 /* matched token2 - "address" */
2463 case 2:
2464 {
2465 /* only network rules can substitute "address" for "name" */
2466
2467 if (RuleType != RULE_NETWORK)
2468
2469 ABORT_PolicyParseRule(("Expecting to see 'name'. Got 'address'\n"));
2470
2471 break;
2472 }
2473
2474 /* didn't match "name" or "address". try to parse as an action clause */
2475 default:
2476 {
2477 ParseLastToken = TRUE;
2478 goto ParseLastToken;
2479 }
2480 }
2481
2482
2483 /*
2484 * Second token should be "eq" or "match"
2485 */
2486
2487 switch (VerifyToken2(&rule, "eq", "match"))
2488 {
2489 /* matched token1 - "eq" */
2490 case 1: wildcard = FALSE; break;
2491
2492 /* matched token2 - "match" */
2493 case 2: wildcard = TRUE; break;
2494
2495 /* didn't match "eq" or "match" */
2496 default: ABORT_PolicyParseRule(("Expecting to see 'eq' or 'match'. Got '%s'\n", rule));
2497 }
2498
2499
2500 /*
2501 * Third token is the object names in quotes
2502 */
2503
2504 /* parse the object name surrounded by quotes: "<object name>" */
2505
2506 if (*rule++ != '"')
2507 ABORT_PolicyParseRule(("Initial quote character not found. Object names should be surrounded by quotes.\n"));
2508
2509
2510 name = rule;
2511
2512 TotalStars = i = 0;
2513
2514
2515 while (*rule != 0 && *rule != '"')
2516 {
2517 if (i >= POLICY_MAX_OBJECT_NAME_LENGTH-1)
2518 ABORT_PolicyParseRule(("Object name '%s' is too long.\nMaximum name length is %d characters.\n", rule - POLICY_MAX_OBJECT_NAME_LENGTH, POLICY_MAX_OBJECT_NAME_LENGTH));
2519
2520 // fail bad regexes
2521 if (*rule == '*')
2522 {
2523 if (++TotalStars > POLICY_TOTAL_NUMBER_OF_STARS)
2524 ABORT_PolicyParseRule(("Invalid regular expression. Maximum of %d stars are allowed\n", POLICY_TOTAL_NUMBER_OF_STARS));
2525 }
2526
2527
2528 if (wildcard == FALSE && (*rule == '*' || *rule == '?') && WildcardWarning == FALSE)
2529 {
2530 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("%S:%d:\n", gPolicyFilename, gPolicyLineNumber));
2531 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Found a regular expression with an 'eq' operator. Use 'match' operator to enable regular expressions.\n"));
2532 WildcardWarning = TRUE;
2533 }
2534
2535
2536 ++i;
2537 ++rule;
2538 }
2539
2540
2541 if (i == 0 || *rule++ != '"')
2542 ABORT_PolicyParseRule(("Final quote character not found. Object names should be surrounded by quotes.\n"));
2543
2544 name[i] = 0;
2545
2546
2547 if (! IS_WHITESPACE(*rule))
2548 {
2549 ABORT_PolicyParseRule(("Expecting to see white space. Got '%s'\n", rule));
2550 }
2551
2552 SKIP_WHITESPACE(rule);
2553
2554
2555 /*
2556 * Fourth token is "then"
2557 */
2558
2559 if (VerifyToken2(&rule, "then", "") != 1)
2560 ABORT_PolicyParseRule(("Expecting to see 'then'. Got '%s'\n", rule));
2561
2562 /*
2563 * Fifth/Last token is "permit", "deny", "quietdeny", "log" or "ask"
2564 */
2565
2566ParseLastToken:
2567
2568 if (PolicyParseActionClause(rule, &ActionType, &RuleNumber) == FALSE)
2569
2570 return FALSE;
2571
2572
2573 /*
2574 * Rule parsed ok. Create a new rule.
2575 */
2576
2577 if (RuleType > sizeof(ObjectParseOps) / sizeof(ObjectParseOps[0]))
2578 ABORT_PolicyParseRule(("Invalid rule type\n"));
2579
2580
2581 if (name)
2582 {
2583 if ((ObjectSize = ObjectParseOps[RuleType].ObjectNameParser(name, &ObjectName, &wildcard)) == INVALID_OBJECT_SIZE)
2584 ABORT_PolicyParseRule(("Invalid object name '%s'\n", name));
2585
2586 MatchType = (wildcard == TRUE ? MATCH_WILDCARD : MATCH_SINGLE);
2587 }
2588 else
2589 {
2590 ObjectSize = 0;
2591 MatchType = MATCH_ALL;
2592 }
2593
2594
2595 if ((OperationType = ObjectParseOps[RuleType].OperationTypeParser(Operation)) == OP_INVALID)
2596 ABORT_PolicyParseRule(("Invalid operation '%s'\n", Operation));
2597
2598
2599 /* POLICY_RULE already includes 1 character for the name buffer */
2600
2601 PolicyRule = ExAllocatePoolWithTag(NonPagedPool, sizeof(POLICY_RULE) + ObjectSize, _POOL_TAG);
2602 if (PolicyRule == NULL)
2603 {
2604 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Object policy parser is out of memory\n"));
2605 return FALSE;
2606 }
2607
2608 RtlZeroMemory(PolicyRule, sizeof(POLICY_RULE));
2609
2610 PolicyRule->ActionType = ActionType;
2611 PolicyRule->MatchType = MatchType;
2612 PolicyRule->OperationType = OperationType;
2613
2614 PolicyRule->RuleNumber = RuleNumber;
2615 PolicyRule->PolicyLineNumber = gPolicyLineNumber;
2616 PolicyRule->pSecurityPolicy = pSecPolicy;
2617
2618
2619 /* ObjectSize can be 0 for MATCH_ALL rules */
2620 if (ObjectSize)
2621 {
2622 PolicyRule->NameLength = (USHORT) ObjectSize;
2623 strcpy(PolicyRule->Name, (PCHAR) ObjectName);
2624 }
2625
2626
2627 /*
2628 * Some rules (such as DLL) are actually enforced by different rules (i.e. section).
2629 * If LearningMode = TRUE then there is no need to convert
2630 */
2631
2632 if (LearningMode == FALSE)
2633 {
2634 /* DLL rules are enforced by section rules. */
2635 if (RuleType == RULE_DLL)
2636 RuleType = RULE_SECTION;
2637
2638 /* Service rules are enforced by registry rules. */
2639 if (RuleType == RULE_SERVICE)
2640 RuleType = RULE_REGISTRY;
2641 }
2642
2643
2644 InsertPolicyRule(pSecPolicy, PolicyRule, RuleType);
2645
2646
2647 return TRUE;
2648}
2649
2650
2651
2652/*
2653 * PolicyParseSyscallRule()
2654 *
2655 * Description:
2656 * .
2657 *
2658 * Parameters:
2659 * .
2660 *
2661 * Returns:
2662 * Nothing.
2663 */
2664
2665BOOLEAN
2666PolicyParseSyscallRule(PSECURITY_POLICY pSecPolicy, PCHAR SyscallName, PCHAR rule)
2667{
2668 PPOLICY_RULE PolicyRule;
2669 KIRQL irql;
2670 ACTION_TYPE ActionType;
2671 ULONG SyscallNameIndex;
2672 BOOLEAN AcceptAll = FALSE;
2673
2674
2675#if HOOK_SYSCALLS
2676
2677 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseSyscallRule: Parsing syscall '%s' rule: '%s'\n", SyscallName, rule));
2678
2679
2680 /* expecting to see "permit", "deny", "quietdeny" or "log" */
2681 if (PolicyParseActionClause(rule, &ActionType, NULL) == FALSE)
2682 {
2683// LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_DEBUG, ("PolicyParseSyscallRule: PolicyParseActionClause failed\n"));
2684 return FALSE;
2685 }
2686
2687#if 0
2688 /*
2689 * all the special system calls such as OpenFile and CreateProcess have already been hooked, it should
2690 * be safe to hook everything else. If a special system call is specified then HookSystemServiceByName
2691 * will just silently fail since it's already hooked.
2692 */
2693
2694 if (HookSystemServiceByName(SyscallName, NULL/*USE_DEFAULT_HOOK_FUNCTION*/) == FALSE)
2695 {
2696 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Unknown syscall '%s'\n", SyscallName));
2697 return FALSE;
2698 }
2699#endif
2700
2701
2702 if (strlen(SyscallName) == 3 && _stricmp(SyscallName, "all") == 0)
2703 {
2704 AcceptAll = TRUE;
2705 }
2706 else
2707 {
2708 SyscallNameIndex = FindSystemServiceNumber(SyscallName);
2709 if (SyscallNameIndex == -1)
2710 {
2711 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_DEBUG, ("PolicyParseSyscallRule: Syscall name '%s' not found\n", SyscallName));
2712 return FALSE;
2713 }
2714 }
2715
2716
2717 /* allocate enough memory to hold a bit array for all existing system calls */
2718
2719 if (pSecPolicy->RuleList[ RULE_SYSCALL ] == NULL)
2720 {
2721 /*
2722 * Take into account 1 ULONG that is already preallocated in POLICY_RULE.
2723 */
2724
2725 USHORT Size = 1 + (USHORT) (ZwCallsNumber - NumberOfBitsInUlong) / 8;
2726
2727
2728 PolicyRule = ExAllocatePoolWithTag(NonPagedPool, sizeof(POLICY_RULE) + Size, _POOL_TAG);
2729 if (rule == NULL)
2730 {
2731 LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Policy parser is out of memory\n"));
2732 return FALSE;
2733 }
2734
2735 RtlZeroMemory(PolicyRule, sizeof(POLICY_RULE) + Size);
2736
2737
2738 /* if default action is permit, then fill the entire bit array with 1's */
2739 if (pSecPolicy->DefaultPolicyAction == ACTION_PERMIT)
2740 {
2741 RtlFillMemory(&PolicyRule->ServiceBitArray, sizeof(PolicyRule->ServiceBitArray) + Size, 0xFF);
2742 }
2743
2744
2745 KeAcquireSpinLock(&pSecPolicy->SpinLock, &irql);
2746
2747 pSecPolicy->RuleList[ RULE_SYSCALL ] = PolicyRule;
2748
2749 KeReleaseSpinLock(&pSecPolicy->SpinLock, irql);
2750 }
2751 else
2752 {
2753 PolicyRule = pSecPolicy->RuleList[ RULE_SYSCALL ];
2754 }
2755
2756
2757 // syscall_all: permit
2758 // syscall_all: deny
2759
2760 // syscall_blah: permit
2761 // syscall_all: deny
2762
2763 if (AcceptAll == TRUE)
2764 ;//XXX
2765
2766 {
2767 /* Calculate the bit array ULONG we need to modify (bit array consists of a bunch of ULONGs) */
2768 ULONG UlongCount = SyscallNameIndex >> UlongBitShift;
2769
2770 /* Choose the correct bit array ULONG */
2771 PULONG BitArray = &PolicyRule->ServiceBitArray[0] + UlongCount;
2772
2773
2774 //XXX what about log?
2775 if (ActionType == ACTION_PERMIT)
2776 {
2777 /* set the bit */
2778 *BitArray |= 1 << (SyscallNameIndex - (UlongCount << UlongBitShift));
2779 }
2780 else if (ActionType >= ACTION_DENY)
2781 {
2782 /* reset the bit */
2783 *BitArray &= ~( 1 << (SyscallNameIndex - (UlongCount << UlongBitShift)) );
2784 }
2785 }
2786
2787#endif
2788
2789
2790 return TRUE;
2791}
2792
2793
2794
2795/*
2796 * WildcardMatch()
2797 *
2798 * Description:
2799 * Simple regex algorithm. Supports '?' for a single character match (does not match EOF) and
2800 * '*' for multiple characters (must reside in the same directory).
2801 *
2802 * i.e. c:\temp\*\blah will match c:\temp\temp2\blah but not c:\temp\temp2\temp3\blah
2803 *
2804 * NOTE: WildcardMatch() is at least 10x slower than simple _stricmp().
2805 *
2806 * Parameters:
2807 * path - ASCII pathname.
2808 * regex - regular expression to match
2809 *
2810 * Returns:
2811 * WILDCARD_MATCH (0) in case of a match, WILDCARD_NO_MATCH (1) otherwise.
2812 */
2813
2814int
2815WildcardMatch(PCHAR path, PCHAR regex)
2816{
2817 BOOLEAN MultipleDirectoryMatch = FALSE, SkippedOverWhitespace = FALSE, ShortFileName = FALSE;
2818
2819
2820 if (path == NULL || regex == NULL)
2821 return WILDCARD_NO_MATCH;
2822
2823
2824 while (*regex)
2825 {
2826 /*
2827 * SPECIAL CASE.
2828 * Try to deal with short names (longna~1.txt vs long name.txt).
2829 * when we encounter a ~X where X is a digit we skip over it and rewind regex
2830 * to either the next '\' or whatever the next path character is.
2831 *
2832 * The reason for this is when we encounter a long directory it will be abbreviated as follows
2833 * c:\Documents and Settings\... -> c:\DOCUME~1\...
2834 * Thus by matching "DOCUME" and then skipping over ~1 in the pathname and by rewinding regex
2835 * until the next '\' we match the two directories.
2836 *
2837 * The long filenames are matched and abbreviated as follows
2838 * c:\longfilename.txt -> c:\longfi~1.txt
2839 * Thus we match "longfi", skip over ~1 in the pathname and rewind regex until we match
2840 * the next path character which is '.', then we match ".txt"
2841 *
2842 * The following scheme was mostly designed to deal with "c:\documents and settings" and
2843 * "c:\program files" directories since they are two very common long names. It breaks
2844 * in a lot of different cases. Consider the following abbreviation list
2845 *
2846 * LONGNA~1 long name1
2847 * LONGNA~2 long name2
2848 * LONGNA~3 long name3
2849 * LONGNA~4 long name4
2850 * LOA926~1 long name5
2851 * LOB926~1 long name6
2852 *
2853 * When more than four names match to the same short name prefix, Windows switches to an
2854 * alternative hash based schemes which is not handled by our code.
2855 *
2856 * Another restriction (there are probably more) has to do with long extensions which are abbreviated
2857 * as follows: file.html.text -> filete~1.htm. The following code does not handle this case either.
2858 *
2859 * All in all, the following code is an ugly hack that is designed to work for most common cases
2860 * with a least amount of effort.
2861 */
2862
2863 if (*path == '~' && isdigit(*(path + 1)))
2864 {
2865 /* rewind regex until we see '\' or whichever character (ie '.' as in ~1.txt) follows ~X in the path */
2866 while (*regex && *regex != '\\' && *regex != *(path + 2) /*&& *regex != '*' && *regex != '?'*/)
2867 ++regex;
2868
2869 /* skip over ~X */
2870 path += 2;
2871
2872 ShortFileName = TRUE;
2873 }
2874
2875
2876 if (*regex == '*')
2877 {
2878 PCHAR str;
2879
2880 /*
2881 * match one or more characters
2882 */
2883
2884 /* if regular expression ends with '*', automatically match the rest of the pathname */
2885 if (*(regex + 1) == 0)
2886 return WILDCARD_MATCH;
2887
2888 if (*(regex + 1) == '*')
2889 {
2890 ++regex;
2891 MultipleDirectoryMatch = TRUE;
2892 }
2893
2894 str = path;
2895 while (*str)
2896 {
2897 if (WildcardMatch(str, regex+1) == WILDCARD_MATCH)
2898 return WILDCARD_MATCH;
2899
2900 if (MultipleDirectoryMatch == FALSE && *str == '\\')
2901 break;
2902
2903 ++str;
2904 }
2905 }
2906 else if (*regex == '?')
2907 {
2908 /*
2909 * match one character
2910 */
2911
2912 if (*path == 0 || *path == '\\')
2913 return WILDCARD_NO_MATCH;
2914
2915 ++path;
2916 }
2917 else if (*regex == '\\')
2918 {
2919 /* if we skipped over whitespace but did not match a short name then bail out as not skipping over
2920 whitespace would not get us this far anyway */
2921 if (SkippedOverWhitespace == TRUE && ShortFileName == FALSE)
2922 return WILDCARD_NO_MATCH;
2923
2924 ShortFileName = FALSE;
2925 SkippedOverWhitespace = FALSE;
2926
2927 /*
2928 * match one or more slashes
2929 */
2930
2931 if (*path++ != '\\')
2932 return WILDCARD_NO_MATCH;
2933
2934 while (*path == '\\')
2935 ++path;
2936 }
2937 else
2938 {
2939 /*
2940 * match all other characters
2941 */
2942
2943// if (toupper(*path++) != toupper(*regex))
2944// return WILDCARD_NO_MATCH;
2945
2946 if (toupper(*path) != toupper(*regex))
2947 {
2948 /*
2949 * Skip over whitespace to match long filenames which are abbreviated with all
2950 * whitespace stripped. In order not to match two filenames with different amount
2951 * of whitespace, we insert an additional check when we reach '\' once we found out
2952 * whether we are working with an abbreviated name.
2953 */
2954 if (*regex == ' ')
2955 SkippedOverWhitespace = TRUE;
2956 else
2957 return WILDCARD_NO_MATCH;
2958 }
2959 else
2960 {
2961 ++path;
2962 }
2963 }
2964
2965 ++regex;
2966 }
2967
2968
2969 /* make sure pathname is not longer than the regex */
2970 return *path == 0 ? WILDCARD_MATCH : WILDCARD_NO_MATCH;
2971}
2972
2973
2974
2975/*
2976 * PolicyCheckPolicy()
2977 *
2978 * Description:
2979 * .
2980 *
2981 * Parameters:
2982 * .
2983 *
2984 * Returns:
2985 * .
2986 */
2987
2988ACTION_TYPE
2989PolicyCheckPolicy(PSECURITY_POLICY pSecPolicy, RULE_TYPE RuleType, PCHAR Object, UCHAR OperationType, ACTION_TYPE DefaultAction, UCHAR *RuleNumber, PWSTR *PolicyFilename, USHORT *PolicyLineNumber)
2990{
2991 PPOLICY_RULE r;
2992 ACTION_TYPE ret = DefaultAction;
2993 KIRQL irql;
2994 size_t len;
2995
2996
2997 ASSERT(pSecPolicy);
2998 ASSERT(PolicyFilename && PolicyLineNumber);
2999 ASSERT(RuleNumber);
3000
3001
3002 if (Object)
3003 len = strlen(Object);
3004
3005 *PolicyFilename = NULL;
3006 *PolicyLineNumber = 0;
3007 *RuleNumber = 0;
3008
3009
3010 if (pSecPolicy->Initialized == FALSE)
3011
3012 return ACTION_NONE;
3013
3014
3015 KeAcquireSpinLock(&pSecPolicy->SpinLock, &irql);
3016
3017
3018 *PolicyFilename = pSecPolicy->Name;
3019
3020
3021 r = pSecPolicy->RuleList[RuleType];
3022
3023 while (r)
3024 {
3025//XXX at least for registry keys there is no point in comparing the first 10 characters since they will always be \Registry\ ?
3026
3027 if ( (r->MatchType == MATCH_ALL) ||
3028 (r->MatchType == MATCH_SINGLE && len == r->NameLength && _stricmp(Object, r->Name) == 0) ||
3029 (r->MatchType == MATCH_WILDCARD && WildcardMatch(Object, r->Name) == WILDCARD_MATCH) )
3030 {
3031 if (Object)
3032 LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("%d PolicyCheckPolicy: matched '%s' (%s) (%x %x %x)\n", CURRENT_PROCESS_PID, Object, r->Name, r->MatchType, r->OperationType, OperationType));
3033 else
3034 LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("%d PolicyCheckPolicy: matched RuleType %d MatchType %d OpType %d\n", CURRENT_PROCESS_PID, RuleType, r->MatchType, r->OperationType));
3035
3036
3037// if (r->OperationType == OP_APPEND)
3038// {
3039// ret = ACTION_PROCESS;
3040// break;
3041// }
3042
3043
3044 /*
3045 * (r->OperationType & OperationType) == r->OperationType
3046 * (r->OperationType & OperationType)
3047 *
3048 * policy_default: deny
3049 * file_read: file.txt
3050 *
3051 * del file.txt (opens file for read + write)
3052 *
3053 * (read & read_write) = read
3054 *
3055 * successfully overwrites the file!
3056 */
3057
3058 /*
3059 * (r->OperationType & OperationType) == OperationType
3060 *
3061 * policy_default: permit
3062 * file_write: file.txt deny
3063 * file_read: file.txt
3064 *
3065 * del file.txt (opens file for read + write)
3066 *
3067 * (write & read_write) != read_write
3068 *
3069 * successfully deletes the file!
3070 */
3071
3072 // XXX if we only allow reading but both read+execute are requested, the following
3073 // if will match! (bad in deny all scenario!)
3074 if (r->OperationType == OP_ALL || r->OperationType & OperationType)
3075// if (r->OperationType == OP_ALL || (r->OperationType & OperationType) == r->OperationType)
3076// if (r->OperationType == OP_ALL || (r->OperationType & OperationType) == OperationType)
3077 {
3078 ret = r->ActionType;
3079
3080
3081 /* remember which rule caused this alert */
3082 *PolicyLineNumber = r->PolicyLineNumber;
3083 *RuleNumber = r->RuleNumber;
3084
3085
3086 LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("%d PolicyCheckPolicy: %s access to %d\n",
3087 CURRENT_PROCESS_PID, ret >= ACTION_DENY ? "deny" : "permit",
3088 (ULONG) PsGetCurrentProcessId()));
3089
3090 break;
3091 }
3092 }
3093
3094 r = (PPOLICY_RULE) r->Next;
3095 }
3096
3097 KeReleaseSpinLock(&pSecPolicy->SpinLock, irql);
3098
3099
3100 return ret;
3101}
3102
3103
3104
3105/*
3106 * PolicyCheck()
3107 *
3108 * Description:
3109 * Verifies whether a specified action (rule (RuleType) + object (arg) ) are allowed
3110 * by a global and current process security policies.
3111 *
3112 * Parameters:
3113 * RuleType - rule type (file rule, registry, etc).
3114 * Object - Object name.
3115 * OperationType - type of operation carried out (read, write, etc).
3116 * RuleNumber - number of the rule that triggered the alert (if it did)
3117 * PolicyFilename - name of the policy where the rule, that triggered the alert, was specified
3118 * PolicyLineNumber - policy line where the rule, that triggered the alert, was specified
3119 *
3120 * Returns:
3121 * ACCESS_NONE, ACCESS_PERMIT, ACCESS_DENY or ACCESS_QUIETDENY depending on a security policy.
3122 */
3123
3124ACTION_TYPE
3125PolicyCheck(RULE_TYPE RuleType, PCHAR Object, UCHAR OperationType, UCHAR *RuleNumber, PWSTR *PolicyFilename, USHORT *PolicyLineNumber)
3126{
3127 PIMAGE_PID_ENTRY p, prev;
3128 PWSTR GlobalPolicyFilename, ProcessPolicyFilename;
3129 USHORT GlobalPolicyLineNumber, ProcessPolicyLineNumber;
3130 UCHAR GlobalRuleNumber, ProcessRuleNumber;
3131 ACTION_TYPE GlobalAction, ProcessAction = ACTION_PERMIT_DEFAULT, ReturnAction;
3132 ULONG ProcessId = CURRENT_PROCESS_PID;
3133
3134
3135 /* don't mess with kernel initiated calls */
3136
3137 if (KeGetPreviousMode() != UserMode || KeGetCurrentIrql() != PASSIVE_LEVEL)
3138
3139 return ACTION_PERMIT;
3140
3141
3142 /*
3143 * As most of system calls PolicyCheck() this is a convinient place to verify
3144 * user return address since it needs to be done for all calls
3145 */
3146
3147 VerifyUserReturnAddress();
3148
3149
3150 /*
3151 * First verify against the global security policy
3152 */
3153
3154 GlobalAction = PolicyCheckPolicy(&gSecPolicy, RuleType, Object, OperationType, gSecPolicy.DefaultPolicyAction, &GlobalRuleNumber, &GlobalPolicyFilename, &GlobalPolicyLineNumber);
3155
3156 /*
3157 * Then against process specific policy
3158 */
3159
3160 /* find the process specific policy */
3161
3162 p = FindImagePidEntry(ProcessId, 0);
3163
3164 if (p)
3165 {
3166 ProcessAction = PolicyCheckPolicy(&p->SecPolicy, RuleType, Object, OperationType, p->SecPolicy.DefaultPolicyAction, &ProcessRuleNumber, &ProcessPolicyFilename, &ProcessPolicyLineNumber);
3167 }
3168
3169
3170// KdPrint(("object %s %d %d action %x %x", Object, RuleType, OperationType, GlobalAction, ProcessAction));
3171
3172 /*
3173 * return the most stringent possible action
3174 */
3175
3176 /* Exception #1: explicit process permit/log overrides general global deny */
3177 if ((ProcessAction == ACTION_PERMIT || ProcessAction == ACTION_LOG) &&
3178 (GlobalAction & (ACTION_ASK | ACTION_DENY)))
3179 {
3180 *PolicyFilename = ProcessPolicyFilename;
3181 *PolicyLineNumber = ProcessPolicyLineNumber;
3182 *RuleNumber = ProcessRuleNumber;
3183 return ProcessAction;
3184 }
3185
3186 /* Exception #2: explicit global permit overrides process default deny */
3187 if (GlobalAction == ACTION_PERMIT && (ProcessAction & ACTION_DEFAULT))
3188 {
3189 *PolicyFilename = GlobalPolicyFilename;
3190 *PolicyLineNumber = GlobalPolicyLineNumber;
3191 *RuleNumber = GlobalRuleNumber;
3192 return GlobalAction;
3193 }
3194
3195 if (GlobalAction & ACTION_DENY)
3196 { *PolicyFilename = GlobalPolicyFilename; *PolicyLineNumber = GlobalPolicyLineNumber; *RuleNumber = GlobalRuleNumber; ReturnAction = GlobalAction; goto done; }
3197
3198 if (ProcessAction & ACTION_DENY)
3199 { *PolicyFilename = ProcessPolicyFilename; *PolicyLineNumber = ProcessPolicyLineNumber; *RuleNumber = ProcessRuleNumber; ReturnAction = ProcessAction; goto done; }
3200
3201
3202 if (GlobalAction & ACTION_LOG)
3203 { *PolicyFilename = GlobalPolicyFilename; *PolicyLineNumber = GlobalPolicyLineNumber; *RuleNumber = GlobalRuleNumber; ReturnAction = GlobalAction; goto done; }
3204
3205 if (ProcessAction & ACTION_LOG)
3206 { *PolicyFilename = ProcessPolicyFilename; *PolicyLineNumber = ProcessPolicyLineNumber; *RuleNumber = ProcessRuleNumber; ReturnAction = ProcessAction; goto done; }
3207
3208
3209 if (GlobalAction & ACTION_ASK)
3210 { *PolicyFilename = GlobalPolicyFilename; *PolicyLineNumber = GlobalPolicyLineNumber; *RuleNumber = GlobalRuleNumber; ReturnAction = GlobalAction; goto done; }
3211
3212 if (ProcessAction & ACTION_ASK)
3213 { *PolicyFilename = ProcessPolicyFilename; *PolicyLineNumber = ProcessPolicyLineNumber; *RuleNumber = ProcessRuleNumber; ReturnAction = ProcessAction; goto done; }
3214
3215
3216 if (GlobalAction & ACTION_PERMIT)
3217 { *PolicyFilename = GlobalPolicyFilename; *PolicyLineNumber = GlobalPolicyLineNumber; *RuleNumber = GlobalRuleNumber; ReturnAction = GlobalAction; goto done; }
3218
3219 if (ProcessAction & ACTION_PERMIT)
3220 { *PolicyFilename = ProcessPolicyFilename; *PolicyLineNumber = ProcessPolicyLineNumber; *RuleNumber = ProcessRuleNumber; ReturnAction = ProcessAction; goto done; }
3221
3222
3223 LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("object %s %d %d action %x %x\n", Object, RuleType, OperationType, GlobalAction, ProcessAction));
3224
3225 *PolicyFilename = NULL;
3226 *PolicyLineNumber = 0;
3227
3228
3229 return ProcessAction;
3230
3231
3232done:
3233
3234 /*
3235 * if we are booting up then don't deny any system calls to prevent a machine
3236 * from not booting up
3237 */
3238
3239 if (BootingUp == TRUE && (ReturnAction == ACTION_ASK || (ReturnAction & ACTION_DENY)))
3240 ReturnAction = ACTION_LOG;
3241
3242 return ReturnAction;
3243}