source: java/net/deterlab/abac/Context.java @ 418b586

abac0-leakabac0-meicompt_changesgec13mei-idmei-rt0-nmei_rt0mei_rt2mei_rt2_fix_1meiyap-rt1meiyap1rt2tvf-new-xml
Last change on this file since 418b586 was 418b586, checked in by Ted Faber <faber@…>, 13 years ago

More run at initial interface

  • Property mode set to 100644
File size: 16.3 KB
Line 
1package net.deterlab.abac;
2
3import org.apache.commons.collections15.*;
4import org.apache.commons.collections15.functors.*;
5
6import edu.uci.ics.jung.graph.*;
7import edu.uci.ics.jung.graph.event.*;
8import edu.uci.ics.jung.graph.util.*;
9
10import java.awt.geom.Point2D;
11import java.io.*;
12import java.util.*;
13import java.util.zip.*;
14import java.security.*;
15import java.security.cert.*;
16
17import org.bouncycastle.x509.*;
18import org.bouncycastle.x509.util.*;
19import org.bouncycastle.openssl.*;
20
21/**
22 * Represents a global graph of credentials in the form of principals and
23 * attributes.
24 */
25public class Context {
26    static final int ABAC_CERT_SUCCESS = 0;
27    static final int ABAC_CERT_INVALID = -1;
28    static final int ABAC_CERT_BAD_SIG = -2;
29    static final int ABAC_CERT_MISSING_ISSUER = -3;
30
31    protected Graph<Role,Credential> g;
32    protected Set<Credential> derived_edges;
33    protected Query pq;
34    protected boolean dirty;
35    protected Set<Identity> identities;
36
37    protected Map<String, String> nicknames;
38    protected Map<String, String> keys;
39
40    public Context() {
41        /* create the graph */
42        g = Graphs.<Role,Credential>synchronizedDirectedGraph(
43                new DirectedSparseGraph<Role,Credential>());
44        derived_edges = new HashSet<Credential>();
45        pq = new Query(g);
46        dirty = false;
47        identities = new TreeSet<Identity>();
48        nicknames = new TreeMap<String, String>();
49        keys = new TreeMap<String, String>();
50    }
51
52    public Context(Context c) {
53        this();
54        for (Identity i: c.identities) 
55            loadIDChunk(i);
56        for (Credential cr: c.credentials()) 
57            loadAttributeChunk(cr);
58        derive_implied_edges();
59    }
60
61    public int loadIDFile(String fn) { return loadIDFile(new File(fn)); }
62    public int loadIDFile(File fn) {
63        try {
64            addIdentity(new Identity(fn));
65        }
66        catch (SignatureException sig) {
67            return ABAC_CERT_BAD_SIG;
68        }
69        catch (Exception e) {
70            return ABAC_CERT_INVALID;
71        }
72        return ABAC_CERT_SUCCESS;
73    }
74
75    public int loadIDChunk(Object c) {
76        try {
77            if (c instanceof Identity)
78                addIdentity((Identity) c);
79            else if (c instanceof String) 
80                addIdentity(new Identity((String) c));
81            else if (c instanceof File) 
82                addIdentity(new Identity((File) c));
83            else if (c instanceof X509Certificate) 
84                addIdentity(new Identity((X509Certificate) c));
85            else 
86                return ABAC_CERT_INVALID;
87        }
88        catch (SignatureException sig) {
89            return ABAC_CERT_BAD_SIG;
90        }
91        catch (Exception e) {
92            return ABAC_CERT_INVALID;
93        }
94        return ABAC_CERT_SUCCESS;
95    }
96
97    public int loadAttributeFile(String fn) { 
98        return loadAttributeFile(new File(fn));
99    }
100
101    public int loadAttributeFile(File fn) {
102        try {
103            add_credential(new Credential(fn, identities));
104        }
105        catch (InvalidKeyException sig) {
106            return ABAC_CERT_MISSING_ISSUER;
107        }
108        catch (Exception e) {
109            return ABAC_CERT_INVALID;
110        }
111        return ABAC_CERT_SUCCESS;
112    }
113
114    public int loadAttributeChunk(Object c) {
115        try {
116            if (c instanceof Credential)
117                add_credential((Credential) c);
118            else if (c instanceof String) 
119                add_credential(new Credential((String) c, identities));
120            else if (c instanceof File) 
121                add_credential(new Credential((File) c, identities));
122            else if ( c instanceof X509V2AttributeCertificate) 
123                add_credential(new Credential((X509V2AttributeCertificate)c,
124                            identities));
125            else 
126                return ABAC_CERT_INVALID;
127        }
128        catch (SignatureException sig) {
129            return ABAC_CERT_BAD_SIG;
130        }
131        catch (Exception e) {
132            return ABAC_CERT_INVALID;
133        }
134        return ABAC_CERT_SUCCESS;
135    }
136
137    public Collection<Credential> query(String role, String principal) {
138        derive_implied_edges();
139
140        Query q = new Query(g);
141        Graph<Role, Credential> rg = q.run(role, principal);
142        return rg.getEdges();
143    }
144
145    /**
146     * Returns a collection of the credentials in the graph.
147     */
148    public Collection<Credential> credentials() {
149        Collection<Credential> creds = new HashSet<Credential>();
150
151        // only return creds with a cert: all others are derived edges
152        for (Credential cred : g.getEdges())
153            if (cred.cert() != null)
154                creds.add(cred);
155
156        return creds;
157    }
158
159
160    /**
161     * Returns a Query object which can be used to query the graph. This object
162     * becomes invalid if the graph is modified.
163     */
164    public Query querier() {
165        derive_implied_edges();
166        return new Query(g);
167    }
168
169    /**
170     * Add a credential to the graph.
171     */
172    public void add_credential(Credential cred) {
173        Role tail = cred.tail();
174        Role head = cred.head();
175
176        /* explicitly add the vertices, otherwise get a null pointer exception */
177        if ( !g.containsVertex(head)) 
178            g.addVertex(head);
179        if ( !g.containsVertex(tail)) 
180            g.addVertex(tail);
181
182        if (!g.containsEdge(cred))
183            g.addEdge(cred, tail, head);
184
185        // add the prereqs of an intersection to the graph
186        if (tail.is_intersection())
187            for (Role prereq : tail.prereqs())
188                g.addVertex(prereq);
189
190        dirty = true;
191    }
192
193    /**
194     * Remove a credential from the graph.
195     */
196    public void remove_credential(Credential cred) {
197        if (g.containsEdge(cred))
198            g.removeEdge(cred);
199        dirty = true;
200    }
201
202    /**
203     * Add a role w/o an edge
204     */
205    public void add_vertex(Role v) {
206        if (!g.containsVertex(v)) {
207            g.addVertex(v);
208            dirty = true;
209        }
210    }
211
212    public void remove_vertex(Role v) {
213        if (g.containsVertex(v)) {
214            g.removeVertex(v);
215            dirty = true;
216        }
217    }
218    public Collection<Role> roles() {
219        return g.getVertices();
220    }
221
222    /**
223     * Derive the implied edges in the graph, according to RT0 derivation rules.
224     * They are added to this graph. See "Distributed Credential Chain Discovery
225     * in Trust Management" by Ninghui Li et al. for details. Note that a
226     * derived linking edge can imply a new intersection edge and vice versa.
227     * Therefore we iteratively derive edges, giving up when an iteration
228     * produces 0 new edges.
229     */
230    protected synchronized void derive_implied_edges() {
231        // nothing to do on a clean graph
232        if (!dirty)
233            return;
234
235        clear_old_edges();
236
237        // iteratively derive links. continue as long as new links are added
238        while (derive_links_iter() > 0)
239            ;
240        dirty = false;
241    }
242
243    /**
244     * Single iteration of deriving implied edges. Returns the number of new
245     * links added.
246     */
247    protected int derive_links_iter() {
248        int count = 0;
249
250        /* for every node in the graph.. */
251        for (Role vertex : g.getVertices()) {
252            if (vertex.is_intersection()) {
253                // for each prereq edge:
254                //     find set of principals that have the prereq
255                // find the intersection of all sets (i.e., principals that satisfy all prereqs)
256                // for each principal in intersection:
257                //     add derived edge
258
259                Set<Role> principals = null;
260
261                for (Role prereq : vertex.prereqs()) {
262                    Set<Role> cur_principals = pq.find_principals(prereq);
263
264                    if (principals == null)
265                        principals = cur_principals;
266                    else
267                        // no, they couldn't just call it "intersection"
268                        principals.retainAll(cur_principals);
269
270                    if (principals.size() == 0)
271                        break;
272                }
273
274                // add em
275                for (Role principal : principals)
276                    if (add_derived_edge(vertex, principal))
277                        ++count;
278            }
279
280            else if (vertex.is_linking()) {
281                // make the rest of the code a bit clearer
282                Role A_r1_r2 = vertex;
283
284                Role A_r1 = new Role(A_r1_r2.A_r1());
285                String r2 = A_r1_r2.r2();
286
287                /* locate the node A.r1 */
288                if (!g.containsVertex(A_r1)) continue; /* boring: nothing of the form A.r1 */
289
290                /* for each B that satisfies A_r1 */
291                for (Role principal : pq.find_principals(A_r1)) {
292                    Role B_r2 = new Role(principal + "." + r2);
293                    if (!g.containsVertex(B_r2)) continue;
294
295                    if (add_derived_edge(A_r1_r2, B_r2))
296                        ++count;
297                }
298            }
299        }
300
301        return count;
302    }
303
304    /**
305     * Add a derived edge in the graph. Returns true only if the edge does not
306     * exist.
307     */
308    protected boolean add_derived_edge(Role head, Role tail) {
309        // edge exists: return false
310        if (g.findEdge(tail, head) != null)
311            return false;
312
313        // add the new edge
314        Credential derived_edge = new Credential(head, tail);
315        derived_edges.add(derived_edge);
316        g.addEdge(derived_edge, tail, head);
317
318        return true;
319    }
320
321    /**
322     * Clear the derived edges that currently exist in the graph. This is done
323     * before the edges are rederived. The derived edges in filtered graphs are
324     * also cleared.
325     */
326    protected void clear_old_edges() { 
327        for (Credential i: derived_edges) 
328            g.removeEdge(i);
329        derived_edges = new HashSet<Credential>();
330    }
331    /**
332     * Put the Identity into the set of ids used to validate certificates.
333     * Also put the keyID and name into the translation mappings used by Roles
334     * to pretty print.  In the role mapping, if multiple ids use the same
335     * common name they are disambiguated.  Only one entry for keyid is
336     * allowed.
337     */
338    protected void addIdentity(Identity id) { 
339        identities.add(id);
340        if (id.getName() != null && id.getKeyID() != null) {
341            if ( !keys.containsKey(id.getKeyID()) ) {
342                String name = id.getName();
343                int n= 1;
344
345                while (nicknames.containsKey(name)) {
346                    name = id.getName() + n++;
347                }
348                nicknames.put(name, id.getKeyID());
349                keys.put(id.getKeyID(), name);
350            }
351        }
352    }
353    /**
354     * Translate either keys to nicknames or vice versa.  Break the string into
355     * space separated tokens and then each of them into period separated
356     * strings.  If any of the smallest strings is in the map, replace it with
357     * the value.
358     */
359    protected String replace(String is, Map<String, String> m) {
360        String rv = "";
361        for (String tok: is.split(" ")) {
362            String term = "";
363            for (String s: tok.split("\\.")) {
364                String next = m.containsKey(s) ? m.get(s) : s;
365
366                if (term.isEmpty()) term = next;
367                else term += "." + next;
368            }
369            if (rv.isEmpty()) rv = term;
370            else rv += " " + term;
371        }
372        return rv;
373    }
374
375    public String expandKeyID(String s) { return replace(s, nicknames); }
376    public String expandNickname(String s) { return replace(s, keys); }
377
378    /**
379     * Import a zip file.  First import all the identities
380     * (pem), then the credentials (der) into the credential graph then any
381     * alias files into the two maps.  If keys is not null, any key pairs in
382     * PEM files are put in there.  If errors is not null, errors reading files
383     * are added indexed by filename.
384     */
385    public void readZipFile(File zf, Collection<KeyPair> keys, 
386            Map<String, Exception> errors) throws IOException {
387        Vector<ZipEntry> derEntries = new Vector<ZipEntry>();
388        Map<String, Identity> ids = new TreeMap<String, Identity>();
389        Map<String, KeyPair> kps = new TreeMap<String, KeyPair>();
390
391        ZipFile z = new ZipFile(zf);
392
393        for (Enumeration<? extends ZipEntry> ze = z.entries(); 
394                ze.hasMoreElements();) {
395            ZipEntry  f = ze.nextElement();
396            try {
397                PEMReader r = new PEMReader(
398                        new InputStreamReader(z.getInputStream(f)));
399                Object o = readPEM(r);
400
401                if ( o != null ) {
402                    if (o instanceof Identity) {
403                        Identity i = (Identity) o;
404                        String kid = i.getKeyID();
405
406                        if (kps.containsKey(kid) ) {
407                            i.setKeyPair(kps.get(kid));
408                            kps.remove(kid);
409                        }
410                        else if (i.getKeyPair() == null ) 
411                            ids.put(i.getKeyID(), i);
412
413                        loadIDChunk(i);
414                    }
415                    else if (o instanceof KeyPair ) {
416                        KeyPair kp = (KeyPair) o;
417                        String kid = Identity.extractKeyID(kp.getPublic());
418
419                        if (ids.containsKey(kid)) {
420                            Identity i = ids.get(kid);
421
422                            i.setKeyPair(kp);
423                            ids.remove(kid);
424                        }
425                        else {
426                            kps.put(kid, kp);
427                        }
428                    }
429                }
430                else {
431                    // Not a PEM file
432                    derEntries.add(f);
433                    continue;
434                }
435            }
436            catch (Exception e ) {
437                if (errors != null ) errors.put(f.getName(), e);
438            }
439        }
440
441        for ( ZipEntry f : derEntries ) {
442            try {
443                add_credential(new Credential(z.getInputStream(f), identities));
444            }
445            catch (Exception e ) {
446                if (errors != null ) errors.put(f.getName(), e);
447            }
448        }
449    }
450
451    public void readZipFile(File d) 
452            throws IOException {
453        readZipFile(d, null, null);
454    }
455    public void readZipFile(File d, 
456            Map<String, Exception> errors) throws IOException {
457        readZipFile(d, null, errors);
458    }
459    public void readZipFile(File d, 
460            Collection<KeyPair> keys) throws IOException {
461        readZipFile(d, keys, null);
462    }
463
464    protected Object readPEM(PEMReader r) throws IOException {
465        Identity i = null;
466        KeyPair keys = null;
467        Object o = null;
468
469        while ( (o = r.readObject()) != null ) {
470            if (o instanceof X509Certificate) {
471                if ( i == null ) {
472                    try {
473                        i = new Identity((X509Certificate)o);
474                    }
475                    catch (Exception e) {
476                        // Translate Idenitiy exceptions to IOException
477                        throw new IOException(e);
478                    }
479                    if (keys != null ) {
480                        i.setKeyPair(keys);
481                        keys = null;
482                    }
483                }
484                else throw new IOException("Two certificates");
485            }
486            else if (o instanceof KeyPair ) {
487                if ( i != null ) i.setKeyPair((KeyPair) o);
488                else keys = (KeyPair) o;
489            }
490            else {
491                throw new IOException("Unexpected PEM object: " + 
492                        o.getClass().getName());
493            }
494        }
495
496        if ( i != null ) return i;
497        else if ( keys != null) return keys;
498        else return null;
499    }
500
501    /**
502     * Import a directory full of files.  First import all the identities
503     * (pem), then the credentials (der) into the credential graph then any
504     * alias files into the two maps.  If keys is not null, any key pairs in
505     * PEM files are put in there.  If errors is not null, errors reading files
506     * are added indexed by filename.
507     */
508    public void readDirectory(File d, Collection<KeyPair> keys, 
509            Map<String, Exception> errors) {
510        Vector<File> derFiles = new Vector<File>();
511        Collection<File> files = new Vector<File>();
512        Map<String, Identity> ids = new TreeMap<String, Identity>();
513        Map<String, KeyPair> kps = new TreeMap<String, KeyPair>();
514
515        if (d.isDirectory() ) 
516            for (File f : d.listFiles()) 
517                files.add(f);
518        else files.add(d);
519
520        for (File f: files ) {
521            try {
522                PEMReader r = new PEMReader(new FileReader(f));
523                Object o = readPEM(r);
524
525                if ( o != null ) {
526                    if (o instanceof Identity) {
527                        Identity i = (Identity) o;
528                        String kid = i.getKeyID();
529
530                        if (kps.containsKey(kid) ) {
531                            i.setKeyPair(kps.get(kid));
532                            kps.remove(kid);
533                        }
534                        else if (i.getKeyPair() == null ) 
535                            ids.put(i.getKeyID(), i);
536
537                        loadIDChunk(i);
538                    }
539                    else if (o instanceof KeyPair ) {
540                        KeyPair kp = (KeyPair) o;
541                        String kid = Identity.extractKeyID(kp.getPublic());
542
543                        if (ids.containsKey(kid)) {
544                            Identity i = ids.get(kid);
545
546                            i.setKeyPair(kp);
547                            ids.remove(kid);
548                        }
549                        else {
550                            kps.put(kid, kp);
551                        }
552                    }
553                }
554                else {
555                    // Not a PEM file
556                    derFiles.add(f);
557                    continue;
558                }
559            }
560            catch (Exception e ) {
561                if (errors != null ) errors.put(f.getName(), e);
562            }
563        }
564
565        for ( File f : derFiles ) {
566            try {
567                add_credential(new Credential(f, identities));
568            }
569            catch (Exception e ) {
570                if (errors != null ) errors.put(f.getName(), e);
571            }
572        }
573    }
574
575    public void readDirectory(File d) {
576        readDirectory(d, null, null);
577    }
578    public void readDirectory(File d, Map<String, Exception> errors) {
579        readDirectory(d, null, errors);
580    }
581    public void readDirectory(File d, Collection<KeyPair> keys) {
582        readDirectory(d, keys, null);
583    }
584
585    public void writeZipFile(File f, boolean allIDs, boolean withPrivateKeys) 
586            throws IOException {
587        ZipOutputStream z = new ZipOutputStream(new FileOutputStream(f));
588        Set<Identity> ids = allIDs ?  identities : new TreeSet<Identity>();
589
590        int n = 0;
591        for (Credential c: credentials()) {
592            z.putNextEntry(new ZipEntry("attr" + n++  + ".der"));
593            c.write(z);
594            z.closeEntry();
595            if ( c.getID() != null && !allIDs) ids.add(c.getID());
596        }
597        for (Identity i: ids) {
598            z.putNextEntry(new ZipEntry(i.getName() + ".pem"));
599            i.write(z);
600            if (withPrivateKeys)
601                i.writePrivateKey(z);
602            z.closeEntry();
603        }
604        z.close();
605    }
606
607}
Note: See TracBrowser for help on using the repository browser.