]> git.saurik.com Git - apple/security.git/blob - libsecurity_codesigning/gke/gkrecord
Security-55471.tar.gz
[apple/security.git] / libsecurity_codesigning / gke / gkrecord
1 #!/usr/bin/python
2 #
3 # gkrecord - record Gatekeeper rejection activity
4 #
5 # gkrecord filename
6 #
7 import sys
8 import os
9 import signal
10 import errno
11 import subprocess
12 import tempfile
13 import plistlib
14
15
16 #
17 # Usage and fail
18 #
19 def usage():
20 print >>sys.stderr, "Usage: %s outputfile" % sys.argv[0]
21 sys.exit(2)
22
23 def fail(whatever):
24 print >>sys.stderr, "%s: %s" % (sys.argv[0], whatever)
25 sys.exit(1)
26
27
28 #
29 # Argument processing
30 #
31 if len(sys.argv) != 2:
32 usage()
33 outputfile = sys.argv[1]
34
35
36 #
37 # If the output file already exists, bail
38 #
39 if os.path.exists(outputfile):
40 fail("already exists: %s" % outputfile)
41
42
43 #
44 # Places and things
45 #
46 collect = "/tmp/gke/"
47
48
49 # must be root
50 if os.getuid() != 0:
51 fail("Must have root privileges")
52
53
54 #
55 # Make sure Gatekeeper is disabled
56 #
57 subprocess.check_call(["/usr/sbin/spctl", "--master-disable"])
58
59
60 #
61 # make sure we have a fresh syspolicyd and get its pid
62 #
63 subprocess.check_call(["/usr/sbin/spctl", "--assess", "--ignore-cache", "/bin/ls"])
64 try:
65 psax = subprocess.check_output("ps ax|grep syspolicyd|grep -v grep", shell=True).split("\n")
66 if len(psax) != 2: # [ found_syspolicyd, '' ]
67 fail("Cannot find syspolicyd")
68 spd_pid = int(psax[0].split()[0])
69 except subprocess.CalledProcessError:
70 fail("Cannot find syspolicyd")
71
72
73 #
74 # run collector dtrace script until dtrace dies.
75 # recorder_mode arguments are (path, type, label, cdhash, flags)
76 #
77 DSCRIPT = '''
78 syspolicy$1:::recorder_mode { printf("RECORD;%d;%d", arg1, arg4); }
79
80 self unsigned char *cdhash;
81
82 syspolicy$1:::recorder_mode
83 {
84 self->cdhash = copyin(arg3, 20);
85 printf(";%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x",
86 self->cdhash[0], self->cdhash[1], self->cdhash[2], self->cdhash[3], self->cdhash[4],
87 self->cdhash[5], self->cdhash[6], self->cdhash[7], self->cdhash[8], self->cdhash[9],
88 self->cdhash[10], self->cdhash[11], self->cdhash[12], self->cdhash[13], self->cdhash[14],
89 self->cdhash[15], self->cdhash[16], self->cdhash[17], self->cdhash[18], self->cdhash[19]);
90 printf(";%s\\n", copyinstr(arg0));
91 }
92
93 syspolicy$1:::recorder_mode_adhoc_path
94 {
95 printf("SIGNATURE;%d;%s;%s\\n", arg1, copyinstr(arg2), copyinstr(arg0));
96 }
97
98 syspolicy$1:::assess-outcome-unsigned
99 {
100 printf("UNSIGNED;%d;%s\\n", arg1, copyinstr(arg0));
101 }
102
103 syspolicy$1:::assess-outcome-broken
104 {
105 printf("BROKEN;%d;%d;%s\\n", arg1, arg2, copyinstr(arg0));
106 }
107 '''
108
109 def sigint(sig, ctx):
110 os.kill(spd_pid, signal.SIGINT)
111 signal.signal(signal.SIGINT, sigint)
112
113 (authfd, authfile) = tempfile.mkstemp()
114 dtrace = subprocess.Popen(["dtrace", "-qs", "/dev/stdin", str(spd_pid)], stdin=subprocess.PIPE, stdout=authfd, stderr=subprocess.PIPE)
115 print "Exercise the programs to be whitelisted now. Interrupt this script (^C) when you are done."
116 (stdout, stderr) = dtrace.communicate(input=DSCRIPT)
117 signal.signal(signal.SIGINT, signal.SIG_DFL)
118 if stderr:
119 fail("dtrace failed: %s" % stderr)
120 os.lseek(authfd, os.SEEK_SET, 0) # rewind
121
122
123 #
124 # Collect all the data into dicts
125 #
126 auth = { }
127 sigs = { }
128 unsigned = { }
129 badsigned = { }
130 errors = { }
131
132 file = os.fdopen(authfd, "r")
133 for line in file:
134 (cmd, s, args) = line.strip().partition(";")
135 if s != ";":
136 continue # spurious
137 # print cmd, "--->", args
138 if cmd == "RECORD":
139 (type, status, cdhash, path) = args.split(";", 3)
140 auth[path] = dict(
141 path=path,
142 type=type,
143 status=status,
144 cdhash=cdhash,
145 version=2
146 )
147 elif cmd == "SIGNATURE":
148 (type, sigpath, path) = args.split(";", 2)
149 with open(sigpath, "r") as sigfile:
150 sigdata = sigfile.read()
151 sigs[path] = dict(
152 path=path,
153 type=type,
154 signature=plistlib.Data(sigdata)
155 )
156 elif cmd == "UNSIGNED":
157 (type, path) = args.split(";", 1)
158 unsigned[path] = dict(
159 path=path,
160 type=type
161 )
162 elif cmd == "BROKEN":
163 (type, exception, path) = args.split(";", 2)
164 badsigned[path] = dict(
165 path=path,
166 type=type,
167 exception=exception
168 )
169
170 # unsigned code that had a good detached signature recorded is okay
171 for rec in sigs:
172 if rec in unsigned:
173 del unsigned[rec]
174
175
176 #
177 # Pack them up as a single output (plist) file
178 #
179 gkedict = dict(
180 authority = auth,
181 signatures = sigs
182 )
183 plistlib.writePlist(gkedict, outputfile)
184
185
186 #
187 # Report on any problems found
188 #
189 for rec in unsigned.values():
190 print >>sys.stderr, "PROBLEM: unsigned type %d object not whitelisted: %s" % (rec["type"], rec["path"])
191 for rec in badsigned.values():
192 print >>sys.stderr, "PROBLEM: broken code signature; object not whitelisted: %s" % rec["path"]
193
194
195 #
196 # Done
197 #
198 print "Recorded %d authorization(s), %d signature(s) in %s" % (len(auth), len(sigs), outputfile)
199 sys.exit(0)