source: libabac/abac_xml.c @ 9e063cb

abac0-leakabac0-meimei-idmei-rt0-nmei_rt0tvf-new-xml
Last change on this file since 9e063cb was 9e063cb, checked in by Mei <mei@…>, 11 years ago

1) test out using encrypted private key to generate id credential

  • Property mode set to 100644
File size: 32.5 KB
Line 
1
2/* abac_xml.c, specifically for GENI */
3
4/* in xml, rc in general is, 1 is good, 0 or -1 is bad */
5
6#define _GNU_SOURCE
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <time.h>
11
12#include <libxml/tree.h>
13#include <libxml/xmlmemory.h>
14#include <libxml/parser.h>
15
16#ifndef XMLSEC_NO_XSLT
17#include <libxslt/xslt.h>
18#include <libxslt/security.h>
19#endif /* XMLSEC_NO_XSLT */
20
21#include <xmlsec/xmlsec.h>
22#include <xmlsec/xmltree.h>
23#include <xmlsec/xmldsig.h>
24#include <xmlsec/base64.h>
25#include <xmlsec/templates.h>
26#include <xmlsec/crypto.h>
27#include <xmlsec/list.h>
28
29#include <openssl/sha.h>
30
31/* from Ted's reader */
32
33/* for accessing GENI privilege credentials */
34#define GENI_signed_credential "signed-credential"
35#define GENI_credential "credential"
36#define GENI_type "type"
37#define GENI_serial "serial"
38#define GENI_owner_gid "owner_gid"
39#define GENI_owner_urn "owner_urn"
40#define GENI_target_gid "target_gid"
41#define GENI_target_urn "target_urn"
42#define GENI_uuid "uuid"
43#define GENI_expires "expires"
44#define GENI_privileges "privileges"
45#define GENI_privilege "privilege"
46#define GENI_name "name"
47#define GENI_can_delegate "can_delegate"
48
49/* for accessing GENI abac credentials. signed-credential, credential, type and
50 * expires are present as well.  */
51#define GENI_rt0 "rt0"
52#define GENI_version "version"
53
54/* maximum credential stringlen */
55#define CREDLEN 1024
56/* Maximum version len */
57#define VERSIONLEN 256
58
59/* Call perror on str and exit */
60static void fatal(const char *str) {
61    perror(str);
62    exit(20);
63}
64
65/* Hex dump the contents of buf (length len) to stderr.  For debugging */
66static void dump_it(xmlChar *buf, int len) {
67    int i;
68
69    for (i=0; i < len; i++ ) 
70        fprintf(stderr, "%02x ", buf[i] & 0xff);
71    fprintf(stderr, "\n");
72}
73
74/* Convert a SHA1 hash to text for printing or use in strings.  keyid is the
75 * hash (SHA_DIGEST_LENGTH bytes long) and text is the output string (at least
76 * 2 * SHA_DIGEST_LENGTH +1 bytes long).  The caller is responsible for
77 * allocating and deallocating each of them. */
78static void sha1_to_text(xmlChar *keyid, xmlChar *text) {
79    int i = 0;
80
81    for (i=0; i < SHA_DIGEST_LENGTH; i++) 
82        snprintf((char *) text+2*i, 3, "%02x", keyid[i] & 0xff);
83}
84
85/*
86 * Returns the content pointer of the XML_TEXT node that is the child of the
87 * node passed in.  Node must point to an XML_ELEMENT node.  the return value
88 * is still part of node's XML document, so treat it as read only. */
89static xmlChar *get_element_content(xmlNodePtr node) {
90    xmlNodePtr text = NULL;
91
92    if ( node->type != XML_ELEMENT_NODE ) return NULL;
93    if ( !( text = node->children) ) return NULL;
94    if ( text->type != XML_TEXT_NODE ) return NULL;
95    return text->content;
96}
97
98/*Find the XML element named field that is a at or below node in the XML
99 * document and return a copy of the base64 decoded content of its content.
100 * For example, find key daya in a signature and return it base64decoded.  buf
101 * is allocated and its length is returned in len.  Any values for buf and len
102 * are ignored and overwritten. */
103static int get_base64_field(xmlNodePtr node, xmlChar *field, 
104        xmlChar **buf, int *len) {
105    xmlChar *txt = NULL;
106
107    *buf = NULL;
108
109    if ( !(node = xmlSecFindNode(node, field, xmlSecDSigNs)))
110        goto fail;
111
112    if ( !(txt = get_element_content(node))) 
113            goto fail;
114   
115    *len = strlen((char *) txt);
116    if ( !(*buf = malloc(*len))) 
117        goto fail;
118
119    if ( (*len = xmlSecBase64Decode(txt, *buf, *len)) < 0 ) 
120        goto fail;
121
122    return 1;
123fail:
124    if ( *buf) free(*buf);
125    *len = 0;
126    return 0;
127}
128/* Construct an ASN.1 header for a field of type h that is len bytes long.
129 * Mosttly this creates the proper length encoding under ASN.1's length
130 * encoding rules. buf will contain the new header (and the caller is
131 * responsible for making sure it is at least 6 bytes long, though fewer are
132 * usually used.  The length of the constructed header is returned. This is
133 * used in creating a key hash from key data.*/
134static int make_asn1_header(char h, size_t len, xmlChar *buf) {
135    if ( len > 0x00ffffff) {
136        buf[0] = h;
137        buf[1] = 0x84;
138        buf[2] = (len >> 24) & 0xff;
139        buf[3] = (len >> 16) & 0xff;
140        buf[4] = (len >> 8) & 0xff;
141        buf[5] = (len) & 0xff;
142        return 6;
143    } else if ( len > 0x0000ffff ) {
144        buf[0] = h;
145        buf[1] = 0x83;
146        buf[2] = (len >> 16) & 0xff;
147        buf[3] = (len >> 8) & 0xff;
148        buf[4] = (len) & 0xff;
149        return 5;
150    } else if ( len > 0x000000ff ) {
151        buf[0] = h;
152        buf[1] = 0x82;
153        buf[2] = (len >> 8) & 0xff;
154        buf[3] = (len) & 0xff;
155        return 4;
156    } else if ( len > 0x80 ) {
157        buf[0] = h;
158        buf[1] = 0x81;
159        buf[2] = (len) & 0xff;
160        return 3;
161    } else {
162        buf[0] = h;
163        buf[1] = (len) & 0xff;
164        return 2;
165    }
166}
167
168/* Find the RSA key parameters in the KeyInfo section of the XML document
169 * pointed to by doc, construct the ASN.1 encoding of that key and SHA1 hash
170 * it.    This gives the standard keyid of that key.  keyid will be the binary
171 * encoding of that (the bits of the hash)  sha1_to_text will turn it to text.
172 * keyid must be at least SHA_DIGEST_LENGTH bytes long, and the caller is
173 * responsible for it. This routine returns 1 on success and 0 on failure. */
174static int get_keyid_from_keyinfo(xmlDocPtr doc, xmlChar *keyid) {
175    xmlNodePtr root = NULL; /* XML document root */
176    xmlNodePtr node = NULL; /* Scratch XML node */
177
178    xmlChar b0[20];         /* Header for the sequence */
179    xmlChar b1[20];         /* Header for the modulus */
180    xmlChar b2[20];         /* Header for the exponent */
181    int l0 = 0;             /* Header length for the sequence */
182    int l1 = 0;             /* Header length for the modulus */
183    int l2 = 0;             /* Header length for the exponent */
184
185    xmlChar *modBuf = NULL; /* Bytes of the modulus */
186    xmlChar *expBuf = NULL; /* Bytes of the exponent */
187    int modLen = 0;         /* Length of the modulus */
188    int expLen = 0;         /* Length of the exponent */
189
190    SHA_CTX sha;            /* SHA1 hash context */
191
192    int rv = 0;             /* return value */
193
194    if ( !SHA1_Init(&sha)) goto fail;
195
196    if ( !doc || !(root = xmlDocGetRootElement(doc)) ) 
197        goto fail;
198
199    /* Find the KeyInfo section to be the root of later searches */
200    if ( !(node = xmlSecFindNode(root, 
201                    xmlSecNodeKeyInfo, xmlSecDSigNs)))
202        goto fail;
203
204    /* Get the binary for the modulus and exponent */
205    if ( !get_base64_field(node, (xmlChar *) "Modulus", &modBuf, &modLen)) 
206        goto fail;
207    if ( !get_base64_field(node, (xmlChar *) "Exponent", &expBuf, &expLen)) 
208        goto fail;
209
210    /* Construct the headers for modulus and exponent.  Another weird fact
211     * about ASN.1 is that all the integers are signed, so if either the
212     * modulus or exponent has the high order bit of its first byte set, the
213     * ASN.1 encoding has a 0 byte prepended.  This code appends the 0 byte to
214     * the header, which results in the same hash. */
215    if ( modBuf[0] & 0x80 ) {
216        l1 = make_asn1_header(0x02, modLen +1, b1);
217        b1[l1++] = '\0';
218    } else {
219        l1 = make_asn1_header(0x02, modLen, b1);
220    }
221
222    if ( expBuf[0] & 0x80 ) {
223        l2 = make_asn1_header(0x02, expLen +1, b2);
224        b2[l2++] = '\0';
225    } else {
226        l2 = make_asn1_header(0x02, expLen, b2);
227    }
228
229    /* Sequence header: have to do it after we know the lengths of the inner
230     * headers. */
231    l0 = make_asn1_header(0x30, modLen + expLen + l1 + l2, b0);
232    /* Hash it all up in parts */
233    SHA1_Update(&sha, b0, l0);
234    SHA1_Update(&sha, b1, l1);
235    SHA1_Update(&sha, modBuf, modLen);
236    SHA1_Update(&sha, b2, l2);
237    SHA1_Update(&sha, expBuf, expLen);
238    SHA1_Final(keyid, &sha);
239    rv = 1;
240fail:
241
242    if (modBuf) free(modBuf);
243    if (expBuf) free(expBuf);
244    return rv;
245}
246
247/* Check the signature of either kind of credential - it'll work for basically
248 * any signed XML. Returns true if the signature checks and false otherwise.
249 * Takes a pointer to the XML document to check.  Similar to the examples at
250 * http://www.aleksey.com/xmlsec/api/xmlsec-examples.html */
251static int check_signature(xmlDocPtr doc) {
252    xmlNodePtr root = NULL;         /* Root XML node */
253    xmlNodePtr node = NULL;         /* Scratch XML node */
254    xmlSecKeyInfoCtxPtr keyCtx = NULL;/* Key info context.  Used to parse
255                                         KeyInfo */
256    xmlSecKeysMngrPtr keyMan = NULL;/* Key manager - used because we have
257                                       certificated. */
258    xmlSecKeyPtr key = NULL;        /* The key extracted */
259    xmlSecDSigCtxPtr dsigCtx = NULL;/* Signature context */
260    int rv = 0;                     /* Return value */
261
262    if ( doc && !(root = xmlDocGetRootElement(doc)) ) 
263        goto fail;
264
265    /* Find the KeyInfo section to pull the keys out. */
266    if ( !(node = xmlSecFindNode(root, 
267                    xmlSecNodeKeyInfo, xmlSecDSigNs)))
268        goto fail;
269
270    /* Create and initialize key, key manager, and context */
271    if ( !(key = xmlSecKeyCreate() ) )
272            goto fail;
273    if ( !(keyMan = xmlSecKeysMngrCreate()) ) 
274        goto fail;
275
276    if ( xmlSecCryptoAppDefaultKeysMngrInit(keyMan) < 0)
277        goto fail;
278
279    if ( !(keyCtx = xmlSecKeyInfoCtxCreate(keyMan)) ) 
280        goto fail;
281
282    /* Do not check certificate signatures */
283    keyCtx->flags |= XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS;
284
285    /* Gather up the key data */
286    if ( xmlSecKeyInfoNodeRead(node, key, keyCtx) < 0 ) 
287        goto fail;
288
289    /* Set up the signature context and attack the keys */
290    if ( !(dsigCtx = xmlSecDSigCtxCreate(NULL)))
291        goto fail;
292
293    dsigCtx->signKey = key;
294
295    /* find the Signature section */
296    if ( !(node = xmlSecFindNode(root, 
297                    xmlSecNodeSignature, xmlSecDSigNs)))
298        goto fail;
299
300    /* Check it */
301    if ( (rv = xmlSecDSigCtxVerify(dsigCtx, node)) < 0 ) 
302        goto fail;
303
304    /* Strangely xmlSecDSigCtxVerify can return success even if the status is
305     * bad.  Check the status in the context explicitly and override the result
306     * above if necessary.*/
307    if ( dsigCtx->status != xmlSecDSigStatusSucceeded) 
308        goto fail;
309
310    return 1;
311fail:
312    if ( key ) xmlSecKeyDestroy(key);
313    if ( keyCtx ) xmlSecKeyInfoCtxDestroy(keyCtx);
314    if ( dsigCtx) xmlSecDSigCtxDestroy(dsigCtx);
315    return 0;
316}
317
318/* Parse the content of the expires field and compare it to the time passed in
319 * n.  If expires is earlier, return false, else true.  If n is null, compare
320 * it to the current time. */
321static int check_GENI_expires(xmlChar *expires, struct tm *n) {
322    struct tm tv;   /* Parsed expires field */
323    time_t now;     /* Now in seconds since the epoch */
324    time_t exp;     /* expires in seconds since the epoch */
325
326    if (n) now = mktime(n);
327    else time(&now);
328
329    strptime((char *) expires, "%FT%TZ", &tv);
330    exp = timegm(&tv);
331
332    return difftime(exp, now) > 0.0;
333}
334
335/* Convert a parsed privilege in a GENI privilege string into one or more ABAC
336 * creds.  Rules are as in http://groups.geni.net/geni/wiki/TIEDCredentials .
337 * keyid is  the issuer's keyid, text_owner is the owners keyid (XXX:a
338 * placeholder) text_target is the target's keyid (XXX: a placeholder), priv is
339 * the privilege being assigned, and delegate is true if it can be delegated.
340 * carray is the output array of character strings that currently has *nc
341 * entries in it.  If nc has nothing in it, insert the "speaks_for" delegation
342 * rules.  Then add the privilege specific rules. On failure ***carray and its
343 * contents are freed and *nc is set to zero.*/
344static void add_privilege_credential_string(xmlChar *text_keyid, xmlChar *text_owner, 
345        xmlChar *text_target, xmlChar *priv, int delegate, xmlChar ***carray,
346        int *nc) {
347    xmlChar **rv = *carray;     /* Local pointer to the array of strings */
348    xmlChar **newrv = NULL;     /* Used to realloc the array of strings */
349    int ncred = *nc;            /* Local copy of the number of creds in rv */
350    int newc = (delegate) ? 3 : 1;  /* Number of new creds to add */
351    int base = ncred;           /* First new credential index.  This advances
352                                   as we add creds to rv. */
353    int i = 0;                  /* Scratch */
354
355    /* If rv is empty, add the speaks_for rules */
356    if (base == 0 ) newc += 2;
357
358    /* Resize rv */
359    if (!(newrv = realloc(rv, (base + newc) * sizeof(xmlChar *))))
360        goto fail;
361    for ( i = 0; i < base +newc; i ++) 
362        newrv[i] = (i < base) ? rv[i] : NULL;
363
364    /* So fail works */
365    rv = newrv;
366    ncred = base + newc;
367
368    /* Add speaks_for rules  if needed */
369    if ( base == 0 ) {
370        if ( !(rv[base] = malloc(CREDLEN))) goto fail;
371        snprintf((char *) rv[base], CREDLEN, 
372                "%s.speaks_for_%s <- %s.speaks_for_%s",
373                text_keyid, text_owner, text_owner, text_owner);
374        base++;
375        if ( !(rv[base] = malloc(CREDLEN))) goto fail;
376        snprintf((char *) rv[base], CREDLEN, 
377                "%s.speaks_for_%s <- %s",
378                text_keyid, text_owner, text_owner);
379        base++;
380    }
381
382    /* The assignemnt of priv.  Always happens */
383    if ( !(rv[base] = malloc(CREDLEN))) goto fail;
384    snprintf((char *) rv[base], CREDLEN, 
385            "%s.%s_%s <- %s.speaks_for_%s",
386            text_keyid, priv, text_target, text_keyid, text_owner);
387    base++;
388    /* Add delegation rules */
389    if ( delegate ) {
390        if ( !(rv[base] = malloc(CREDLEN))) goto fail;
391        snprintf((char *) rv[base], CREDLEN, 
392                "%s.%s_%s <- %s.can_delegate_%s_%s.%s_%s",
393                text_keyid, priv, text_target, text_keyid, priv, 
394                text_target, priv, text_target);
395        base++;
396        if ( !(rv[base] = malloc(CREDLEN))) goto fail;
397        snprintf((char *) rv[base], CREDLEN, 
398                "%s.can_delegate_%s_%s <- %s",
399                text_keyid, priv, text_target, text_owner);
400        base++;
401    }
402    /* And return new values */
403    *carray = rv;
404    *nc = ncred;
405    return;
406fail:
407    if ( rv ) {
408        /* Delete all the allocations, ours or not, and clear the caller's
409         * variables */
410        for (i = 0; i < ncred; i++) 
411            if (rv[i]) free(rv[i]);
412        free(rv);
413    }
414    *carray = NULL;
415    *nc = 0;
416}
417
418/* Parse a GENI privilege credential (that has already had its signature
419 * checked) and return the RT0 strings that the credential is encoded as.  The
420 * return value is an array of strings, zero-terminated (like argv) that holds
421 * the RT0 strings.  It is NULL on failure. */
422static xmlChar **parse_privilege(xmlDocPtr doc) {
423    xmlNodePtr root = NULL;     /* XML root node */
424    xmlNodePtr node = NULL;     /* XML scratch node */
425    xmlNodePtr owner = NULL;    /* XML owner_gid node */
426    xmlNodePtr expires = NULL;  /* XML expires node */
427    xmlNodePtr target = NULL;   /* XML target_gid node */
428    xmlNodePtr privs = NULL;    /* XML privileges node */
429    xmlNodePtr priv = NULL;     /* XML privilege node - used to iterate */
430    xmlChar keyid[SHA_DIGEST_LENGTH];   /* Issuer key SHA1 */
431    xmlChar text_keyid[2*SHA_DIGEST_LENGTH+1];/* Issuer keyid as text */
432    xmlChar **newrv = NULL;     /* Used to realloc rv to add the NULL
433                                   terminator*/
434    xmlChar **rv = NULL;        /* return value */
435    int ncred = 0;              /* number of creds in rv, incase we need to
436                                   deallocate it */
437    int i = 0;                  /* scratch */
438
439    if ( doc && !(root = xmlDocGetRootElement(doc)) ) 
440        goto fail;
441
442    /* Get the issuer keyid */
443    if ( !get_keyid_from_keyinfo(doc, keyid)) 
444        goto fail;
445    sha1_to_text(keyid, text_keyid);
446
447    /* Find the various fields of interest */
448    if ( !(node = xmlSecFindNode(root, (xmlChar *) GENI_credential, NULL)))
449        goto fail;
450
451    /* Make sure this is not expired */
452    if ( !(expires = xmlSecFindNode(node, (xmlChar *) GENI_expires, NULL)))
453        goto fail;
454
455    if ( !check_GENI_expires(get_element_content(expires), NULL))
456        goto fail;
457
458    /* XXX: owner and target will be X.509 pem files from which we need to
459     * extract keyids for add_privilege_credential_string.  I didn't replciate
460     * that code which lives in libabac. Code to get those keyids needs to be
461     * added. */
462    if ( !(owner = xmlSecFindNode(node, (xmlChar *) GENI_owner_gid, NULL)))
463        goto fail;
464
465    if ( !(target = xmlSecFindNode(node, (xmlChar *) GENI_target_gid, NULL)))
466        goto fail;
467
468    if ( !(privs = xmlSecFindNode(node, (xmlChar *) GENI_privileges, NULL)))
469        goto fail;
470
471    /* Iterate through the privileges, parsing out names and can_delegate and
472     * generating the strings from it. */
473    for (priv = privs->children; priv; priv = priv->next) {
474        /* reinitialized every time around */
475        xmlNodePtr n = NULL;
476        xmlChar *name = NULL;
477        int delegate = -1;
478
479        /* Privilege node */
480        if ( priv->type != XML_ELEMENT_NODE || 
481                strcmp((char *) priv->name, (char *) GENI_privilege) ) {
482            fprintf(stderr, "Bad node!\n");
483            goto fail;
484        }
485        /* looking for name and can_delegate */
486        for (n = priv->children; n; n = n->next) {
487            if ( n->type != XML_ELEMENT_NODE ) 
488                continue;
489            if ( !strcmp((char *) n->name, (char *) GENI_name)) {
490                name = get_element_content(n);
491                continue;
492            }
493            if ( !strcmp((char *) n->name, (char *) GENI_can_delegate)) {
494                xmlChar *boolean = get_element_content(n);
495
496                if ( !strcmp((char *) boolean, "true") ||
497                        !strcmp((char *) boolean, "1") ) {
498                    delegate = 1;
499                } else if ( !strcmp((char *) boolean, "false") ||
500                        !strcmp((char *) boolean, "0") ) {
501                    delegate = 0;
502                } else {
503                    fprintf(stderr, "Unknown delegation value %s", boolean);
504                }
505            }
506        }
507        /* Found both name and can_delegate, add the RT0 to rv and ncred */
508        if ( name && delegate != -1 ) {
509            add_privilege_credential_string(text_keyid, 
510                    (xmlChar *) "owner-sha1", (xmlChar *) "target-sha1", name,
511                    delegate, &rv, &ncred);
512            if ( !rv ) goto fail;
513        }
514    }
515
516    /* Add the terminating NULL */
517    if (!(newrv = realloc(rv, ncred+1)))
518        goto fail;
519
520    for ( i = 0; i < ncred+1; i ++) 
521        newrv[i] = (i < ncred) ? rv[i] : NULL;
522
523    return rv;
524
525fail:
526    /* Throw away all of rv if there's an error */
527    if ( rv ) {
528        for (i = 0; i < ncred; i++) 
529            if (rv[i]) free(rv[i]);
530        free(rv);
531    }
532    return NULL;
533}
534
535/* Compare the version string (major.minor) to the major and minor numbers, and
536 * return true if the version string is >= the major minor pair. */
537static int check_GENI_abac_version(xmlChar *version, int major, int minor) {
538    xmlChar lversion[VERSIONLEN];   /* Buffer to parse version in */
539    xmlChar *lp = &lversion[0];     /* strsep needs a pointer it can modify */
540    char *p = NULL;                 /* Scratch */
541    char *ep = NULL;                /* used to check the integer parsing */
542    int maj = 0;                    /* Parsed major number */
543    int min = 0;                    /* Parsed minor number */
544
545    snprintf((char *) lp, VERSIONLEN, "%s", (char *) version);
546    if ( !(p = strsep((char **) &lp, "."))) return 0;
547    /* lp points t the major number and p to the minor, now we parse integers*/
548
549    maj = strtol((char *) lp, &ep, 10);
550    /* If the major version string was empty or (more likely) there were
551     * non-digits in it, throw out the result */
552    if ( *lp == '\0' || *ep != '\0' ) 
553        return 0;
554    min = strtol(p, &ep, 10);
555    /* Same test on the minor string */
556    if ( *p == '\0' || *ep != '\0' ) 
557        return 0;
558
559    if (maj > major || (maj == major && min > minor) ) return 0;
560    else return 1;
561}
562/*
563 * Parse an ABAC credential (that has had its signature checked.  Make sure it
564 * has not expired and that its version is at least 1.0 (XXX: parameterize
565 * that), and return the RT0 it encodes as the only entry in an NULL-terminated
566 * array of strings (just like parse_privilege). On failure return NULL. */
567xmlChar **parse_abac(xmlDocPtr doc) {
568    xmlNodePtr root = NULL;     /* XML root node */
569    xmlNodePtr node = NULL;     /* XML credential node */
570    xmlNodePtr rt0 = NULL;      /* XML rt0 node */
571    xmlNodePtr expires = NULL;  /* XML expires node */
572    xmlNodePtr version = NULL;  /* XML version node */
573    xmlChar keyid[SHA_DIGEST_LENGTH];/* issuer SHA1 hash */
574    xmlChar text_keyid[2*SHA_DIGEST_LENGTH+1];/* Issuer keyid in text */
575    xmlChar *txt;               /* rt0 content */
576    xmlChar **rv = NULL;        /* return value */
577    int ncred = 0;              /* number of creds in rv */
578    int i = 0;                  /* Scratch (used only in fail:)  */
579
580    if ( doc && !(root = xmlDocGetRootElement(doc)) ) 
581        goto fail;
582
583    /* Get the issuer keyid */
584    if ( !get_keyid_from_keyinfo(doc, keyid)) 
585        goto fail;
586    sha1_to_text(keyid, text_keyid);
587    /* get the various nodes of interest */
588    if ( !(node = xmlSecFindNode(root, (xmlChar *) GENI_credential, NULL)))
589        goto fail;
590    if ( !(expires = xmlSecFindNode(node, (xmlChar *) GENI_expires, NULL)))
591        goto fail;
592    if ( !(rt0 = xmlSecFindNode(node, (xmlChar *) GENI_rt0, NULL)))
593        goto fail;
594    if ( !(version = xmlSecFindNode(node, (xmlChar *) GENI_version, NULL)))
595        goto fail;
596
597    /* Check the version and expiration */
598    if ( !check_GENI_abac_version(get_element_content(version), 1, 0)) 
599        goto fail;
600    if ( !check_GENI_expires(get_element_content(expires), NULL))
601        goto fail;
602
603    /* read the RT0 and return it */
604    if ( !(txt = get_element_content(rt0)) ) goto fail;
605
606    if ( !(rv = malloc(2 * sizeof(xmlChar *)))) goto fail;
607    if (!(rv[0] = malloc(strlen((char *) txt)+1))) goto fail;
608    ncred++;
609
610    strcpy((char *) rv[0], (char *) txt);
611    rv[1] = NULL;
612
613    return rv;
614fail:
615    if ( rv ) {
616        for (i = 0; i < ncred; i++) 
617            if (rv[i]) free(rv[i]);
618        free(rv);
619    }
620    return NULL;
621}
622
623/* Check and parse a GENI credential.  Return the new RT0 rules in a
624 * NULL-terminated array of strings.  If the signature or parsing fails, return
625 * NULL. Demultiplexed to parse_privilege or parse_abac to do the parsing and
626 * uses check_signature to confirm the sig. */
627static xmlChar **read_credential(char *infile, xmlChar **xml) {
628    xmlDocPtr doc = xmlParseFile(infile);   /* Parse the document */
629    xmlNodePtr node = NULL;                 /* XML scratch node */
630    xmlChar *text = NULL;                   /* Text of the type field */
631    xmlChar **rv = NULL;                    /* return value from parse_* */
632
633    if ( !check_signature(doc) ) 
634        goto fail;
635    /* Parse out the type field */
636    if ( !(node = xmlDocGetRootElement(doc)) ) 
637        goto fail;
638    if ( !(node = xmlSecFindNode(node, (xmlChar *) GENI_credential, NULL)))
639        goto fail;
640    if ( !(node = xmlSecFindNode(node, (xmlChar *) GENI_type, NULL)))
641        goto fail;
642
643    if ( !(text = get_element_content(node)) ) 
644        goto fail;
645
646    /* Demux on type */
647    if ( !strcmp((char *) text, "privilege")) {
648        rv = parse_privilege(doc);
649    } else if ( !strcmp((char *) text, "abac")) {
650        rv = parse_abac(doc);
651    } else { 
652        goto fail;
653    }
654    int len=0;
655    xmlDocDumpMemoryEnc(doc, xml, &len, "UTF-8");
656    if(len == 0)
657       goto fail;
658
659fail:
660    xmlFreeDoc(doc);
661    return rv;
662}
663
664
665/* from Ted's writer */
666
667/* maximum rt0 stringlen */
668#define CREDLEN 1024
669/* Maximum version len */
670#define VERSIONLEN 256
671
672/* XML template for a new RT0 credential.  Fill in the RT0 to store (XML
673 * escaped) and the expiration time (formated using strftime_format and
674 * strftime). */
675xmlChar *template = (xmlChar *) (
676"<signed-credential>\n"
677"    <credential xml:id=\"ref0\">\n"
678"       <type>abac</type>\n"
679"       <version>1.0</version>\n"
680"       <expires>%s</expires>\n"
681"       <rt0>%s</rt0>\n"
682"    </credential>\n"
683"    <signatures>\n"
684"       <Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">\n"
685"           <SignedInfo>\n"
686"               <CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\n"
687"               <SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/>\n"
688"               <Reference URI=\"#ref0\">\n"
689"                   <Transforms>\n"
690"                       <Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/>\n"
691"                   </Transforms>\n"
692"                   <DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>\n"
693"                   <DigestValue/>\n"
694"               </Reference>\n"
695"           </SignedInfo>\n"
696"           <SignatureValue/>\n"
697"           <KeyInfo>\n"
698"             <KeyValue/>\n"
699"               <X509Data>\n"
700"                   <X509Certificate/>\n"
701"                   <X509SubjectName/>\n"
702"                   <X509IssuerSerial/>\n"
703"               </X509Data>\n"
704"           </KeyInfo>\n"
705"       </Signature>\n"
706"    </signatures>\n"
707"</signed-credential>");
708
709char *strftime_fmt = "%FT%TZ";
710#define EXPIRESLEN 20
711
712/* Return a copy of str with > < and & replaced by &gt; &lt; and &amp; for
713 * embedding in XML.  Caller is responsible for deallocating the return value
714 * using xmlFree().
715 */
716static xmlChar *minimal_xml_escaping(xmlChar *str) {
717    /* A quickie translation table with the character to escape, the output
718     * string and the length of the output in it. The table is initialized with
719     * the three translations we want. */
720    static struct esc {
721        xmlChar c;
722        xmlChar *esc;
723        int l;
724    } escapes[] = {
725        { (xmlChar) '<', (xmlChar *) "&lt;", 4 }, 
726        { (xmlChar) '>', (xmlChar *) "&gt;", 4},
727        { (xmlChar) '&', (xmlChar *) "&amp;", 5},
728        { (xmlChar) '\0', NULL, 0 },
729    };
730
731    xmlChar *rv = NULL;     /* Return value */
732    xmlChar *p = NULL;      /* Scratch (walking str) */
733    xmlChar *q = NULL;      /* Scratch (walking rv) */
734    struct esc *e = NULL;   /* Scratch for walking escapes */
735    int len = 0;            /* Length of rv */
736
737    /* Walk str and calculate how long the escaped version is */
738    for ( p = str; *p ; p++) {
739        int foundit = 0;
740        for ( e = escapes; !foundit && e->c ; e++ ) {
741            if ( *p == e->c ) {
742                len += e->l;
743                foundit = 1;
744            }
745        }
746        if ( !foundit ) len++;
747    }
748    /* Allocate the new string */
749    q = rv = (xmlChar *) xmlMalloc(len+1);
750    /* copy str to rv, escaping when necessary */
751    for ( p = str; *p ; p++) {
752        int foundit = 0;
753        for ( e = escapes; !foundit && e->c ; e++ ) {
754            if ( *p == e->c ) {
755                strncpy((char *) q, (char *) e->esc, e->l);
756                q += e->l;
757                foundit = 1;
758            }
759        }
760        if ( !foundit ) *q++ = *p;
761    }
762    /* terminate rv */
763    *q = '\0';
764    return rv;
765}
766
767/* Make an new GENI abac credential with the rt0 rule that expires secs from
768 * now.  cert is the PEM encoded X.509 of the issuer's certificate as a string.
769 * certlen is the length of cert.  Returns the XML. Caller is responsible for
770 * freeing it. */
771static char *make_credential(xmlChar *rt0, int secs, xmlSecByte *cert, 
772        xmlSecSize certlen) {
773    xmlDocPtr doc = NULL;       /* parsed XML document */
774    xmlNodePtr root = NULL;     /* Root of the document */
775    xmlNodePtr signNode = NULL; /* <Signature> element */
776    xmlSecDSigCtxPtr dsigCtx = NULL;  /* Signing context */
777    xmlChar *rt0_escaped = minimal_xml_escaping(rt0); /* RT0, escaped */
778    xmlChar *rv = NULL;         /* return value */
779    time_t exp;                 /* expriation time (seconds since epoch) */
780    struct tm exp_tm;           /* expiration for formatting */
781    char estr[EXPIRESLEN+1];    /* String with formatted expiration */
782    char *temp = NULL;          /* populated XML template */
783    int len = 0;                /* length of the populated template (temp) */
784
785    if ( !rt0_escaped) goto fail;
786
787    /* Calculate the length of the populated template and allocate it */
788    len = strlen((char *) template)+EXPIRESLEN+strlen((char *) rt0_escaped)+1;
789
790    if ( !(temp = malloc(len)) )
791        goto fail;
792
793    /* format expiration */
794    time(&exp);
795    exp += secs;
796    gmtime_r(&exp, &exp_tm);
797
798    if (strftime(estr, EXPIRESLEN+1, strftime_fmt, &exp_tm) == 0 ) 
799        goto fail;
800
801    /* Populate template with  expiration and escaped rt0 */
802    snprintf(temp, len, (char *) template, estr, (char *) rt0_escaped);
803
804    /* parse the populated template */
805    if ( !(doc = xmlParseMemory(temp, len))) 
806        goto fail;
807
808    if (!(root = xmlDocGetRootElement(doc)) )
809        goto fail;
810
811    /* Find the signature element for the Signing call */
812    signNode = xmlSecFindNode(root, xmlSecNodeSignature, xmlSecDSigNs);
813
814    /* Create the context */
815    if ( !(dsigCtx = xmlSecDSigCtxCreate(NULL))) 
816        goto fail;
817
818    /* Assign the key (a PEM key) */
819    if (!(dsigCtx->signKey = xmlSecCryptoAppKeyLoadMemory(cert, certlen,
820                    xmlSecKeyDataFormatPem, NULL, NULL, NULL)) )
821        goto fail;
822
823    /* Load the certificate */
824    if ( xmlSecCryptoAppKeyCertLoadMemory(dsigCtx->signKey, cert, certlen,
825                xmlSecKeyDataFormatPem) < 0)
826        goto fail;
827
828    /* Sign it */
829    if ( xmlSecDSigCtxSign(dsigCtx, signNode) < 0)
830        goto fail;
831
832    /* Store the signed credential to rv */
833    xmlDocDumpMemoryEnc(doc, &rv, &len, "UTF-8");
834fail:
835    /* clean up */
836    if (dsigCtx) 
837        xmlSecDSigCtxDestroy(dsigCtx);
838    if ( doc) xmlFreeDoc(doc);
839    if (rt0_escaped) xmlFree(rt0_escaped);
840    if ( temp) free(temp);
841    return (char *) rv;
842}
843
844#define CERTSIZE 4096
845/* Read a file into a the dynamically allocated array buf.  Whatever was in buf
846 * is discarded w/o freeing so it should initially be NULL and len 0. On return
847 * buf contains as much of the contents of filename as possible and len is the
848 * length of it.  The caller must free() buf. buf is zero terminated.*/
849static void read_cert(char *filename, char **buf, int *len) {
850    FILE *cert = NULL;
851    char *nbuf = NULL;
852    int cap = CERTSIZE;
853    int r = 0;
854
855    *len = 0; 
856    if (!(*buf = malloc(CERTSIZE))) return;
857
858    if ( !(cert = fopen(filename, "r"))) 
859        goto fail;
860
861    /* Read cert, expanding buf as necessary */
862    while ( (r = fread(*buf + *len, 1, cap - *len, cert)) != 0) {
863        *len += r;
864        if ( *len == cap ) {
865            if ( !(nbuf = realloc(*buf, cap + CERTSIZE)) ) 
866                goto fail;
867            memcpy(nbuf, *buf, cap);
868            cap += CERTSIZE;
869            *buf = nbuf;
870        }
871    }
872    /* Extend if we read exactly cap bytes so we can terminate the string */
873    if ( *len == cap ) {
874        if ( !(nbuf = realloc(*buf, cap + CERTSIZE)) ) 
875            goto fail;
876        memcpy(nbuf, *buf, cap);
877        cap += CERTSIZE;
878        *buf = nbuf;
879    }
880    (*buf)[*len] = '\0';
881    return;
882fail:
883    if ( *buf) free(*buf);
884    *buf = NULL;
885    *len = 0;
886    return;
887}
888
889
890/******** helper functions used by libabac **********************/
891
892/* Straight off http://www.aleksey.com/xmlsec/api/xmlsec-examples.html .
893 * Voodoo.  But call it. */
894int init_xmlsec() {
895    /* Init xmlsec library */
896    if(xmlSecInit() < 0) {
897        fprintf(stderr, "Error: xmlsec initialization failed.\n");
898        return(-1);
899    }
900
901    /* Check loaded library version */
902    if(xmlSecCheckVersion() != 1) {
903        fprintf(stderr,
904                "Error: loaded xmlsec library version is not compatible.\n");
905        return(-1);
906    }
907
908    /* Load default crypto engine if we are supporting dynamic
909     * loading for xmlsec-crypto libraries. Use the crypto library
910     * name ("openssl", "nss", etc.) to load corresponding
911     * xmlsec-crypto library.
912     */
913#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
914    if(xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
915        fprintf(stderr, "Error: unable to load default xmlsec-crypto library. "
916                "Make sure\n" 
917                "that you have it installed and check shared libraries path\n"
918                "(LD_LIBRARY_PATH) envornment variable.\n");
919        return(-1);     
920    }
921#endif /* XMLSEC_CRYPTO_DYNAMIC_LOADING */
922
923    /* Init crypto library */
924    if(xmlSecCryptoAppInit(NULL) < 0) {
925        fprintf(stderr, "Error: crypto initialization failed.\n");
926        return(-1);
927    }
928
929    /* Init xmlsec-crypto library */
930    if(xmlSecCryptoInit() < 0) {
931        fprintf(stderr, "Error: xmlsec-crypto initialization failed.\n");
932        return(-1);
933    }
934    return 0;
935}
936
937int deinit_xmlsec() {
938  /* no op for now */
939    return 0;
940}
941
942
943/* to cast the parameters */
944char **my_read_credential(char *infile, char **xml) {
945    return (char **)read_credential(infile, (xmlChar **)xml);
946}
947
948char *my_make_credential(char* rt0, int secs, char* cert, int certlen)
949{
950    return make_credential((xmlChar *)rt0, secs, (xmlSecByte *)cert, (xmlSecSize)certlen);
951}
952
953/* parse the xml blob and extract keyid */ 
954char *get_keyid_from_xml(char *xml) {
955    xmlDocPtr doc=xmlParseMemory(xml,strlen(xml));
956    xmlChar keyid[SHA_DIGEST_LENGTH];   /* Issuer key SHA1 */
957    xmlChar text_keyid[2*SHA_DIGEST_LENGTH+1];/* Issuer keyid as text */
958    char *ret=NULL;
959
960    /* Get the issuer keyid */
961    if ( !get_keyid_from_keyinfo(doc, keyid))
962        goto fail;
963    sha1_to_text(keyid, text_keyid);
964    ret=strdup((char *)text_keyid);
965fail:
966    xmlFreeDoc(doc);
967    return ret;
968}
969
970/* parse xml and get the expected expiration time and returns
971 * (expiration time-current time)
972 */
973long get_validity_from_xml(char *xml) {
974    xmlDocPtr doc=xmlParseMemory(xml,strlen(xml));
975    xmlNodePtr node = NULL;                 /* XML scratch node */
976    xmlNodePtr expires = NULL;  /* XML expires node */
977    struct tm tv;   /* Parsed expires field */
978    time_t now;     /* Now in seconds since the epoch */
979    time_t exp;     /* expires in seconds since the epoch */
980    long dtime=0;
981
982    if ( !(node = xmlDocGetRootElement(doc)) )
983        goto fail;
984
985    if ( !(expires = xmlSecFindNode(node, (xmlChar *) GENI_expires, NULL)))
986        goto fail;
987
988    xmlChar *etime=get_element_content(expires);
989    time(&now);
990
991    strptime((char *) etime, "%FT%TZ", &tv);
992    exp = timegm(&tv);
993    dtime=difftime(exp, now);
994
995fail:
996    xmlFreeDoc(doc);
997    return dtime;
998}
999
1000/* parse xml structure and extract the attribute rules */
1001char **get_rt0_from_xml(char *xml) {
1002    xmlDocPtr doc=xmlParseMemory(xml,strlen(xml));
1003    xmlNodePtr node = NULL;                 /* XML scratch node */
1004    xmlChar *text = NULL;                   /* Text of the type field */
1005    xmlChar **rv = NULL;                    /* return value from parse_* */
1006   
1007    if ( !check_signature(doc) )
1008        goto fail;
1009    /* Parse out the type field */
1010    if ( !(node = xmlDocGetRootElement(doc)) )
1011        goto fail;
1012    if ( !(node = xmlSecFindNode(node, (xmlChar *) GENI_credential, NULL)))
1013        goto fail;
1014    if ( !(node = xmlSecFindNode(node, (xmlChar *) GENI_type, NULL)))
1015        goto fail;
1016
1017    if ( !(text = get_element_content(node)) )
1018        goto fail;
1019
1020    /* Demux on type */
1021    if ( !strcmp((char *) text, "privilege")) {
1022        rv = parse_privilege(doc);
1023    } else if ( !strcmp((char *) text, "abac")) {
1024        rv = parse_abac(doc);
1025    } else {
1026        goto fail;
1027    }
1028fail:
1029    xmlFreeDoc(doc);
1030    return (char **)rv;
1031}
1032
1033
Note: See TracBrowser for help on using the repository browser.