source: libabac/abac_graph.c @ 63dcd99

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

1) fixed double free

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