source: java/net/deterlab/abac/Context.java @ 53f5c27

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

checkpoint

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