1 | #!/usr/local/bin/python |
---|
2 | |
---|
3 | import sys |
---|
4 | import os |
---|
5 | import os.path |
---|
6 | import re |
---|
7 | |
---|
8 | import ABAC |
---|
9 | import Creddy |
---|
10 | import zipfile |
---|
11 | import tempfile |
---|
12 | from util import * |
---|
13 | |
---|
14 | class policy: |
---|
15 | def __init__(self): |
---|
16 | self.ctxt = ABAC.Context() |
---|
17 | self.translate = 'keyids' |
---|
18 | self.names = { } |
---|
19 | self.keyids = { } |
---|
20 | self.issuers = { } |
---|
21 | self.roles = set() |
---|
22 | self.filename = None |
---|
23 | |
---|
24 | def read_identity(self, cf): |
---|
25 | cid = None |
---|
26 | issuer = False |
---|
27 | ct = abac_pem_type(cf) |
---|
28 | if ct is None or ct =='cert': |
---|
29 | cid = Creddy.ID(cf) |
---|
30 | elif ct == 'both': |
---|
31 | tkf = None |
---|
32 | tcf = None |
---|
33 | try: |
---|
34 | tkf, tcf = abac_split_cert(cf) |
---|
35 | cid = Creddy.ID(tcf) |
---|
36 | cid.load_privkey(tkf) |
---|
37 | issuer = True |
---|
38 | finally: |
---|
39 | if tkf: os.remove(tkf) |
---|
40 | if tcf: os.remove(tcf) |
---|
41 | return (cid, issuer) |
---|
42 | |
---|
43 | def remember_credentials(self): |
---|
44 | self.roles = set() |
---|
45 | for c in self.ctxt.credentials(): |
---|
46 | h = c.head() |
---|
47 | t = c.tail() |
---|
48 | |
---|
49 | if h.is_role(): |
---|
50 | self.roles.add(h.role_name()) |
---|
51 | |
---|
52 | if t.is_role(): |
---|
53 | self.roles.add(t.role_name()) |
---|
54 | elif t.is_linking(): |
---|
55 | self.roles.add(re.sub('.*\.', '', t.linked_role())) |
---|
56 | self.roles.add(t.role_name()) |
---|
57 | |
---|
58 | def read_directory(self, dirname): |
---|
59 | self.names = { } |
---|
60 | self.keyids = { } |
---|
61 | self.issuers = { } |
---|
62 | self.roles = set() |
---|
63 | self.filename = None |
---|
64 | attrs = [ ] |
---|
65 | for p, dirs, files in os.walk(dirname): |
---|
66 | for f in files: |
---|
67 | if not f.endswith('.pem') and not f.endswith('.der'): |
---|
68 | continue |
---|
69 | cf = os.path.join(p, f) |
---|
70 | try: |
---|
71 | cid, issuer = self.read_identity(cf) |
---|
72 | except RuntimeError, e: |
---|
73 | attrs.append(cf) |
---|
74 | cid = None |
---|
75 | |
---|
76 | if cid: |
---|
77 | n = re.sub('_ID.pem', '', cid.cert_filename()) |
---|
78 | self.names[cid.keyid()] = n |
---|
79 | self.keyids[n] = cid.keyid() |
---|
80 | if issuer: self.issuers[cid.keyid()] = cid |
---|
81 | err = self.ctxt.load_id_chunk(cid.cert_chunk()) |
---|
82 | if err != ABAC.ABAC_CERT_SUCCESS: |
---|
83 | print >>sys.stderr, \ |
---|
84 | "Cannot load chunk from %s: %d" % (cf, err) |
---|
85 | for cf in attrs: |
---|
86 | if self.ctxt.load_attribute_file(cf) != ABAC.ABAC_CERT_SUCCESS: |
---|
87 | print >>sys.stderr, "Cannot load %s" % cf |
---|
88 | |
---|
89 | self.remember_credentials() |
---|
90 | |
---|
91 | def read_zip(self, zipname): |
---|
92 | d = tempfile.mkdtemp() |
---|
93 | zf = zipfile.ZipFile(zipname) |
---|
94 | zf.extractall(d) |
---|
95 | self.read_directory(d) |
---|
96 | remove_dirs(d) |
---|
97 | self.filename = zipname |
---|
98 | |
---|
99 | def write_zip(self, zipname): |
---|
100 | old = os.getcwd() |
---|
101 | d = tempfile.mkdtemp() |
---|
102 | z = zipfile.ZipFile(zipname, 'w') |
---|
103 | os.chdir(d) |
---|
104 | os.mkdir('creds') |
---|
105 | seenit = set() |
---|
106 | issuer_no = 0 |
---|
107 | attr_no = 0 |
---|
108 | for cid in self.issuers.values(): |
---|
109 | if cid not in seenit: |
---|
110 | try: |
---|
111 | f = open(os.path.join('creds', |
---|
112 | 'issuer%03d.pem' % issuer_no), 'w') |
---|
113 | issuer_no += 1 |
---|
114 | cid.write_cert(f) |
---|
115 | cid.write_privkey(f) |
---|
116 | f.close() |
---|
117 | seenit.add(cid.cert_chunk()) |
---|
118 | except RuntimeError, e: |
---|
119 | print >>sys.stderr, 'Error writing issuer %s' % e |
---|
120 | for cred in self.ctxt.credentials(): |
---|
121 | issuer_cert = cred.issuer_cert() |
---|
122 | if issuer_cert not in seenit: |
---|
123 | try: |
---|
124 | f = open(os.path.join('creds', |
---|
125 | 'issuer%03d.der' % issuer_no), 'w') |
---|
126 | issuer_no += 1 |
---|
127 | f.write(issuer_cert) |
---|
128 | f.close() |
---|
129 | seenit.add(issuer_cert) |
---|
130 | except: |
---|
131 | print >>sys.stderr, 'Error writing issuer' |
---|
132 | attr_cert = cred.attribute_cert() |
---|
133 | if attr_cert not in seenit: |
---|
134 | try: |
---|
135 | f = open(os.path.join('creds', |
---|
136 | 'attr%03d.der' % attr_no), 'w') |
---|
137 | attr_no += 1 |
---|
138 | f.write(attr_cert) |
---|
139 | f.close() |
---|
140 | seenit.add(attr_cert) |
---|
141 | except: |
---|
142 | print >>sys.stderr, 'Error writing attribute' |
---|
143 | for p, dirs, files in os.walk('.'): |
---|
144 | for f in files: |
---|
145 | z.write(os.path.join(p, f)) |
---|
146 | z.close() |
---|
147 | self.filename = zipname |
---|
148 | os.chdir(old) |
---|
149 | remove_dirs(d) |
---|
150 | |
---|
151 | def translate_cred_roles(self, cred): |
---|
152 | h = cred.head() |
---|
153 | t = cred.tail() |
---|
154 | issuer = h.principal() |
---|
155 | role = h.role_name() |
---|
156 | |
---|
157 | if t.is_principal(): |
---|
158 | return self.replace_keyids("%s says %s acts in role %s" % \ |
---|
159 | ( issuer, t.principal(), role)) |
---|
160 | elif t.is_role(): |
---|
161 | return self.replace_keyids(("%s says any principal that %s " + \ |
---|
162 | "says acts in role %s acts in role %s") % \ |
---|
163 | (issuer, t.principal(), t.role_name(), role)) |
---|
164 | else: |
---|
165 | lr = re.sub('.*\.', '', t.linked_role()) |
---|
166 | return self.replace_keyids(("%s says any principal that a " + \ |
---|
167 | "principal that %s says acts in the role of %s " + \ |
---|
168 | "says acts in role %s acts in role %s") % \ |
---|
169 | (issuer, t.principal(), lr, t.role_name(), role)) |
---|
170 | |
---|
171 | def translate_cred_sets(self, cred): |
---|
172 | h = cred.head() |
---|
173 | t = cred.tail() |
---|
174 | issuer = h.principal() |
---|
175 | role = h.role_name() |
---|
176 | |
---|
177 | if t.is_principal(): |
---|
178 | return self.replace_keyids("%s says %s is in its %s set" % \ |
---|
179 | ( issuer, t.principal(), role)) |
---|
180 | elif t.is_role(): |
---|
181 | return self.replace_keyids(("%s says any principal in %s's " + \ |
---|
182 | "%s set is in %s's %s set") % \ |
---|
183 | (issuer, t.principal(), t.role_name(), issuer, role)) |
---|
184 | else: |
---|
185 | lr = re.sub('.*\.', '', t.linked_role()) |
---|
186 | return self.replace_keyids(("%s says any principal in %s's " + \ |
---|
187 | "%s set can put a principal in %s's %s set by putting " + \ |
---|
188 | " that principal in %s") % \ |
---|
189 | (issuer, t.principal(), lr, issuer, role, t.role_name())) |
---|
190 | |
---|
191 | def translate_cred_keyids(self, cred): |
---|
192 | return self.replace_keyids("%s<-%s" % \ |
---|
193 | (cred.head().string(), cred.tail().string())) |
---|
194 | |
---|
195 | def translate_cred_null(self, cred): |
---|
196 | return "%s<-%s" % (cred.head().string(), cred.tail().string()) |
---|
197 | |
---|
198 | def translate_cred(self, cred): |
---|
199 | if self.translate == 'sets': return self.translate_cred_sets(cred) |
---|
200 | elif self.translate == 'roles': return self.translate_cred_roles(cred) |
---|
201 | elif self.translate == 'keyids': return self.translate_cred_keyids(cred) |
---|
202 | elif self.translate == 'none': return self.translate_cred_null(cred) |
---|
203 | else: return 'Bad type of translation (%s)???' % self.translate |
---|
204 | |
---|
205 | @staticmethod |
---|
206 | def is_issuer(cid): |
---|
207 | tf = tempfile.TemporaryFile() |
---|
208 | try: |
---|
209 | cid.write_privkey(tf) |
---|
210 | return True |
---|
211 | except: |
---|
212 | return False |
---|
213 | |
---|
214 | def add_credential(self, cert): |
---|
215 | ''' |
---|
216 | Cert is a Creddy.Credential |
---|
217 | ''' |
---|
218 | rv = self.ctxt.load_attribute_chunk(cert.cert_chunk()) |
---|
219 | self.remember_credentials() |
---|
220 | return rv |
---|
221 | |
---|
222 | def remove_credentials(self, creds): |
---|
223 | if isinstance(creds, list): |
---|
224 | cs = set(["%s<-%s" % (c.head().string, c.tail.string()) \ |
---|
225 | for c in creds]) |
---|
226 | else: |
---|
227 | cs = set(["%s<-%s" % (creds.head().string, creds.tail.string())]) |
---|
228 | |
---|
229 | nc = ABAC.Context() |
---|
230 | for c in self.ctxt.credentials(): |
---|
231 | crid = "%s<-%s" % (c.head().string, c.tail.string()) |
---|
232 | if crid not in cs: |
---|
233 | nc.load_id_chunk(c.issuer_cert()) |
---|
234 | nc.load_attribute_chunk(c.attribute_cert()) |
---|
235 | self.ctxt = nc |
---|
236 | |
---|
237 | def add_identity(self, cid): |
---|
238 | ''' |
---|
239 | cid is a Creddy.ID |
---|
240 | ''' |
---|
241 | rv = self.ctxt.load_attribute_chunk(cid.cert_chunk()) |
---|
242 | if self.is_issuer(cid): |
---|
243 | self.issuers[cid.keyid()] = cid |
---|
244 | n = re.sub('_ID.pem', '', cid.cert_filename()) |
---|
245 | self.names[cid.keyid()] = n |
---|
246 | self.keyids[n] = cid.keyid() |
---|
247 | |
---|
248 | def credentials(self): |
---|
249 | return self.ctxt.credentials() |
---|
250 | |
---|
251 | def query(self, role, principal): |
---|
252 | return self.ctxt.query(role, principal) |
---|
253 | |
---|
254 | def principal_names(self): |
---|
255 | return self.names.keys() |
---|
256 | |
---|
257 | def principal_items(self): |
---|
258 | return self.names.items() |
---|
259 | |
---|
260 | def replace_keyids(self, s): |
---|
261 | for k,r in self.names.items(): |
---|
262 | s = re.sub(k, r, s) |
---|
263 | return s |
---|
264 | |
---|
265 | def name_to_keyid(self, n): |
---|
266 | return self.keyids.get(n, n) |
---|