source: libabac/abac_graph.c @ 9a411d7

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

fold intersection into ordinary role object

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