source: libabac/abac_xml.c @ 95b150a

abac0-leakabac0-meimei-idmei-rt0-nmei_rt0tvf-new-xml
Last change on this file since 95b150a was 95b150a, checked in by Ted Faber <faber@…>, 11 years ago

Remove libxml2 debugging

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