source: libabac/abac_graph.c @ aba6e07

abac0-leakabac0-meicompt_changesgec13mei-idmei-rt0-nmei_rt0mei_rt2mei_rt2_fix_1meiyap-rt1meiyap1rt2tvf-new-xml
Last change on this file since aba6e07 was aba6e07, checked in by Mike Ryan <mikeryan@…>, 14 years ago

fix memory leak: list of prereq vertices on intersection node was not
freed

  • Property mode set to 100644
File size: 14.8 KB
RevLine 
[d4cbf71]1#include <assert.h>
[379a53f]2#include <stdlib.h>
[d4cbf71]3
[06293d1]4#include "abac_graph.h"
[cb0f2b2]5
[2fd24c7]6#include "abac_set.h"
[3c251d0]7#include "abac_util.h"
[d4cbf71]8
[f46412c]9#include "uthash.h"
10
11// vertex
[06293d1]12struct _abac_vertex_t {
[1743825]13    abac_role_t *role;
[f46412c]14    char *name;
15
[6d5623e]16    abac_list_t *edges;
[f46412c]17
[0eb4a8e]18    // only relevant to intersection edges
19    abac_list_t *prereqs;
20
[f46412c]21    UT_hash_handle hh;
22};
23
[ae72c5d]24// edge
[06293d1]25typedef struct _abac_edge_t {
26    abac_vertex_t *vertex;
[401a054]27    abac_credential_t *credential;
[06293d1]28} abac_edge_t;
[ae72c5d]29
[f46412c]30// derived edge
[06293d1]31typedef struct _abac_derived_key_t {
32    abac_vertex_t *head;
33    abac_edge_t *tail;
34} abac_derived_key_t;
[f46412c]35
[06293d1]36typedef struct _abac_derived_t {
37    abac_derived_key_t key;
[f46412c]38    UT_hash_handle hh;
[06293d1]39} abac_derived_t;
[f46412c]40
41// graph
[06293d1]42struct _abac_graph_t {
43    abac_vertex_t *vertices;
44    abac_derived_t *derived;
[f46412c]45    int dirty;
46};
[0ec9e25]47
[0eb4a8e]48// ugghhhghhhghh need this for intersections
[9a411d7]49abac_list_t *abac_role_prereqs(abac_role_t *);
[0eb4a8e]50
[0ec9e25]51/**
52 * Create a new graph.
53 */
[06293d1]54abac_graph_t *abac_graph_new(void) {
[3c251d0]55    abac_graph_t *graph = abac_xmalloc(sizeof(abac_graph_t));
[82aeefa]56
57    graph->vertices = NULL;
[6188f4e]58    graph->derived = NULL;
[82aeefa]59    graph->dirty = 0;
60
61    return graph;
62}
[d4cbf71]63
[b26942d]64/**
65 * Deep copy a graph.
66 */
[06293d1]67abac_graph_t *abac_graph_dup(abac_graph_t *graph) {
68    abac_vertex_t *vertex;
69    abac_edge_t *edge;
[b26942d]70
[06293d1]71    abac_graph_t *clone = abac_graph_new();
[b26942d]72
73    // copy the vertices edge by edge
74    for (vertex = graph->vertices; vertex != NULL; vertex = vertex->hh.next)
[6d5623e]75        abac_list_foreach(vertex->edges, edge,
[b26942d]76            // only copy non-derived edges
[401a054]77            if (edge->credential != NULL)
78                abac_graph_add_credential(clone, edge->credential);
[b26942d]79        );
80
81    return clone;
82}
83
[d4cbf71]84/**
[401a054]85 * Add a vertex to the graph. Should only be called by abac_graph_add_credential.
[d4cbf71]86 */
[06293d1]87static abac_vertex_t *_get_vertex(abac_graph_t *graph, abac_role_t *role) {
88    abac_vertex_t *vertex;
[c28bd8b]89    char *string;
90   
[dcc1a8e]91    string = abac_role_string(role);
[82aeefa]92    HASH_FIND_STR(graph->vertices, string, vertex);
[c28bd8b]93
94    // add the vertex if it doesn't exist
95    if (vertex == NULL) {
[3c251d0]96        vertex = abac_xmalloc(sizeof(abac_vertex_t));
[dcc1a8e]97        vertex->role = abac_role_dup(role);
98        vertex->name = abac_role_string(vertex->role);
[d4cbf71]99
[379a53f]100        // create the list of edges
[6d5623e]101        vertex->edges = abac_list_new();
[d4cbf71]102
[0eb4a8e]103        // for intersections, always NULL on normal vertices
[9a411d7]104        if (abac_role_is_intersection(role)) {
105            abac_role_t *prereq;
106            vertex->prereqs = abac_list_new();
107
108            // add each prereq to the vertex
109            abac_list_foreach(abac_role_prereqs(role), prereq,
110                abac_vertex_t *tail_vertex = _get_vertex(graph, prereq);
111                abac_list_add(vertex->prereqs, tail_vertex);
112            );
113        }
[0eb4a8e]114
[9a411d7]115        // normal edges have no prereqs
116        else
117            vertex->prereqs = NULL;
[0eb4a8e]118
[c28bd8b]119        // add it to the vertices
[82aeefa]120        HASH_ADD_KEYPTR(hh, graph->vertices, vertex->name, strlen(vertex->name), vertex);
[c28bd8b]121    }
[d4cbf71]122
123    return vertex;
124}
125
126/**
[401a054]127 * Add a credential to the credential graph.
[d4cbf71]128 */
[401a054]129int abac_graph_add_credential(abac_graph_t *graph, abac_credential_t *cred) {
[06293d1]130    abac_vertex_t *head_vertex, *tail_vertex;
[314869f]131    abac_edge_t *edge;
[d4cbf71]132
[401a054]133    assert(cred != NULL);
[ae72c5d]134
[401a054]135    abac_role_t *head = abac_credential_head(cred);
136    abac_role_t *tail = abac_credential_tail(cred);
[d4cbf71]137
[401a054]138    // a valid credential must have a role for the head
[dcc1a8e]139    if (!abac_role_is_role(head)) return 0;
[d4cbf71]140
[82aeefa]141    head_vertex = _get_vertex(graph, head);
[9a411d7]142    tail_vertex = _get_vertex(graph, tail);
[d4cbf71]143
[314869f]144    // make sure we don't insert the same edge twice (ugh)
145    abac_list_foreach(head_vertex->edges, edge,
146        if (edge->vertex == tail_vertex)
147            return 0;
148    );
149
[ae72c5d]150    // create the edge and add it
[314869f]151    edge = abac_xmalloc(sizeof(abac_edge_t));
[ae72c5d]152    edge->vertex = tail_vertex;
[401a054]153    edge->credential = abac_credential_dup(cred);
[ae72c5d]154
[6d5623e]155    abac_list_add(head_vertex->edges, edge);
[d4cbf71]156
[82aeefa]157    // must re-derive edges
158    graph->dirty = 1;
159
[d4cbf71]160    return 1;
161}
162
[ebde9dd]163// find the principals that have a role
[06293d1]164static abac_set_t *_find_principals(abac_graph_t *graph, abac_vertex_t *start_vertex) {
[2fd24c7]165    abac_set_t *principals = abac_set_new();
[d4cbf71]166
[06293d1]167    abac_list_t *traversal = abac_graph_postorder(graph, start_vertex->role);
168    abac_vertex_t *vertex;
[d4cbf71]169
[6d5623e]170    abac_list_foreach(traversal, vertex,
[9a411d7]171        if (abac_role_is_principal(vertex->role))
172            abac_set_add(principals, abac_role_string(vertex->role));
[ebde9dd]173    );
[d4cbf71]174
[6d5623e]175    abac_list_free(traversal);
[ebde9dd]176    return principals;
177}
[d4cbf71]178
[6188f4e]179// remove any derived edges from the graph
[06293d1]180void _clear_derived(abac_graph_t *graph) {
181    abac_derived_t *current;
[6188f4e]182
183    while (graph->derived) {
184        current = graph->derived;
185
186        HASH_DEL(graph->derived, current);
187
[06293d1]188        abac_vertex_t *head = current->key.head;
189        abac_edge_t *tail = current->key.tail;
[401a054]190        assert(tail->credential == NULL);
[6188f4e]191
192        // this can fail, but we assume the data structures are consistent
[6d5623e]193        abac_list_remove(head->edges, tail);
[6188f4e]194
195        free(current);
[186cb75]196        free(tail);
[6188f4e]197    }
198}
199
[704fde7]200// add a derived edge, returns 1 if added 0 if dup
201static int _derived_edge(abac_graph_t *graph, abac_vertex_t *head, abac_vertex_t *tail) {
202    abac_edge_t *edge;
203
204    // don't add duplicate edges
205    abac_list_foreach(head->edges, edge,
206        if (edge->vertex == tail)
207            return 0;
208    );
209
210    debug_printf("derived edge %s <- %s\n", head->name, tail->name);
211
212    edge = abac_xmalloc(sizeof(abac_edge_t));
[e2a0f26]213    edge->vertex = tail;
214    edge->credential = NULL;
215    abac_list_add(head->edges, edge);
216
217    // add to list of derived edges
218    abac_derived_t *derived = abac_xmalloc(sizeof(abac_derived_t));
219    derived->key.head = head;
220    derived->key.tail = edge;
221    HASH_ADD(hh, graph->derived, key, sizeof(abac_derived_key_t), derived);
[704fde7]222
223    return 1;
[e2a0f26]224}
225
226// find a vertex by name
227abac_vertex_t *_find_vertex(abac_graph_t *graph, char *name) {
228    abac_vertex_t *ret = NULL;
229    HASH_FIND_STR(graph->vertices, name, ret);
230    return ret;
231}
232
[704fde7]233/**
234 * Single iteration of deriving new edges. Returns the number of new edges
235 * added.
236 */
237static int _derive_links_iter(abac_graph_t *graph) {
238    int count = 0;
[06293d1]239    abac_vertex_t *vertex;
[d4cbf71]240
[ebde9dd]241    for (vertex = graph->vertices; vertex != NULL; vertex = vertex->hh.next) {
[0eb4a8e]242        // intersection
[9a411d7]243        if (abac_role_is_intersection(vertex->role)) {
[0eb4a8e]244            // for each prereq edge:
245            //     find principals that have the edge
246            // find intersection of all sets
247            // for each principal B in intersection:
248            //     add link
[e2a0f26]249
250            char *name;
251            abac_vertex_t *prereq;
252            abac_set_t *principals = NULL;
253
254            abac_list_foreach(vertex->prereqs, prereq,
255                abac_set_t *cur = _find_principals(graph, prereq);
256
257                if (principals == NULL)
258                    principals = cur;
259                else {
260                    abac_set_intersect(principals, cur);
261                    abac_set_free(cur);
262                }
263
264                if (abac_set_size(principals) == 0)
265                    goto isect_done;
266            );
267
268            abac_list_t *prin_names = abac_set_elements(principals);
269            abac_list_foreach(prin_names, name,
270                abac_vertex_t *principal = _find_vertex(graph, name);
[704fde7]271                count += _derived_edge(graph, vertex, principal);
[e2a0f26]272            );
273
274            abac_list_free(prin_names);
275isect_done:
276            abac_set_free(principals);
[0eb4a8e]277        }
278
279        // linking role
280        else if (abac_role_is_linking(vertex->role)) {
281            // linking roles take the form A.r1.r2
282            char *A_r1 = abac_role_linked_role(vertex->role);
283            char *r2 = abac_role_role_name(vertex->role);
284
285            // find the linked role in the graph
286            abac_vertex_t *A_r1_vertex;
287            HASH_FIND_STR(graph->vertices, A_r1, A_r1_vertex);
288            if (A_r1_vertex == NULL)
289                continue;
290
291            // find the principals that have A.r1
292            abac_set_t *principals = _find_principals(graph, A_r1_vertex);
293            char *B;
294
295            abac_list_t *elts = abac_set_elements(principals);
296
297            // and add a link for each B.r2 to A.r1.r2
298            abac_list_foreach(elts, B,
299                int B_len = strlen(B);
300                int r2_len = strlen(r2);
301
302                // create the string B.r2, thx C
303                char *B_r2 = malloc(B_len + r2_len + 2);
304                memcpy(B_r2, B, B_len);
305                B_r2[B_len] = '.';
306                memcpy(B_r2 + B_len + 1, r2, r2_len);
307                B_r2[B_len + r2_len + 1] = 0;
308
309                // add an edge if the principal's granted it to someone
[e2a0f26]310                abac_vertex_t *B_r2_vertex = _find_vertex(graph, B_r2);
[0eb4a8e]311                if (B_r2_vertex) {
312                    debug_printf("adding edge from %s to %s\n", B_r2, abac_role_string(vertex->role));
[704fde7]313                    count += _derived_edge(graph, vertex, B_r2_vertex);
[0eb4a8e]314                }
[ebde9dd]315
[dbbf777]316#ifdef DEBUG
[0eb4a8e]317                debug_printf("    incoming edges for %s\n", abac_role_string(vertex->role));
318                abac_edge_t *cur;
319                abac_list_foreach(vertex->edges, cur,
320                    debug_printf("        %s (%s)\n", abac_role_string(cur->vertex->role), cur->vertex->name);
321                );
[dbbf777]322#endif
[d4cbf71]323
[0eb4a8e]324                free(B_r2);
325            );
[ebde9dd]326
[0eb4a8e]327            abac_list_free(elts);
328            abac_set_free(principals);
329        }
[ebde9dd]330    }
[6188f4e]331
[704fde7]332    return count;
333}
334
335/**
336 * Derive all implied edges in the graph. These can come from linking roles
337 * and intersections.
338 *
339 * We have to do it iteratively because derived edges can imply new edges.
340 */
341void abac_graph_derive_links(abac_graph_t *graph) {
342    if (!graph->dirty)
343        return;
344
345    // iterate as long as new links are derived
346    while (_derive_links_iter(graph) > 0)
347        ;
348
[6188f4e]349    graph->dirty = 0;
[d4cbf71]350}
[cb0f2b2]351
[06293d1]352static void _order_recurse(abac_vertex_t *vertex, abac_set_t *seen, int preorder, abac_list_t *stack) {
353    abac_edge_t *incoming;
[cb0f2b2]354
355    // don't revisit nodes
[9a411d7]356    if (!abac_set_add(seen, abac_role_string(vertex->role)))
[cb0f2b2]357        return;
358
359    if (preorder)
[6d5623e]360        abac_list_add(stack, vertex);
[cb0f2b2]361
[9536712]362    // recurse along the incoming vertices
[6d5623e]363    abac_list_foreach(vertex->edges, incoming,
[ae72c5d]364        _order_recurse(incoming->vertex, seen, preorder, stack);
[379a53f]365    );
[cb0f2b2]366
367    if (!preorder)
[6d5623e]368        abac_list_add(stack, vertex);
[cb0f2b2]369}
370
[06293d1]371static abac_list_t *_order(abac_graph_t *graph, abac_role_t *start, int preorder) {
[dcc1a8e]372    debug_printf("%sorder at %s\n", preorder ? "pre" : "post", abac_role_string(start));
[ebde9dd]373
[06293d1]374    abac_vertex_t *start_vertex = _get_vertex(graph, start);
[2fd24c7]375    abac_set_t *seen = abac_set_new();
[cb0f2b2]376
[379a53f]377    // create the return list
[6d5623e]378    abac_list_t *stack = abac_list_new();
[cb0f2b2]379
380    _order_recurse(start_vertex, seen, preorder, stack);
381
[2fd24c7]382    abac_set_free(seen);
[be963dc]383
[cb0f2b2]384    return stack;
385}
386
[06293d1]387abac_list_t *abac_graph_postorder(abac_graph_t *graph, abac_role_t *start) {
[82aeefa]388    return _order(graph, start, 0);
[cb0f2b2]389}
[97a5e13]390
[4e426c9]391/**
392 * Postorder traverse the graph and return all the credentials within.
393 */
394abac_list_t *abac_graph_postorder_credentials(abac_graph_t *graph, char *start) {
395    abac_vertex_t *vertex;
396    abac_edge_t *incoming;
397
398    // get the postorder of vertices
399    abac_role_t *role = abac_role_from_string(start);
400    abac_list_t *order = abac_graph_postorder(graph, role);
401
402    // go through the list and dup all the credentials
403    abac_list_t *credentials = abac_list_new();
404    abac_list_foreach(order, vertex,
405        abac_list_foreach(vertex->edges, incoming,
406            if (incoming->credential != NULL)
407                abac_list_add(credentials, abac_credential_dup(incoming->credential));
408        );
409    );
410
411    abac_role_free(role);
412    abac_list_free(order);
413
414    return credentials;
415}
416
[401a054]417static void _query(abac_graph_t *graph, char *role_name, char *principal, abac_graph_t *return_graph) {
[06293d1]418    abac_vertex_t *vertex;
419    abac_edge_t *incoming;
[97a5e13]420
[401a054]421    abac_role_t *role = abac_role_from_string(role_name);
[dcc1a8e]422    abac_role_t *prin_role = abac_role_from_string(principal);
[97a5e13]423
[675cbea]424    // give up on bogus roles
425    if (role == NULL || prin_role == NULL) {
426        free(role);
427        free(prin_role);
428        return;
429    }
430
[2fd24c7]431    abac_set_t *on_path = abac_set_new();
[dcc1a8e]432    abac_set_add(on_path, abac_role_string(prin_role));
[97a5e13]433
[401a054]434    abac_list_t *traversal = abac_graph_postorder(graph, role);
[6d5623e]435    abac_list_foreach(traversal, vertex,
[1743825]436        abac_role_t *role = vertex->role;
[97a5e13]437
[6d5623e]438        abac_list_foreach(vertex->edges, incoming,
[1743825]439            abac_role_t *incoming_role = incoming->vertex->role;
[97a5e13]440
[9a411d7]441            if (!abac_set_contains(on_path, abac_role_string(incoming_role)))
[ae72c5d]442                continue;
[97a5e13]443
[9a411d7]444            abac_set_add(on_path, abac_role_string(role));
[97a5e13]445
[ebe824c]446            // get implying edges for intersection vertices
[9a411d7]447            if (abac_role_is_intersection(role)) {
[ebe824c]448                abac_vertex_t *prereq;
449                abac_list_foreach(vertex->prereqs, prereq,
450                    _query(graph, prereq->name, principal, return_graph);
451                );
[e2a0f26]452            }
[97a5e13]453
454            // recursively find linked roles
[e2a0f26]455            else if (abac_role_is_linking(role)) {
[401a054]456                char *linked_role = abac_role_linked_role(role);
[dcc1a8e]457                char *principal = abac_role_principal(incoming_role);
[97a5e13]458
[401a054]459                _query(graph, linked_role, principal, return_graph);
[97a5e13]460            }
[e2a0f26]461
462            // add non-derived edges to the proof graph
463            else
464                abac_graph_add_credential(return_graph, incoming->credential);
[97a5e13]465        );
466    );
467
[6d5623e]468    abac_list_free(traversal);
[2fd24c7]469    abac_set_free(on_path);
[401a054]470    abac_role_free(role);
[dcc1a8e]471    abac_role_free(prin_role);
[97a5e13]472}
473
[401a054]474abac_graph_t *abac_graph_query(abac_graph_t *graph, char *role, char *principal) {
[06293d1]475    abac_graph_derive_links(graph);
[97a5e13]476
[06293d1]477    abac_graph_t *return_graph = abac_graph_new();
[401a054]478    _query(graph, role, principal, return_graph);
[06293d1]479    abac_graph_derive_links(return_graph);
[97a5e13]480    return return_graph;
481}
[be963dc]482
[902d079]483/**
[401a054]484 * Get all the credentials (attribute/issuer cert pairs) from the graph.
[902d079]485 */
[401a054]486abac_list_t *abac_graph_credentials(abac_graph_t *graph) {
487    abac_list_t *credentials = abac_list_new();
[902d079]488
[06293d1]489    abac_vertex_t *vertex;
[902d079]490
491    for (vertex = graph->vertices; vertex != NULL; vertex = vertex->hh.next) {
[06293d1]492        abac_edge_t *edge;
[6d5623e]493        abac_list_foreach(vertex->edges, edge,
[401a054]494            if (edge->credential != NULL)
495                abac_list_add(credentials, abac_credential_dup(edge->credential));
[902d079]496        );
497    }
498
[401a054]499    return credentials;
[902d079]500}
501
[06293d1]502void abac_graph_free(abac_graph_t *graph) {
503    abac_vertex_t *vertex;
504    abac_edge_t *edge;
[be963dc]505
506    // kill derived edges
507    _clear_derived(graph);
508
509    // delete vertices
510    while ((vertex = graph->vertices) != NULL) {
511        HASH_DEL(graph->vertices, vertex);
512
[dcc1a8e]513        abac_role_free(vertex->role);
[186cb75]514
[6d5623e]515        abac_list_foreach(vertex->edges, edge,
[401a054]516            if (edge->credential != NULL)
517                abac_credential_free(edge->credential);
[186cb75]518            free(edge);
519        );
[6d5623e]520        abac_list_free(vertex->edges);
[aba6e07]521
522        // the prereq vertices will be freed by the outer while loop
523        if (vertex->prereqs != NULL)
524            abac_list_free(vertex->prereqs);
525
[be963dc]526        free(vertex);
527    }
528
529    free(graph);
530}
[f46412c]531
[06293d1]532abac_role_t *abac_vertex_role(abac_vertex_t *vertex) {
[f46412c]533    return vertex->role;
534}
Note: See TracBrowser for help on using the repository browser.