source: libabac/abac_graph.c @ 367e333

abac0-leak
Last change on this file since 367e333 was 8a9f7af, checked in by Ted Faber <faber@…>, 11 years ago

Get refcounting right

  • Property mode set to 100644
File size: 19.6 KB
Line 
1#include <assert.h>
2#include <stdlib.h>
3
4#include "abac_graph.h"
5
6#include "abac_set.h"
7#include "abac_util.h"
8
9#include "uthash.h"
10
11// vertex
12struct _abac_vertex_t {
13    abac_role_t *role;
14    char *name;
15    int refcount;
16
17    abac_list_t *edges;
18    abac_list_t *reverse_edges;
19
20    // only relevant to intersection edges
21    abac_list_t *prereqs;
22
23    UT_hash_handle hh;
24};
25
26// edge
27typedef struct _abac_edge_t {
28    int refcount;
29    abac_vertex_t *vertex;
30    abac_vertex_t *reverse_vertex;
31    abac_credential_t *credential;
32} abac_edge_t;
33
34// derived edge
35typedef struct _abac_derived_key_t {
36    abac_vertex_t *head;
37    abac_edge_t *tail;
38} abac_derived_key_t;
39
40typedef struct _abac_derived_t {
41    abac_derived_key_t key;
42    UT_hash_handle hh;
43} abac_derived_t;
44
45// graph
46struct _abac_graph_t {
47    abac_vertex_t *vertices;
48    abac_derived_t *derived;
49    int dirty;
50};
51
52// ugghhhghhhghh need this for intersections
53abac_list_t *abac_role_prereqs(abac_role_t *);
54
55/**
56 * Create a new graph.
57 */
58abac_graph_t *abac_graph_new(void) {
59    abac_graph_t *graph = abac_xmalloc(sizeof(abac_graph_t));
60
61    graph->vertices = NULL;
62    graph->derived = NULL;
63    graph->dirty = 0;
64
65    return graph;
66}
67
68/**
69 * Deep copy a graph.
70 */
71abac_graph_t *abac_graph_dup(abac_graph_t *graph) {
72    abac_vertex_t *vertex;
73    abac_edge_t *edge;
74
75    abac_graph_t *clone = abac_graph_new();
76
77    // copy the vertices edge by edge
78    for (vertex = graph->vertices; vertex != NULL; vertex = vertex->hh.next)
79        abac_list_foreach(vertex->edges, edge,
80            // only copy non-derived edges
81            if (edge->credential != NULL)
82                abac_graph_add_credential(clone, edge->credential);
83        );
84
85    return clone;
86}
87/**
88 * Add a vertex to the graph. Should only be called by
89 * abac_graph_add_credential. This returns an existing vertex with the refcount
90 * bumped, or a new vertex with a refcount of 1.  Either way it gets and
91 * references the vertex.
92 */
93static abac_vertex_t *_get_vertex(abac_graph_t *graph, abac_role_t *role) {
94    abac_vertex_t *vertex;
95    char *string;
96   
97    string = abac_role_string(role);
98    HASH_FIND_STR(graph->vertices, string, vertex);
99
100    // add the vertex if it doesn't exist
101    if (vertex == NULL) {
102        vertex = abac_xmalloc(sizeof(abac_vertex_t));
103        vertex->refcount = 0;
104        vertex->role = abac_role_dup(role);
105        vertex->name = abac_role_string(vertex->role);
106
107        // create the list of edges
108        vertex->edges = abac_list_new();
109        vertex->reverse_edges = abac_list_new();
110
111        // for intersections, always NULL on normal vertices
112        if (abac_role_is_intersection(role)) {
113            abac_role_t *prereq;
114            vertex->prereqs = abac_list_new();
115
116            // add each prereq to the vertex
117            abac_list_foreach(abac_role_prereqs(role), prereq,
118                abac_vertex_t *tail_vertex = _get_vertex(graph, prereq);
119                abac_list_add(vertex->prereqs, tail_vertex);
120            );
121        }
122
123        // normal edges have no prereqs
124        else
125            vertex->prereqs = NULL;
126
127        // add it to the vertices
128        HASH_ADD_KEYPTR(hh, graph->vertices, vertex->name, strlen(vertex->name), vertex);
129    }
130    vertex->refcount++;
131
132    return vertex;
133}
134
135/* forward decl */
136static void _free_edge(abac_edge_t *edge);
137/*
138 * Reduce the vertex reference count and free it if this is the last reference
139 */
140static void _free_vertex(abac_vertex_t *vertex) {
141    abac_edge_t *edge=NULL;
142    abac_vertex_t *pre=NULL;
143
144    if ( --vertex->refcount > 0) return;
145
146    abac_role_free(vertex->role);
147
148    abac_list_foreach(vertex->edges, edge,
149        _free_edge(edge);
150    );
151    abac_list_free(vertex->edges);
152
153    abac_list_foreach(vertex->reverse_edges, edge,
154        _free_edge(edge);
155    );
156    abac_list_free(vertex->reverse_edges);
157
158    // Free the prereqs
159    if (vertex->prereqs != NULL) {
160        abac_list_foreach(vertex->prereqs, pre,
161            if (pre != NULL)
162                _free_vertex(pre);
163            free(pre);
164        );
165        abac_list_free(vertex->prereqs);
166    }
167
168    free(vertex);
169}
170
171/*
172 * Increment vertex reference count
173 */
174static abac_vertex_t *_dup_vertex(abac_vertex_t *v) {
175    v->refcount++;
176    return v;
177}
178
179
180/*
181 * create a new edge from the given head, tail and credential
182 */
183
184static abac_edge_t *_get_edge(abac_vertex_t *h, abac_vertex_t *t,
185        abac_credential_t *c) {
186
187    /* An edge does not own it's vertices.  Do not delete them from an edge
188     * reference. */
189    abac_edge_t *edge = abac_xmalloc(sizeof(abac_edge_t));
190    edge->refcount = 1;
191    edge->vertex = t;
192    edge->reverse_vertex = h;
193    /* Don't dup a null credential */
194    edge->credential = (c) ? abac_credential_dup(c): NULL;
195
196    return edge;
197}
198
199
200/**
201 * Increment the reference count
202 */
203static abac_edge_t *_dup_edge(abac_edge_t *e) {
204    e->refcount++;
205    return e;
206}
207
208/**
209 * Decerement the refcount and free it if this was the last reference.  NB
210 * edges do not own teh vertices, so they must be deleted elsewhere.
211 */
212static void _free_edge(abac_edge_t *edge) {
213    assert(edge && edge->refcount > 0);
214    if ( --edge->refcount > 0) return;
215    if (edge->credential) abac_credential_free(edge->credential);
216    free(edge);
217}
218
219/**
220 * Add a credential to the credential graph.
221 */
222int abac_graph_add_credential(abac_graph_t *graph, abac_credential_t *cred) {
223    abac_vertex_t *head_vertex, *tail_vertex;
224    abac_edge_t *edge;
225
226    assert(cred != NULL);
227
228    abac_role_t *head = abac_credential_head(cred);
229    abac_role_t *tail = abac_credential_tail(cred);
230
231    // a valid credential must have a role for the head
232    if (!abac_role_is_role(head)) return 0;
233
234    head_vertex = _get_vertex(graph, head);
235    tail_vertex = _get_vertex(graph, tail);
236
237    // make sure we don't insert the same edge twice (ugh)
238    abac_list_foreach(head_vertex->edges, edge,
239        if (edge->vertex == tail_vertex) {
240            _free_vertex(head_vertex);
241            _free_vertex(tail_vertex);
242            return 0;
243        }
244    );
245
246    // create the edge and add it
247    edge = _get_edge(head_vertex, tail_vertex, cred);
248
249    abac_list_add(head_vertex->edges, edge);
250    abac_list_add(tail_vertex->reverse_edges, _dup_edge(edge));
251
252    // must re-derive edges
253    graph->dirty = 1;
254
255    return 1;
256}
257
258// find the principals that have a role
259static abac_set_t *_find_principals(abac_graph_t *graph, abac_vertex_t *start_vertex) {
260    abac_set_t *principals = abac_set_new();
261
262    abac_list_t *traversal = abac_graph_postorder(graph, start_vertex->role);
263    abac_vertex_t *vertex;
264
265    abac_list_foreach(traversal, vertex,
266        if (abac_role_is_principal(vertex->role))
267            abac_set_add(principals, abac_role_string(vertex->role));
268    );
269
270    abac_list_free(traversal);
271    return principals;
272}
273
274// remove any derived edges from the graph
275void _clear_derived(abac_graph_t *graph) {
276    abac_derived_t *current;
277
278    while (graph->derived) {
279        current = graph->derived;
280
281        HASH_DEL(graph->derived, current);
282
283        abac_vertex_t *head = current->key.head;
284        abac_edge_t *tail = current->key.tail;
285        assert(tail->credential == NULL);
286
287        // this can fail, but we assume the data structures are consistent
288        abac_list_remove(head->edges, tail);
289        abac_list_remove(tail->reverse_vertex->edges, tail);
290
291        free(current);
292        _free_edge(tail);
293    }
294}
295
296// add a derived edge, returns 1 if added 0 if dup
297static int _derived_edge(abac_graph_t *graph, abac_vertex_t *head, abac_vertex_t *tail) {
298    abac_edge_t *edge;
299
300    // don't add duplicate edges
301    abac_list_foreach(head->edges, edge,
302        if (edge->vertex == tail)
303            return 0;
304    );
305
306    debug_printf("derived edge %s <- %s\n", head->name, tail->name);
307
308    edge = _get_edge(head, tail, NULL);
309    abac_list_add(head->edges, edge);
310    abac_list_add(tail->reverse_edges, _dup_edge(edge));
311
312    // add to list of derived edges
313    abac_derived_t *derived = abac_xmalloc(sizeof(abac_derived_t));
314    derived->key.head = head;
315    derived->key.tail = edge;
316    HASH_ADD(hh, graph->derived, key, sizeof(abac_derived_key_t), derived);
317
318    return 1;
319}
320
321// find a vertex by name
322abac_vertex_t *_find_vertex(abac_graph_t *graph, char *name) {
323    abac_vertex_t *ret = NULL;
324    HASH_FIND_STR(graph->vertices, name, ret);
325    return ret;
326}
327
328/**
329 * Single iteration of deriving new edges. Returns the number of new edges
330 * added.
331 */
332static int _derive_links_iter(abac_graph_t *graph) {
333    int count = 0;
334    abac_vertex_t *vertex;
335
336    for (vertex = graph->vertices; vertex != NULL; vertex = vertex->hh.next) {
337        // intersection
338        if (abac_role_is_intersection(vertex->role)) {
339            // for each prereq edge:
340            //     find principals that have the edge
341            // find intersection of all sets
342            // for each principal B in intersection:
343            //     add link
344
345            char *name;
346            abac_vertex_t *prereq;
347            abac_set_t *principals = NULL;
348
349            abac_list_foreach(vertex->prereqs, prereq,
350                abac_set_t *cur = _find_principals(graph, prereq);
351
352                if (principals == NULL)
353                    principals = cur;
354                else {
355                    abac_set_intersect(principals, cur);
356                    abac_set_free(cur);
357                }
358
359                if (abac_set_size(principals) == 0)
360                    goto isect_done;
361            );
362
363            abac_list_t *prin_names = abac_set_elements(principals);
364            abac_list_foreach(prin_names, name,
365                abac_vertex_t *principal = _find_vertex(graph, name);
366                count += _derived_edge(graph, vertex, principal);
367            );
368
369            abac_list_free(prin_names);
370isect_done:
371            abac_set_free(principals);
372        }
373
374        // linking role
375        else if (abac_role_is_linking(vertex->role)) {
376            // linking roles take the form A.r1.r2
377            char *A_r1 = abac_role_linked_role(vertex->role);
378            char *r2 = abac_role_role_name(vertex->role);
379
380            // find the linked role in the graph
381            abac_vertex_t *A_r1_vertex;
382            HASH_FIND_STR(graph->vertices, A_r1, A_r1_vertex);
383            if (A_r1_vertex == NULL)
384                continue;
385
386            // find the principals that have A.r1
387            abac_set_t *principals = _find_principals(graph, A_r1_vertex);
388            char *B;
389
390            abac_list_t *elts = abac_set_elements(principals);
391
392            // and add a link for each B.r2 to A.r1.r2
393            abac_list_foreach(elts, B,
394                int B_len = strlen(B);
395                int r2_len = strlen(r2);
396
397                // create the string B.r2, thx C
398                char *B_r2 = malloc(B_len + r2_len + 2);
399                memcpy(B_r2, B, B_len);
400                B_r2[B_len] = '.';
401                memcpy(B_r2 + B_len + 1, r2, r2_len);
402                B_r2[B_len + r2_len + 1] = 0;
403
404                // add an edge if the principal's granted it to someone
405                abac_vertex_t *B_r2_vertex = _find_vertex(graph, B_r2);
406                if (B_r2_vertex) {
407                    debug_printf("adding edge from %s to %s\n", B_r2, abac_role_string(vertex->role));
408                    count += _derived_edge(graph, vertex, B_r2_vertex);
409                }
410
411#ifdef DEBUG
412                debug_printf("    incoming edges for %s\n", abac_role_string(vertex->role));
413                abac_edge_t *cur;
414                abac_list_foreach(vertex->edges, cur,
415                    debug_printf("        %s (%s)\n", abac_role_string(cur->vertex->role), cur->vertex->name);
416                );
417#endif
418
419                free(B_r2);
420            );
421
422            abac_list_free(elts);
423            abac_set_free(principals);
424        }
425    }
426
427    return count;
428}
429
430/**
431 * Derive all implied edges in the graph. These can come from linking roles
432 * and intersections.
433 *
434 * We have to do it iteratively because derived edges can imply new edges.
435 */
436void abac_graph_derive_links(abac_graph_t *graph) {
437    if (!graph->dirty)
438        return;
439
440    // iterate as long as new links are derived
441    while (_derive_links_iter(graph) > 0)
442        ;
443
444    graph->dirty = 0;
445}
446
447static void _reverse_order_recurse(abac_vertex_t *vertex, abac_set_t *seen, int preorder, abac_list_t *stack) {
448    abac_edge_t *outgoing;
449
450    // don't revisit nodes
451    if (!abac_set_add(seen, abac_role_string(vertex->role)))
452        return;
453
454    if (preorder)
455        abac_list_add(stack, vertex);
456
457    // recurse along the incoming vertices
458    abac_list_foreach(vertex->reverse_edges, outgoing,
459        _reverse_order_recurse(outgoing->reverse_vertex, seen, preorder, stack);
460    );
461
462    if (!preorder)
463        abac_list_add(stack, vertex);
464}
465
466static abac_list_t *_reverse_order(abac_graph_t *graph, abac_role_t *start, int preorder) {
467    debug_printf("%sorder at %s\n", preorder ? "pre" : "post", abac_role_string(start));
468
469    abac_vertex_t *start_vertex = _get_vertex(graph, start);
470    abac_set_t *seen = abac_set_new();
471
472    // create the return list
473    abac_list_t *stack = abac_list_new();
474
475    _reverse_order_recurse(start_vertex, seen, preorder, stack);
476
477    abac_set_free(seen);
478
479    return stack;
480}
481
482static void _order_recurse(abac_vertex_t *vertex, abac_set_t *seen, int preorder, abac_list_t *stack) {
483    abac_edge_t *incoming;
484
485    // don't revisit nodes
486    if (!abac_set_add(seen, abac_role_string(vertex->role)))
487        return;
488
489    if (preorder)
490        abac_list_add(stack, vertex);
491
492    // recurse along the incoming vertices
493    abac_list_foreach(vertex->edges, incoming,
494        _order_recurse(incoming->vertex, seen, preorder, stack);
495    );
496
497    if (!preorder)
498        abac_list_add(stack, vertex);
499}
500
501static abac_list_t *_order(abac_graph_t *graph, abac_role_t *start, int preorder) {
502    debug_printf("%sorder at %s\n", preorder ? "pre" : "post", abac_role_string(start));
503
504    abac_vertex_t *start_vertex = _get_vertex(graph, start);
505    abac_set_t *seen = abac_set_new();
506
507    // create the return list
508    abac_list_t *stack = abac_list_new();
509
510    _order_recurse(start_vertex, seen, preorder, stack);
511
512    abac_set_free(seen);
513
514    return stack;
515}
516
517abac_list_t *abac_graph_postorder(abac_graph_t *graph, abac_role_t *start) {
518    return _order(graph, start, 0);
519}
520
521/**
522 * Postorder traverse the graph and return all the credentials within.
523 */
524abac_list_t *abac_graph_postorder_credentials(abac_graph_t *graph, char *start) {
525    abac_vertex_t *vertex;
526    abac_edge_t *incoming;
527
528    // get the postorder of vertices
529    abac_role_t *role = abac_role_from_string(start);
530    abac_list_t *order = abac_graph_postorder(graph, role);
531
532    // go through the list and dup all the credentials
533    abac_list_t *credentials = abac_list_new();
534    abac_list_foreach(order, vertex,
535        abac_list_foreach(vertex->edges, incoming,
536            if (incoming->credential != NULL)
537                abac_list_add(credentials, abac_credential_dup(incoming->credential));
538        );
539    );
540
541    abac_role_free(role);
542    abac_list_free(order);
543
544    return credentials;
545}
546
547
548abac_list_t *abac_graph_postorder_reverse(abac_graph_t *graph, abac_role_t *start) {
549    return _reverse_order(graph, start, 0);
550}
551
552/**
553 * Postorder traverse the graph and return all the credentials within.
554 */
555abac_list_t *abac_graph_postorder_reverse_credentials(abac_graph_t *graph, char *start) {
556    abac_vertex_t *vertex;
557    abac_edge_t *outgoing;
558
559    // get the postorder of vertices
560    abac_role_t *role = abac_role_from_string(start);
561    abac_list_t *order = abac_graph_postorder_reverse(graph, role);
562
563    // go through the list and dup all the credentials
564    abac_list_t *credentials = abac_list_new();
565    abac_list_foreach(order, vertex,
566        abac_list_foreach(vertex->reverse_edges, outgoing,
567            if (outgoing->credential != NULL)
568                abac_list_add(credentials, abac_credential_dup(outgoing->credential));
569        );
570    );
571
572    abac_role_free(role);
573    abac_list_free(order);
574
575    return credentials;
576}
577
578static void _query(abac_graph_t *graph, char *role_name, char *principal, abac_graph_t *return_graph) {
579    abac_vertex_t *vertex;
580    abac_edge_t *incoming;
581
582    abac_role_t *role = abac_role_from_string(role_name);
583    abac_role_t *prin_role = abac_role_from_string(principal);
584
585    // give up on bogus roles
586    if (role == NULL || prin_role == NULL) {
587        free(role);
588        free(prin_role);
589        return;
590    }
591
592    abac_set_t *on_path = abac_set_new();
593    abac_set_add(on_path, abac_role_string(prin_role));
594
595    abac_list_t *traversal = abac_graph_postorder(graph, role);
596    abac_list_foreach(traversal, vertex,
597        abac_role_t *role = vertex->role;
598
599        abac_list_foreach(vertex->edges, incoming,
600            abac_role_t *incoming_role = incoming->vertex->role;
601
602            if (!abac_set_contains(on_path, abac_role_string(incoming_role)))
603                continue;
604
605            abac_set_add(on_path, abac_role_string(role));
606
607            // get implying edges for intersection vertices
608            if (abac_role_is_intersection(role)) {
609                abac_vertex_t *prereq;
610                abac_list_foreach(vertex->prereqs, prereq,
611                    _query(graph, prereq->name, principal, return_graph);
612                );
613            }
614
615            // recursively find linked roles
616            else if (abac_role_is_linking(role)) {
617                char *linked_role = abac_role_linked_role(role);
618                char *principal = abac_role_principal(incoming_role);
619
620                _query(graph, linked_role, principal, return_graph);
621            }
622
623            // add non-derived edges to the proof graph
624            else
625                abac_graph_add_credential(return_graph, incoming->credential);
626        );
627    );
628
629    abac_list_free(traversal);
630    abac_set_free(on_path);
631    abac_role_free(role);
632    abac_role_free(prin_role);
633}
634
635abac_graph_t *abac_graph_query(abac_graph_t *graph, char *role, char *principal) {
636    abac_graph_derive_links(graph);
637
638    abac_graph_t *return_graph = abac_graph_new();
639    _query(graph, role, principal, return_graph);
640    abac_graph_derive_links(return_graph);
641    return return_graph;
642}
643
644abac_graph_t *abac_graph_principal_creds(abac_graph_t *graph, char *principal) {
645    abac_graph_derive_links(graph);
646    abac_graph_t *result_graph = abac_graph_new();
647    abac_list_t *result = abac_graph_postorder_reverse_credentials(graph, 
648            principal);
649    abac_credential_t *cur = NULL;
650    abac_list_foreach(result, cur,
651        abac_graph_add_credential(result_graph, cur);
652    );
653    abac_list_free(result);
654    /* For each terminal role that the principal can reach, roll a proof into
655       the result_graph. */
656    abac_vertex_t *vertex = NULL;
657    for (vertex = result_graph->vertices; vertex != NULL; 
658            vertex = vertex->hh.next) {
659        if ( abac_list_size(vertex->reverse_edges) == 0) 
660            _query(graph, vertex->name, principal, result_graph);
661    }
662    abac_graph_derive_links(result_graph);
663    return result_graph;
664}
665
666
667/**
668 * Get all the credentials (attribute/issuer cert pairs) from the graph.
669 */
670abac_list_t *abac_graph_credentials(abac_graph_t *graph) {
671    abac_list_t *credentials = abac_list_new();
672
673    abac_vertex_t *vertex;
674
675    for (vertex = graph->vertices; vertex != NULL; vertex = vertex->hh.next) {
676        abac_edge_t *edge;
677        abac_list_foreach(vertex->edges, edge,
678            if (edge->credential != NULL)
679                abac_list_add(credentials, abac_credential_dup(edge->credential));
680        );
681    }
682
683    return credentials;
684}
685
686void abac_graph_free(abac_graph_t *graph) {
687    abac_vertex_t *vertex;
688    abac_edge_t *edge;
689
690    // kill derived edges
691    _clear_derived(graph);
692
693    // delete vertices
694    while ((vertex = graph->vertices) != NULL) {
695        HASH_DEL(graph->vertices, vertex);
696        _free_vertex(vertex);
697    }
698
699    free(graph);
700}
701
702abac_role_t *abac_vertex_role(abac_vertex_t *vertex) {
703    return vertex->role;
704}
Note: See TracBrowser for help on using the repository browser.