source: libabac/abac_graph.c @ c0fe894

abac0-leak
Last change on this file since c0fe894 was c0fe894, checked in by Mei <mei@…>, 11 years ago

1) take debug statement out

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