/** ** abac_attribute.c **/ #include #include #include #include #include #include #include #include "abac_util.h" #include "abac_aspect.h" #include "abac_m64.h" #include "abac_id.h" #include "abac_pl_yy.h" #include "rt2_yy.h" #include "abac_verifier.h" #include "abac_util_cert.h" #include "abac_id.h" /*****************************************************************/ static int debug=0; /* the correct setting of using_this depends on user if the attribute is not generate from yyparse call */ struct _abac_attribute_t { abac_id_t *issuer_id; abac_aspect_t *head; abac_aspect_t *tail; int validity; int using_this; /* tracking use of (?This) */ certificate_t *cert; // NULL until baked certificate_t *issuer_cert; int refcount; }; /* local */ abac_attribute_t *abac_attribute_set_head(abac_attribute_t *ptr, abac_aspect_t *head); abac_aspect_t *abac_attribute_head(abac_attribute_t *ptr); abac_aspect_t *abac_attribute_tail(abac_attribute_t *ptr); abac_attribute_t *abac_attribute_add_tail(abac_attribute_t *ptr, abac_aspect_t *tail); void abac_attribute_set_using_this(abac_attribute_t *ptr, int using_this); /********************************************************************/ /* currently the hashkeyid is the uuencoded 'sha attribute string' */ char *get_cred_encoding(abac_attribute_t *ptr) { char *encoding=NULL; /* must have a head aspect and tail aspect */ assert(ptr->head != NULL); assert(ptr->tail != NULL); if(ptr->cert != NULL) { /* already a baked one */ ac_t *ac = (ac_t *)ptr->cert; ietf_attributes_t *attr_cert = ac->get_groups(ac); char *encoded_attr_string=attr_cert->get_string(attr_cert); return encoded_attr_string; } /* not a baked one should not be in ABAC_CN mode at all !!! */ /* needs to be the one with sha */ if(USE("ABAC_CN")) { fprintf(stderr,"get_cred_encoding, MIGHT be BAD..\n"); } char *head_string=abac_aspect_typed_string_with_condition(ptr->head); char *tail_string=abac_aspect_typed_string_with_condition(ptr->tail); asprintf(&encoding,"%s<-%s", head_string,tail_string); char* base64_encoding=abac_encode_string(encoding); if(USE("ABAC_CN")) { fprintf(stderr," --> (%s)\n", encoding); } free(encoding); free(head_string); free(tail_string); return base64_encoding; } static abac_attribute_t *_abac_attribute_init() { abac_attribute_t *ptr = abac_xmalloc(sizeof(abac_attribute_t)); ptr->issuer_id = NULL; ptr->head = NULL; ptr->tail = NULL; ptr->validity= 365 * 86400; // default // NULL until baked ptr->using_this=0; ptr->cert= NULL; ptr->issuer_cert= NULL; ptr->refcount=1; return ptr; } /********************************************************************/ abac_attribute_t *abac_attribute_new(abac_id_t *issuer, certificate_t *cert, certificate_t *issuer_cert) { abac_attribute_t *ptr = _abac_attribute_init(); ptr->issuer_id = abac_id_dup(issuer); ptr->cert=cert; // already baked.. ptr->issuer_cert=issuer_cert; ptr->refcount=1; return ptr; } // validity is measured in seconds (as of 0.2.0) // remember using_this, int abac_attribute_create(abac_attribute_t **ret, abac_aspect_t *head, abac_aspect_t *tail, int validity) { abac_id_t *issuer_id=abac_aspect_get_issuer_id(head); if(debug) print_abac_key("abac_attribute_create" , abac_id_keyptr(issuer_id)); char *name=abac_aspect_aspect_name(head); if (!abac_id_has_privkey(issuer_id)) return ABAC_ATTRIBUTE_ISSUER_NOKEY; if (!abac_validate_clean_aspect_name(name)) return ABAC_ATTRIBUTE_INVALID_ROLE; if (validity < 0) return ABAC_ATTRIBUTE_INVALID_VALIDITY; abac_attribute_t *ptr = _abac_attribute_init(); ptr->issuer_id = abac_id_dup(issuer_id); ptr->head = abac_aspect_dup(head); if(tail) ptr->tail = abac_aspect_dup(tail); ptr->validity=(validity==0?(365 * 86400):validity); *ret = ptr; return ABAC_ATTRIBUTE_SUCCESS; } // validity is measured in seconds (as of 0.2.0) int abac_attribute_create_creddy(abac_attribute_t **ret, abac_id_t *issuer, abac_aspect_t *head_aspect, abac_aspect_t *tail_aspect, char *name, int validity) { if (!abac_id_has_privkey(issuer)) return ABAC_ATTRIBUTE_ISSUER_NOKEY; if (!abac_validate_clean_aspect_name(name)) return ABAC_ATTRIBUTE_INVALID_ROLE; if (validity < 0) return ABAC_ATTRIBUTE_INVALID_VALIDITY; abac_attribute_t *ptr = _abac_attribute_init(); ptr->issuer_id = abac_id_dup(issuer); ptr->head=head_aspect; ptr->tail=tail_aspect; ptr->validity = validity; *ret = ptr; return ABAC_ATTRIBUTE_SUCCESS; } static void _check_cert_group(certificate_t *cert, char *estring) { ietf_attributes_t *attr_cert = NULL; int ret, i; // get the attr ac_t *ac = (ac_t *)cert; attr_cert = ac->get_groups(ac); if(attr_cert == NULL) { fprintf(stderr,"BAD group from the cert !!!\n"); return; } char *encoded_attr_string=attr_cert->get_string(attr_cert); if(encoded_attr_string==NULL) { fprintf(stderr,"BAD empty, encoded attr string from the cert !!!\n"); /* chunk_t b64 = chunk_to_base64(chunk, NULL); chunk_t chk = chunk_from_base64(base64, NULL); */ int len = strlen(estring); chunk_t b64 = { estring, len }; chunk_t chk= chunk_from_base64(b64, NULL); chk.ptr[chk.len]='\0'; fprintf(stderr,"new convert: (%s)\n", chk.ptr); chunk_t nb64 = chunk_to_base64(chk, NULL); fprintf(stderr,"back again: (%d)(%s)\n", strlen(nb64.ptr), nb64.ptr); } } int abac_attribute_bake(abac_attribute_t *ptr) { assert(ptr); char *cred_encoding = get_cred_encoding(ptr); // create attribute cert time_t not_before = time(NULL); time_t not_after = not_before + ptr->validity; chunk_t serial = abac_generate_serial(); private_key_t *private = abac_id_privkey(ptr->issuer_id); if(private==NULL) errx(1,"can not bake without a private key!!!"); /**** XXX this needs to be removed !!!!! limitation from strongswan... ****/ if(strlen(cred_encoding) > 512) { fprintf(stderr,"due to BUILD_IETF_GROUP_ATTR buffer size limit, can not bake this attribute (%d)!!\n", strlen(cred_encoding)); return 0; } certificate_t *attr_cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_AC, BUILD_CERT, abac_id_cert(ptr->issuer_id), BUILD_NOT_BEFORE_TIME, not_before, BUILD_NOT_AFTER_TIME, not_after, BUILD_SERIAL, serial, BUILD_IETF_GROUP_ATTR, cred_encoding, BUILD_SIGNING_CERT, abac_id_cert(ptr->issuer_id), BUILD_SIGNING_KEY, private, BUILD_END ); _check_cert_group(attr_cert, cred_encoding); DESTROY_IF(private); if (attr_cert == NULL) return 0; ptr->cert = attr_cert; // cert and issuer can be the same, in which case it is the self signing ptr->issuer_cert= attr_cert->get_ref(attr_cert); free(cred_encoding); free(serial.ptr); return 1; } abac_chunk_t abac_attribute_cert_chunk(abac_attribute_t *ptr) { assert(ptr->cert); chunk_t encoding = chunk_empty; int rc=ptr->cert->get_encoding(ptr->cert,CERT_ASN1_DER,&encoding); abac_chunk_t ret = { encoding.ptr, encoding.len }; return ret; } /* just loading it without any verifying */ certificate_t *abac_attribute_cert_from_file(char *filename) { if(!file_exist(filename)) return NULL; libabac_init(); certificate_t *cert = cert_get_attr_cert_from_file(filename); if (cert == NULL) errx(1, "Couldn't load attribute cert %s", filename); return cert; } certificate_t *abac_attribute_cert_from_chunk(abac_chunk_t achunk) { chunk_t chunk = { .ptr = achunk.ptr, .len = achunk.len }; if(achunk.len == 0) { errx(1, "Couldn't load attribute chunk, size of 0"); } libabac_init(); certificate_t *cert = cert_get_attr_cert_from_chunk(chunk); if (cert == NULL) errx(1, "Couldn't load attribute chunk"); return cert; } /** * Load an attribute cert with a cert. * Returns true only if the certificate is valid and is issued by the proper * authority. * attribute string is parsed via yyparse call */ static abac_attribute_t *_load_attribute_from_cert(certificate_t *cert) { ietf_attributes_t *attr_cert = NULL; int ret, i; int using_this=0; // get the attr ac_t *ac = (ac_t *)cert; attr_cert = ac->get_groups(ac); if (attr_cert == NULL) { ret = ABAC_CERT_INVALID; goto error; } char *encoded_attr_string=attr_cert->get_string(attr_cert); char *attr_string = abac_decode_string(encoded_attr_string); if(debug) fprintf(stderr, "string to be yyparse..(%d)(%s)\n",strlen(attr_string),attr_string); if (attr_string == NULL) { ret = ABAC_CERT_INVALID; goto error; } abac_id_credential_t *id_cred; abac_id_t *issuer_id; /* reset lex input line, call into yacc parser */ abac_aspect_t *head_aspect = NULL; abac_aspect_t *tail_aspect = NULL; int rc=abac_yy_parse(NULL, attr_string, &head_aspect, &tail_aspect, &using_this); if (rc) { ret = ABAC_CERT_INVALID; goto error; } // get the issuer based on keyid char *principalname = abac_aspect_principal_principalname(head_aspect); if(debug) fprintf(stderr, "LOOKING for %s\n", principalname); id_cred = abac_id_credential_lookup(principalname); if(id_cred == NULL) { ret = ABAC_CERT_MISSING_ISSUER; if(debug) fprintf(stderr, "can not find %s in id_creds\n", principalname); goto error; } issuer_id=abac_id_credential_id(id_cred); if (issuer_id == NULL) { ret = ABAC_CERT_MISSING_ISSUER; if(debug) fprintf(stderr, "can not find %s in id_creds\n", principalname); goto error; } // make sure the issuer's signed it ret = verify_signature(abac_id_cert(issuer_id), cert); if (!ret) { abac_yy_set_error_code(ABAC_RT_CERT_BAD_SIG); ret=ABAC_CERT_BAD_SIG; goto error; } // at this point we know we have a good attribute cert baked it in abac_attribute_t *attr=abac_attribute_new(issuer_id, cert, cert->get_ref(cert)); abac_attribute_set_head(attr, head_aspect); abac_attribute_add_tail(attr, tail_aspect); abac_attribute_set_using_this(attr, using_this); // free up some stuff attr_cert->destroy(attr_cert); return attr; error: if (cert) cert->destroy(cert); errx(1, "fail to extract attribute from a cert properly\n"); } abac_attribute_t *abac_attribute_from_chunk(abac_chunk_t achunk) { certificate_t *cert=abac_attribute_cert_from_chunk(achunk); abac_attribute_t *ret=_load_attribute_from_cert(cert); return ret; } // returns 0 if the cert hasn't been baked int abac_attribute_baked(abac_attribute_t *ptr) { return ptr->cert != NULL; } int abac_attribute_write_cert(abac_attribute_t *ptr, FILE *out) { assert(ptr != NULL); if (ptr->cert == NULL) return 0; // write to file chunk_t encoding = chunk_empty; int rc=ptr->cert->get_encoding(ptr->cert,CERT_ASN1_DER,&encoding); if(rc) { fwrite(encoding.ptr, encoding.len, 1, out); free(encoding.ptr); return 1; } return 0; } abac_attribute_t *abac_attribute_dup(abac_attribute_t *ptr) { assert(ptr); abac_id_dup(ptr->issuer_id); ++ptr->refcount; return ptr; } static certificate_t *_cert_copy(certificate_t *cert) { if(cert==NULL) return NULL; chunk_t encoding = chunk_empty; int rc=cert->get_encoding(cert,CERT_ASN1_DER,&encoding); certificate_t *ncert = cert_get_attr_cert_from_chunk(encoding); return ncert; } /* deep copy of a attribute structure, this is used by libabac api so that we can have a local control of the data within libabac */ abac_attribute_t *abac_attribute_copy(abac_attribute_t *ptr) { assert(ptr); abac_aspect_t *tmp; abac_attribute_t *nptr = _abac_attribute_init(); nptr->issuer_id = abac_id_copy(ptr->issuer_id); nptr->head=abac_aspect_copy(abac_attribute_head(ptr)); nptr->tail=abac_aspect_copy(abac_attribute_tail(ptr)); nptr->validity= ptr->validity; nptr->using_this = ptr->using_this; certificate_t *attr_cert=_cert_copy(ptr->cert); nptr->cert=attr_cert; // already baked.. if(attr_cert) nptr->issuer_cert= attr_cert->get_ref(attr_cert); nptr->refcount=ptr->refcount+1; return nptr; } void abac_attribute_free(abac_attribute_t *ptr) { int i; if (ptr == NULL) return; --ptr->refcount; if (ptr->refcount > 0) return; if(ptr->issuer_id) abac_id_free(ptr->issuer_id); if(ptr->head) abac_aspect_free(ptr->head); if(ptr->tail) abac_aspect_free(ptr->tail); if(ptr->cert) DESTROY_IF(ptr->cert); if(ptr->issuer_cert) DESTROY_IF(ptr->issuer_cert); free(ptr); } char *abac_attribute_string(abac_attribute_t *ptr) { char *head=abac_aspect_string_with_condition(ptr->head); char *tail=abac_aspect_string_with_condition(ptr->tail); if(head==NULL || tail==NULL) errx(1, "attribute string, head and tail can not be NULL"); char *tmp=NULL; asprintf(&tmp,"%s<-%s",head,tail); return tmp; } char *abac_attribute_typed_string(abac_attribute_t *ptr) { char *head=abac_aspect_typed_string_with_condition(ptr->head); char *tail=abac_aspect_typed_string_with_condition(ptr->tail); if(head==NULL || tail==NULL) errx(1, "attribute typed string, head and tail can not be NULL"); char *tmp=NULL; asprintf(&tmp,"%s<-%s",head,tail); return tmp; } /********************************************************************/ certificate_t *abac_attribute_issuer_cert(abac_attribute_t *ptr) { assert(ptr); return ptr->issuer_cert; } abac_aspect_t *abac_attribute_head(abac_attribute_t *ptr) { assert(ptr); return ptr->head; } int abac_attribute_is_role(abac_attribute_t *ptr) { assert(ptr); assert(ptr->head); return abac_aspect_is_role(ptr->head); } abac_attribute_t *abac_attribute_set_head(abac_attribute_t *ptr, abac_aspect_t *head) { assert(ptr); ptr->head=head; return ptr; } void abac_attribute_set_using_this(abac_attribute_t *ptr, int using_this) { assert(ptr); ptr->using_this=using_this; } int abac_attribute_get_this(abac_attribute_t *ptr) { assert(ptr); return ptr->using_this; } abac_aspect_t *abac_attribute_tail(abac_attribute_t *ptr) { assert(ptr); return ptr->tail; } abac_attribute_t *abac_attribute_add_tail(abac_attribute_t *ptr, abac_aspect_t *tail) { assert(ptr); assert(tail); /* type of head and tail has to match */ abac_aspect_t *head=abac_attribute_head(ptr); if(debug) { fprintf(stderr, "head->(%s)\n",abac_aspect_type_string(head)); fprintf(stderr, "tail->(%s)\n",abac_aspect_type_string(tail)); } if(abac_aspect_is_intersecting(tail)) { if(debug) { fprintf(stderr, "tail is intersection \n"); fprintf(stderr, "tail is (%s)\n", abac_aspect_string_with_condition(tail)); } if(abac_aspect_intersecting_aspect_type(tail) != abac_aspect_aspect_type(head)) errx(1, "head and intersecting tail's aspect type does not match"); } else { if(abac_aspect_aspect_type(head) != abac_aspect_aspect_type(tail)) { if(debug) fprintf(stderr, "tail is (%s)\n", abac_aspect_string_with_condition(tail)); errx(1, "head and tail's aspect type does not match"); } } if(ptr->tail == NULL) { ptr->tail=abac_aspect_dup(tail); } else { if(abac_aspect_is_intersecting(ptr->tail)) { abac_aspect_add_intersecting_aspect(ptr->tail, tail); } else { /* special case.. if there is a tail there already and it is not an intersecting tail, need to turn this into an intersecting tails */ abac_aspect_t *nptr=abac_aspect_intersection_new(ptr->tail); abac_aspect_add_intersecting_aspect(nptr, tail); ptr->tail=nptr; } } return ptr; } certificate_t *abac_attribute_cert(abac_attribute_t *ptr) { assert(ptr); return ptr->cert; } int abac_attribute_lastone(abac_attribute_t *ptr) { assert(ptr); if(ptr->refcount == 1) return 1; return 0; } abac_aspect_t **abac_attribute_tail_vectorized(abac_attribute_t *ptr) { abac_aspect_t **tails=NULL; abac_aspect_t *tail=ptr->tail; abac_list_t *list=NULL; int cnt=0; if(tail != NULL) { if(!abac_aspect_is_intersecting(tail)) { tails = abac_xmalloc(sizeof(abac_aspect_t *) * 2); tails[0] = abac_aspect_dup(tail); tails[1] = NULL; if(debug) { fprintf(stderr, "abac_attribute_tail_vectorized, only 1 tail\n"); } return tails; } else { abac_list_t *list=abac_aspect_prereqs(tail); cnt=abac_list_size(list); } } // make the array (leave space to NULL terminate it) // n.b., even if the list is empty, we still return an array that // only contains the NULL terminator tails = abac_xmalloc(sizeof(abac_aspect_t *) * (cnt + 1)); abac_aspect_t *cur; int i = 0; if(i