/* abac_attribute.c */ #define _GNU_SOURCE #include #include #include #include #include #include "libabac_common.h" #include "abac_list.h" #include "abac_util.h" #include "abac_xml.h" #define ROLE_SEPARATOR " <- " #define INTERSECTION_SEP " & " #define SHA1_LENGTH 40 static int debug=0; // a GENI XML attribute chunk might contain multiple // attribute rules. It will be translate into multiple // abac_attribute structures but with cert ptr pointing // to the same xml chunk // issuer can be missing but then it won't be bakable // unless it is baked just for creddy's roles call struct _abac_attribute_t { abac_id_t *issuer_id; char *role; long validity; char *head_string; char *tail_string; abac_chunk_t cert; // the XML chunk }; char *abac_attribute_role_string(abac_attribute_t *attr); extern abac_id_t *abac_verifier_lookup_id(char *keyid); static char *_validate_principal(char *keyid); /************************************************************/ abac_chunk_t abac_attribute_cert(abac_attribute_t *ptr) { assert(ptr); return ptr->cert; } abac_id_t *abac_attribute_issuer_id(abac_attribute_t *ptr) { assert(ptr); return ptr->issuer_id; } // validity is measured in seconds (as of 0.2.0) // Acme.customer int abac_attribute_create(abac_attribute_t **ret, abac_id_t *issuer_id, char *role, long validity) { if (!abac_id_has_privkey(issuer_id)) return ABAC_ATTRIBUTE_ISSUER_NOKEY; if (!abac_clean_name(role)) return ABAC_ATTRIBUTE_INVALID_ROLE; if (validity < 0) return ABAC_ATTRIBUTE_INVALID_VALIDITY; if (!abac_id_still_valid(issuer_id)) return ABAC_ATTRIBUTE_INVALID_ISSUER; if(validity == 0) validity = (long)(60*60*24*(365)); abac_attribute_t *attr = abac_xmalloc(sizeof(abac_attribute_t)); if(issuer_id) attr->issuer_id = abac_id_dup(issuer_id); else attr->issuer_id = NULL; attr->role = abac_xstrdup(role); attr->validity = validity; attr->head_string = NULL; asprintf(&attr->head_string,"%s.%s",abac_id_keyid(issuer_id),role); attr->tail_string = NULL; // NULL until baked attr->cert.ptr=NULL; attr->cert.len=0; *ret = attr; return ABAC_SUCCESS; } /** * Get the validity period.(xml module returns the diff from expire time - now() */ int abac_attribute_validity(abac_attribute_t *attr,struct tm *not_before,struct tm *not_after) { assert(attr); memset(not_before, 0, sizeof(struct tm)); memset(not_after, 0, sizeof(struct tm)); time_t now; time(&now); gmtime_r(&now, not_before); char *xml=(char *)attr->cert.ptr; long validity=get_validity_from_xml(xml); time_t etime = now + validity; gmtime_r(&etime, not_after); if(debug) fprintf(stderr,"validity from the xml blob is %ld\n",validity); if(validity == 0) return ABAC_FAILURE; return ABAC_SUCCESS; } int abac_attribute_still_valid(abac_attribute_t *attr) { assert(attr); assert(attr->cert.ptr); long v=get_validity_from_xml((char *)attr->cert.ptr); if (v > 0.0) return 1; else return 0; } void abac_attribute_set_head(abac_attribute_t *attr, char *string) { assert(attr); attr->head_string=string; } char *abac_attribute_get_head(abac_attribute_t *attr) { assert(attr); return attr->head_string; } void abac_attribute_set_tail(abac_attribute_t *attr, char *string) { assert(attr); attr->tail_string=string; } char *abac_attribute_get_tail(abac_attribute_t *attr) { assert(attr); return attr->tail_string; } /* A.b->C, return copy of a A */ char *abac_attribute_get_principal(abac_attribute_t *attr) { char *role_string=abac_attribute_role_string(attr); /* make an copy */ char *tmp=strdup(role_string); char *head_tail[2]; int ret; abac_split(tmp, "<-", head_tail, &ret); if (ret != 2) goto err; abac_split(head_tail[0], ".", head_tail, &ret); if (ret != 2) goto err; char *prin=strdup(head_tail[0]); free(tmp); return prin; err: free(tmp); return NULL; } int abac_attribute_principal(abac_attribute_t *attr, char *keyid) { char *copy = _validate_principal(keyid); if (copy == NULL) return 0; abac_attribute_set_tail(attr,copy); return 1; } int abac_attribute_role(abac_attribute_t *attr, char *keyid, char *role) { if (!abac_clean_name(role)) return 0; char *copy = _validate_principal(keyid); if (copy == NULL) return 0; int len = strlen(copy) + strlen(role) + strlen(ROLE_SEPARATOR) + 1; copy = abac_xrealloc(copy, len); strcat(copy, "."); strcat(copy, role); abac_attribute_set_tail(attr,copy); return 1; } int abac_attribute_linking_role(abac_attribute_t *attr, char *keyid, char *role, char *linked) { if (!abac_clean_name(role) || !abac_clean_name(linked)) return 0; char *copy = _validate_principal(keyid); if (copy == NULL) return 0; int len = strlen(copy) + strlen(role) + strlen(linked) + strlen(ROLE_SEPARATOR) + 2; copy = abac_xrealloc(copy, len); strcat(copy, "."); strcat(copy, role); strcat(copy, "."); strcat(copy, linked); abac_attribute_set_tail(attr,copy); return 1; } // 0 for fail to bake, 1 is baked okay int abac_attribute_bake(abac_attribute_t *attr) { assert(attr); assert(attr->head_string); assert(attr->tail_string); abac_chunk_t id_chunk = { NULL, 0 }; int ret=abac_id_PEM(attr->issuer_id, &id_chunk); if(ret != ABAC_CERT_SUCCESS) return 0; char *role_string = abac_attribute_role_string(attr); /* Make an new GENI abac credential with the rt0 rule that expires secs from * now. cert is the PEM encoded X.509 of the issuer's certificate as a string. * certlen is the length of cert. Returns the XML. Caller is responsible for * freeing it. */ char *attr_cert=my_make_credential(role_string, attr->validity, (char *)id_chunk.ptr, id_chunk.len); if (attr_cert == NULL) return 0; attr->cert.ptr = (unsigned char *)attr_cert; attr->cert.len = strlen(attr_cert); free(role_string); return 1; } // make an explicit copy abac_chunk_t abac_attribute_cert_chunk(abac_attribute_t *attr) { abac_chunk_t chunk= {NULL,0}; if (attr->cert.ptr == NULL) return chunk; /* return the xml chunk */ chunk.ptr= (unsigned char *)abac_xstrdup((char *)attr->cert.ptr); chunk.len=attr->cert.len; return chunk; } int abac_attribute_baked(abac_attribute_t *attr) { return (attr->cert.ptr != NULL); } static abac_attribute_t *_load_attr(char *role_string, char *xml) { if(debug) fprintf(stderr,"loading -> %s \n", role_string); char *head_tail[2]; int ret; abac_split(role_string, "<-", head_tail, &ret); if (ret != 2) return NULL; char *keyid=get_keyid_from_xml(xml); abac_id_t *issuer_id=abac_verifier_lookup_id(keyid); long validity=get_validity_from_xml(xml); abac_attribute_t *attr = abac_xmalloc(sizeof(abac_attribute_t)); if(issuer_id) attr->issuer_id = abac_id_dup(issuer_id); else attr->issuer_id=NULL; attr->validity = validity; attr->head_string = abac_xstrdup(head_tail[0]); attr->tail_string = abac_xstrdup(head_tail[1]); char *tmp=strstr(attr->head_string,"."); attr->role =abac_xstrdup(tmp+1); attr->cert.ptr=(unsigned char *)xml; attr->cert.len=strlen(xml); return attr; } abac_list_t *abac_attribute_certs_from_file(char *filename) { abac_list_t *alist=abac_list_new(); char *xml=NULL; char *rt0=NULL; char **rt0s=my_read_credential(filename, &xml); if(rt0s == NULL) return alist; if(xml == NULL || strlen(xml)==0) return alist; abac_attribute_t *attr; int i=0; do { rt0 = rt0s[i]; if(rt0 == NULL) break; attr=_load_attr(rt0, xml); if(attr) abac_list_add(alist, attr); free(rt0); i++; } while (rt0s[i] !=NULL); free(rt0s); free(xml); return alist; } abac_list_t *abac_attribute_certs_from_chunk(abac_chunk_t chunk) { abac_list_t *alist=abac_list_new(); char *xml=(char *)chunk.ptr; if(chunk.len==0) return alist; char **rt0s=get_rt0_from_xml(xml); char *rt0=NULL; if(rt0s==NULL) return alist; abac_attribute_t *attr; int i=0; do { rt0 = rt0s[i]; if(rt0 == NULL) break; attr=_load_attr(rt0, xml); if(attr) abac_list_add(alist, attr); free(rt0); i++; } while (rt0s[i] !=NULL); free(rt0s); return alist; } // returns ABAC_FAILURE if the cert hasn't been baked int abac_attribute_write(abac_attribute_t *attr, FILE *out) { assert(attr != NULL); if (attr->cert.ptr == NULL) return ABAC_FAILURE; // write to file fwrite(attr->cert.ptr, attr->cert.len, 1, out); return ABAC_SUCCESS; } // returns ABAC_FAILURE if the cert hasn't been baked int abac_attribute_write_file(abac_attribute_t *attr, const char *fname) { if (attr->cert.ptr == NULL) return ABAC_FAILURE; FILE *fp=fopen(fname,"w+"); if(fp) { // write to file fwrite(attr->cert.ptr, attr->cert.len, 1, fp); } else return ABAC_FAILURE; fclose(fp); return ABAC_SUCCESS; } void abac_attribute_free(abac_attribute_t *attr) { if(debug) fprintf(stderr,"calling abac_attribute_free on %s\n", attr->head_string); if (attr == NULL) return; if(attr->issuer_id) abac_id_free(attr->issuer_id); free(attr->role); free(attr->head_string); free(attr->tail_string); /* XXX this can not be free as if it is a char string free(attr->cert.ptr); */ free(attr); } // // Helper functions below // // validate a princpal's name // makes sure it's a valid SHA1 identifier // return values: // success: malloc'd copy with all hex digits lowercase // fail: NULL static char *_validate_principal(char *keyid) { int i; char *copy = NULL; if (strlen(keyid) != SHA1_LENGTH) return NULL; copy = abac_xstrdup(keyid); for (i = 0; i < SHA1_LENGTH; ++i) { copy[i] = tolower(copy[i]); if (!isxdigit(copy[i])) goto error; } return copy; error: free(copy); return NULL; } // combine up the attribute's rule string, explicit copy char *abac_attribute_role_string(abac_attribute_t *attr) { assert(attr); char *role_string=NULL; asprintf(&role_string,"%s<-%s",attr->head_string,attr->tail_string); return role_string; }