source: libabac/abac_xml.c @ c199a27

abac0-leakabac0-meimei-idtvf-new-xml
Last change on this file since c199a27 was c199a27, checked in by Ted Faber <faber@…>, 11 years ago

Remove a few assumptions about XML parsing.

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