1#!/usr/bin/python
2#
3# gkgenerate - produce Gatekeeper explicit allow data
4#
5# gkgenerate [--output name] files...
6#	will collect GKE data from all files and write two output files (name.auth and name.sigs)
7#	that are ready to drop into a /var/db for pickup.
8#
9import sys
10import os
11import signal
12import errno
13import subprocess
14import argparse
15import plistlib
16import uuid
17
18
19#
20# Parameters and constants
21#
22authfile = "gke.auth"
23sigfile = "gke.dsig"
24
25#
26# Usage and fail
27#
28def usage():
29	print >>sys.stderr, "Usage: %s sourcedir" % sys.argv[0]
30	sys.exit(2)
31
32def fail(whatever):
33	print >>sys.stderr, "%s: %s" % (sys.argv[0], whatever)
34	sys.exit(1)
35
36
37#
38# Argument processing
39#
40parser = argparse.ArgumentParser()
41parser.add_argument("--output", default="./gke", help="name of output files")
42parser.add_argument("--uuid", default=uuid.uuid4(), help="explicitly specify the uuid stamp")
43parser.add_argument("--empty", action='store_true', help="allow empty output sets")
44parser.add_argument('source', nargs='+', help='files generated by the gkrecord command')
45args = parser.parse_args()
46
47authfile = args.output + ".auth"
48sigsfile = args.output + ".sigs"
49
50
51#
52# Augment a snippet record
53#
54def augment(data):
55	for auth in data.authority.values():
56		if auth.path in data.signatures:
57			signature = data.signatures[auth.path].signature.data
58			unpack = subprocess.Popen(["/usr/local/bin/gkunpack"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
59			(stdout, stderr) = unpack.communicate(input=signature)
60			if stderr:
61				fail("signature unpack failed for %s" % auth.path)
62			auth.screen = stdout.rstrip();
63
64
65#
66# Start by collecting authority evidence from the authority records
67#
68auth = { }
69sigs = { }
70for source in args.source:
71	if source[0] == '+':
72		data = plistlib.readPlist(source[1:])
73		augment(data)
74		auth.update(data["authority"])
75		sigs.update(data["signatures"])
76	else:
77		data = plistlib.readPlist(source)
78		augment(data)
79		auth.update(data["authority"])
80
81if not auth and not args.empty:
82	fail("No authority records (nothing to do)")
83
84
85#
86# Scrub the authority records to remove incriminating evidence
87#
88new_auth = { }
89for rec in auth.values():
90	u = uuid.uuid4()
91	rec["path"] = "(gke)"
92	del rec["status"]
93	new_auth[str(u)] = rec
94auth = new_auth
95
96
97#
98# The authority file is written as-is, as a plist
99#
100wrap = dict(
101	authority=auth,
102	uuid=str(args.uuid)
103)
104plistlib.writePlist(wrap, authfile)
105print "Wrote %d authority record(s) to %s" % (len(auth), authfile)
106
107
108#
109# The signatures are written as tightly packed signature blobs
110#
111sigblobs = open(sigsfile, "w")
112for sig in sigs:
113	sigdata = sigs[sig]
114	sigblobs.write(sigdata["signature"].data)
115sigblobs.close()
116print "Wrote %d signature record(s) to %s" % (len(sigs), sigsfile)
117