source: libabac/abac_xml.c @ ec550f7

abac0-leakabac0-meimei-idtvf-new-xml
Last change on this file since ec550f7 was ec550f7, checked in by Mei <mei@…>, 11 years ago

1) reworked how API doc is generated
2) tweak top level Makefile.am
3) loading issuer principal as side-effect of loading

an attribute credentials

4) add examples of GENI specific attribute credentials

and principal certificates into the regression testing

5) rename examples to tests

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