source: libabac/abac_graph.c @ a0772a2

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

elegantly handle badly formatted roles in abac query

  • Property mode set to 100644
File size: 11.4 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
16    abac_list_t *edges;
17
18    UT_hash_handle hh;
19};
20
21// edge
22typedef struct _abac_edge_t {
23    abac_vertex_t *vertex;
24    abac_credential_t *credential;
25} abac_edge_t;
26
27// derived edge
28typedef struct _abac_derived_key_t {
29    abac_vertex_t *head;
30    abac_edge_t *tail;
31} abac_derived_key_t;
32
33typedef struct _abac_derived_t {
34    abac_derived_key_t key;
35    UT_hash_handle hh;
36} abac_derived_t;
37
38// graph
39struct _abac_graph_t {
40    abac_vertex_t *vertices;
41    abac_derived_t *derived;
42    int dirty;
43};
44
45/**
46 * Create a new graph.
47 */
48abac_graph_t *abac_graph_new(void) {
49    abac_graph_t *graph = abac_xmalloc(sizeof(abac_graph_t));
50
51    graph->vertices = NULL;
52    graph->derived = NULL;
53    graph->dirty = 0;
54
55    return graph;
56}
57
58/**
59 * Deep copy a graph.
60 */
61abac_graph_t *abac_graph_dup(abac_graph_t *graph) {
62    abac_vertex_t *vertex;
63    abac_edge_t *edge;
64
65    abac_graph_t *clone = abac_graph_new();
66
67    // copy the vertices edge by edge
68    for (vertex = graph->vertices; vertex != NULL; vertex = vertex->hh.next)
69        abac_list_foreach(vertex->edges, edge,
70            // only copy non-derived edges
71            if (edge->credential != NULL)
72                abac_graph_add_credential(clone, edge->credential);
73        );
74
75    return clone;
76}
77
78/**
79 * Add a vertex to the graph. Should only be called by abac_graph_add_credential.
80 */
81static abac_vertex_t *_get_vertex(abac_graph_t *graph, abac_role_t *role) {
82    abac_vertex_t *vertex;
83    char *string;
84   
85    string = abac_role_string(role);
86    HASH_FIND_STR(graph->vertices, string, vertex);
87
88    // add the vertex if it doesn't exist
89    if (vertex == NULL) {
90        vertex = abac_xmalloc(sizeof(abac_vertex_t));
91        vertex->role = abac_role_dup(role);
92        vertex->name = abac_role_string(vertex->role);
93
94        // create the list of edges
95        vertex->edges = abac_list_new();
96
97        // add it to the vertices
98        HASH_ADD_KEYPTR(hh, graph->vertices, vertex->name, strlen(vertex->name), vertex);
99    }
100
101    return vertex;
102}
103
104/**
105 * Add a credential to the credential graph.
106 */
107int abac_graph_add_credential(abac_graph_t *graph, abac_credential_t *cred) {
108    abac_vertex_t *head_vertex, *tail_vertex;
109    abac_edge_t *edge;
110
111    assert(cred != NULL);
112
113    abac_role_t *head = abac_credential_head(cred);
114    abac_role_t *tail = abac_credential_tail(cred);
115
116    // a valid credential must have a role for the head
117    if (!abac_role_is_role(head)) return 0;
118
119    head_vertex = _get_vertex(graph, head);
120    tail_vertex = _get_vertex(graph, tail);
121
122    // make sure we don't insert the same edge twice (ugh)
123    abac_list_foreach(head_vertex->edges, edge,
124        if (edge->vertex == tail_vertex)
125            return 0;
126    );
127
128    // create the edge and add it
129    edge = abac_xmalloc(sizeof(abac_edge_t));
130    edge->vertex = tail_vertex;
131    edge->credential = abac_credential_dup(cred);
132
133    abac_list_add(head_vertex->edges, edge);
134
135    // must re-derive edges
136    graph->dirty = 1;
137
138    return 1;
139}
140
141// find the principals that have a role
142static abac_set_t *_find_principals(abac_graph_t *graph, abac_vertex_t *start_vertex) {
143    abac_set_t *principals = abac_set_new();
144
145    abac_list_t *traversal = abac_graph_postorder(graph, start_vertex->role);
146    abac_vertex_t *vertex;
147
148    abac_list_foreach(traversal, vertex,
149        if (abac_role_is_principal(vertex->role))
150            abac_set_add(principals, abac_role_string(vertex->role));
151    );
152
153    abac_list_free(traversal);
154    return principals;
155}
156
157// remove any derived edges from the graph
158void _clear_derived(abac_graph_t *graph) {
159    abac_derived_t *current;
160
161    while (graph->derived) {
162        current = graph->derived;
163
164        HASH_DEL(graph->derived, current);
165
166        abac_vertex_t *head = current->key.head;
167        abac_edge_t *tail = current->key.tail;
168        assert(tail->credential == NULL);
169
170        // this can fail, but we assume the data structures are consistent
171        abac_list_remove(head->edges, tail);
172
173        free(current);
174        free(tail);
175    }
176}
177
178void abac_graph_derive_links(abac_graph_t *graph) {
179    abac_vertex_t *vertex;
180
181    if (!graph->dirty)
182        return;
183
184    for (vertex = graph->vertices; vertex != NULL; vertex = vertex->hh.next) {
185        // we only care about linking roles
186        if (!abac_role_is_linking(vertex->role))
187            continue;
188
189        // linking roles take the form A.r1.r2
190        char *A_r1 = abac_role_linked_role(vertex->role);
191        char *r2 = abac_role_role_name(vertex->role);
192
193        // find the linked role in the graph
194        abac_vertex_t *A_r1_vertex;
195        HASH_FIND_STR(graph->vertices, A_r1, A_r1_vertex);
196        if (A_r1_vertex == NULL)
197            continue;
198
199        // find the principals that have A.r1
200        abac_set_t *principals = _find_principals(graph, A_r1_vertex);
201        char *B;
202
203        abac_list_t *elts = abac_set_elements(principals);
204
205        // and add a link for each B.r2 to A.r1.r2
206        abac_list_foreach(elts, B,
207            int B_len = strlen(B);
208            int r2_len = strlen(r2);
209
210            // create the string B.r2, thx C
211            char *B_r2 = malloc(B_len + r2_len + 2);
212            memcpy(B_r2, B, B_len);
213            B_r2[B_len] = '.';
214            memcpy(B_r2 + B_len + 1, r2, r2_len);
215            B_r2[B_len + r2_len + 1] = 0;
216
217            abac_vertex_t *B_r2_vertex;
218            HASH_FIND_STR(graph->vertices, B_r2, B_r2_vertex);
219
220            // add an edge if the principal's granted it to someone
221            if (B_r2_vertex) {
222                debug_printf("adding edge from %s to %s\n", B_r2, abac_role_string(vertex->role));
223                abac_edge_t *edge = abac_xmalloc(sizeof(abac_edge_t));
224                edge->vertex = B_r2_vertex;
225                edge->credential = NULL;
226                abac_list_add(vertex->edges, edge);
227
228                // add to list of derived edges
229                abac_derived_t *derived = abac_xmalloc(sizeof(abac_derived_t));
230                derived->key.head = vertex;
231                derived->key.tail = edge;
232                HASH_ADD(hh, graph->derived, key, sizeof(abac_derived_key_t), derived);
233            }
234
235#ifdef DEBUG
236            debug_printf("    incoming edges for %s\n", abac_role_string(vertex->role));
237            abac_edge_t *cur;
238            abac_list_foreach(vertex->edges, cur,
239                debug_printf("        %s (%s)\n", abac_role_string(cur->vertex->role), cur->vertex->name);
240            );
241#endif
242
243            free(B_r2);
244        );
245
246        abac_list_free(elts);
247        abac_set_free(principals);
248    }
249
250    graph->dirty = 0;
251}
252
253static void _order_recurse(abac_vertex_t *vertex, abac_set_t *seen, int preorder, abac_list_t *stack) {
254    abac_edge_t *incoming;
255
256    // don't revisit nodes
257    if (!abac_set_add(seen, abac_role_string(vertex->role)))
258        return;
259
260    if (preorder)
261        abac_list_add(stack, vertex);
262
263    // recurse along the incoming vertices
264    abac_list_foreach(vertex->edges, incoming,
265        _order_recurse(incoming->vertex, seen, preorder, stack);
266    );
267
268    if (!preorder)
269        abac_list_add(stack, vertex);
270}
271
272static abac_list_t *_order(abac_graph_t *graph, abac_role_t *start, int preorder) {
273    debug_printf("%sorder at %s\n", preorder ? "pre" : "post", abac_role_string(start));
274
275    abac_vertex_t *start_vertex = _get_vertex(graph, start);
276    abac_set_t *seen = abac_set_new();
277
278    // create the return list
279    abac_list_t *stack = abac_list_new();
280
281    _order_recurse(start_vertex, seen, preorder, stack);
282
283    abac_set_free(seen);
284
285    return stack;
286}
287
288abac_list_t *abac_graph_postorder(abac_graph_t *graph, abac_role_t *start) {
289    return _order(graph, start, 0);
290}
291
292/**
293 * Postorder traverse the graph and return all the credentials within.
294 */
295abac_list_t *abac_graph_postorder_credentials(abac_graph_t *graph, char *start) {
296    abac_vertex_t *vertex;
297    abac_edge_t *incoming;
298
299    // get the postorder of vertices
300    abac_role_t *role = abac_role_from_string(start);
301    abac_list_t *order = abac_graph_postorder(graph, role);
302
303    // go through the list and dup all the credentials
304    abac_list_t *credentials = abac_list_new();
305    abac_list_foreach(order, vertex,
306        abac_list_foreach(vertex->edges, incoming,
307            if (incoming->credential != NULL)
308                abac_list_add(credentials, abac_credential_dup(incoming->credential));
309        );
310    );
311
312    abac_role_free(role);
313    abac_list_free(order);
314
315    return credentials;
316}
317
318static void _query(abac_graph_t *graph, char *role_name, char *principal, abac_graph_t *return_graph) {
319    abac_vertex_t *vertex;
320    abac_edge_t *incoming;
321
322    abac_role_t *role = abac_role_from_string(role_name);
323    abac_role_t *prin_role = abac_role_from_string(principal);
324
325    // give up on bogus roles
326    if (role == NULL || prin_role == NULL) {
327        free(role);
328        free(prin_role);
329        return;
330    }
331
332    abac_set_t *on_path = abac_set_new();
333    abac_set_add(on_path, abac_role_string(prin_role));
334
335    abac_list_t *traversal = abac_graph_postorder(graph, role);
336    abac_list_foreach(traversal, vertex,
337        abac_role_t *role = vertex->role;
338
339        abac_list_foreach(vertex->edges, incoming,
340            abac_role_t *incoming_role = incoming->vertex->role;
341
342            if (!abac_set_contains(on_path, abac_role_string(incoming_role)))
343                continue;
344
345            abac_set_add(on_path, abac_role_string(role));
346
347            // add non-derived edges to the proof graph
348            if (!abac_role_is_linking(role))
349                abac_graph_add_credential(return_graph, incoming->credential);
350
351            // recursively find linked roles
352            else {
353                char *linked_role = abac_role_linked_role(role);
354                char *principal = abac_role_principal(incoming_role);
355
356                _query(graph, linked_role, principal, return_graph);
357            }
358        );
359    );
360
361    abac_list_free(traversal);
362    abac_set_free(on_path);
363    abac_role_free(role);
364    abac_role_free(prin_role);
365}
366
367abac_graph_t *abac_graph_query(abac_graph_t *graph, char *role, char *principal) {
368    abac_graph_derive_links(graph);
369
370    abac_graph_t *return_graph = abac_graph_new();
371    _query(graph, role, principal, return_graph);
372    abac_graph_derive_links(return_graph);
373    return return_graph;
374}
375
376/**
377 * Get all the credentials (attribute/issuer cert pairs) from the graph.
378 */
379abac_list_t *abac_graph_credentials(abac_graph_t *graph) {
380    abac_list_t *credentials = abac_list_new();
381
382    abac_vertex_t *vertex;
383
384    for (vertex = graph->vertices; vertex != NULL; vertex = vertex->hh.next) {
385        abac_edge_t *edge;
386        abac_list_foreach(vertex->edges, edge,
387            if (edge->credential != NULL)
388                abac_list_add(credentials, abac_credential_dup(edge->credential));
389        );
390    }
391
392    return credentials;
393}
394
395void abac_graph_free(abac_graph_t *graph) {
396    abac_vertex_t *vertex;
397    abac_edge_t *edge;
398
399    // kill derived edges
400    _clear_derived(graph);
401
402    // delete vertices
403    while ((vertex = graph->vertices) != NULL) {
404        HASH_DEL(graph->vertices, vertex);
405
406        abac_role_free(vertex->role);
407
408        abac_list_foreach(vertex->edges, edge,
409            if (edge->credential != NULL)
410                abac_credential_free(edge->credential);
411            free(edge);
412        );
413        abac_list_free(vertex->edges);
414        free(vertex);
415    }
416
417    free(graph);
418}
419
420abac_role_t *abac_vertex_role(abac_vertex_t *vertex) {
421    return vertex->role;
422}
Note: See TracBrowser for help on using the repository browser.