source: libabac/abac_xml.c @ 9ac7fb4

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

1) work around some compilation warnings

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