source: libabac/abac_xml.c @ 92661b4

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

1) add some doc
2) fix the version in abac_xml

  • 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        /* Privilege node */
499        if ( priv->type != XML_ELEMENT_NODE || 
500                strcmp((char *) priv->name, (char *) GENI_privilege) ) {
501            fprintf(stderr, "Bad node!\n");
502            goto fail;
503        }
504        /* looking for name and can_delegate */
505        for (n = priv->children; n; n = n->next) {
506            if ( n->type != XML_ELEMENT_NODE ) 
507                continue;
508            if ( !strcmp((char *) n->name, (char *) GENI_name)) {
509                name = get_element_content(n);
510                continue;
511            }
512            if ( !strcmp((char *) n->name, (char *) GENI_can_delegate)) {
513                xmlChar *boolean = get_element_content(n);
514
515                if ( !strcmp((char *) boolean, "true") ||
516                        !strcmp((char *) boolean, "1") ) {
517                    delegate = 1;
518                } else if ( !strcmp((char *) boolean, "false") ||
519                        !strcmp((char *) boolean, "0") ) {
520                    delegate = 0;
521                } else {
522                    fprintf(stderr, "Unknown delegation value %s", boolean);
523                }
524            }
525        }
526        /* Found both name and can_delegate, add the RT0 to rv and ncred */
527        if ( name && delegate != -1 ) {
528            add_privilege_credential_string(text_keyid, 
529                    (xmlChar *) owner_sha1, (xmlChar *) target_sha1, name,
530                    delegate, &rv, &ncred);
531            if ( !rv ) goto fail;
532        }
533    }
534
535    /* Add the terminating NULL */
536    if (!(newrv = realloc(rv, sizeof(xmlChar*)*(ncred+1))))
537        goto fail;
538
539    newrv[ncred] = NULL;
540    return newrv;
541
542fail:
543    /* Throw away all of rv if there's an error */
544    if ( rv ) {
545        for (i = 0; i < ncred; i++) 
546            if (rv[i]) free(rv[i]);
547        free(rv);
548    }
549    return NULL;
550}
551
552/* Compare the version string (major.minor) to the major and minor numbers, and
553 * return true if the version string is >= the major minor pair. */
554static int check_GENI_abac_version(xmlChar *version, int major, int minor) {
555    xmlChar lversion[VERSIONLEN];   /* Buffer to parse version in */
556    xmlChar *lp = &lversion[0];     /* strsep needs a pointer it can modify */
557    int maj = 0;                    /* Parsed major number */
558    int min = 0;                    /* Parsed minor number */
559    char *values[2];
560    int ret=0;
561
562    snprintf((char *) lp, VERSIONLEN, "%s", (char *) version);
563    abac_split(lp, ".", values, &ret);
564    if(ret==2) {
565        /* split successful, values[0] is the major number,
566           values[1] is the minor, now we parse integers*/
567        if ( *values[0] == '\0') return 0;
568        maj = strtol(values[0], NULL, 10);
569        if ( *values[1] == '\0') return 0;                           
570        min = strtol(values[1], NULL, 10);   
571    } else return 0;                                                 
572                                                                     
573    if (maj > major ||             
574           (maj == major && min > minor) ||                           
575               (maj == major && min == minor) ) return 1;             
576    else return 0;                                                   
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.