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