#ifndef __ABAC_HH__ #define __ABAC_HH__ #include #include namespace ABAC { extern "C" { #include "abac.h" } static int debug=0; class Role; class Oset; /*** ABAC::Constraint This is a constraint on a data term. It holds a ptr to a abac_condition_t structure ***/ class Constraint { public: /*** Constraint() default constructure, do not use, for swig only Constraint(const Constraint &) copy constructor, used for cloning a constraint ~Constraint() default destructor ***/ Constraint() : m_constraint(NULL) { } Constraint(const Constraint &constraint) { m_constraint =abac_condition_dup(constraint.m_constraint); } ~Constraint() { if(m_constraint) abac_condition_free(m_constraint); } /*** Constraint(abac_condition_t *) constructor that takes an abac_condition_t Constraint(char *) constructor that takes one of following string as its vartype for a range constraint: "integer" "urn" "float" "boolean" "time" "string" ***/ Constraint(abac_condition_t *constraint): m_constraint(abac_condition_dup(constraint)) { } /* range constraint */ Constraint(char *vartype) { m_constraint=abac_condition_create(vartype); } /*** void add_constraint_integer_max(int) void add_constraint_integer_min(int) Utility routines to setup a integer range constraint [integer:?I[10 .. 20] void add_constraint_integer_target(int) Utility routine to setup a integer list constraint [integer:?I[10,20] ***/ void add_constraint_integer_max(int val) { abac_condition_add_range_integer_item(m_constraint,abac_max_item_type(),val); } void add_constraint_integer_min(int val) { abac_condition_add_range_integer_item(m_constraint,abac_min_item_type(),val); } void add_constraint_integer_target(int val) { abac_condition_add_range_integer_item(m_constraint,abac_target_item_type(),val); } /*** void add_constraint_float_max(float) void add_constraint_float_min(float) Utility routines to setup a float range constraint [float:?F[1.0 .. 2.5] void add_constraint_float_target(float) Utility routine to setup a float list constraint [float:?F[0.5, 2.5] ***/ void add_constraint_float_max(float val) { abac_condition_add_range_float_item(m_constraint,abac_max_item_type(),val); } void add_constraint_float_min(float val) { abac_condition_add_range_float_item(m_constraint,abac_min_item_type(),val); } void add_constraint_float_target(float val) { abac_condition_add_range_float_item(m_constraint,abac_target_item_type(),val); } /* quoted string values */ /* [time:?T["20201101T182930"] */ void add_constraint_time_max(char* val) { abac_condition_add_range_time_item(m_constraint,abac_max_item_type(),val); } void add_constraint_time_min(char* val) { abac_condition_add_range_time_item(m_constraint,abac_min_item_type(),val); } void add_constraint_time_target(char* val) { abac_condition_add_range_time_item(m_constraint,abac_target_item_type(),val); } /* [urn:?U["fileA","http://fileB"] -only listed range */ void add_constraint_urn_target(char* val) { abac_condition_add_range_urn_item(m_constraint,val); } /* [string:?S["abc",'efg',"hij"] -only listed range */ void add_constraint_string_target(char* val) { abac_condition_add_range_string_item(m_constraint,val); } /*[boolean:?B['true']] */ void add_constraint_boolean_target(char* val) { abac_condition_add_range_boolean_item(m_constraint,val); } /* [oset:?O[{oset-constraint}] */ /* [role:?R[{role-constraint}] */ /* [urn:?F[keyid:$alpha_keyid].oset:documents([string:?P])] */ /* role constraint */ Constraint(Role *role); /* oset constraint */ Constraint(Oset *oset); char *string() const { return abac_condition_string(m_constraint); } char *typed_string() const { return abac_condition_typed_string(m_constraint); } abac_condition_t *constraint() { return m_constraint; } private: abac_condition_t *m_constraint; }; /* XXX need to track the principal [keyid:alice] so can generate the id_type_clause within attribute clause */ class DataTerm { public: DataTerm() : m_term(NULL) { } // do not use: here for swig DataTerm(abac_term_t *term) { m_term=abac_term_dup(term); abac_condition_t *constraint=abac_term_constraint(m_term); if(constraint) m_cond=new Constraint(constraint); else m_cond=NULL; } DataTerm(const DataTerm &term) { m_term =abac_term_dup(term.m_term); abac_condition_t *constraint=abac_term_constraint(m_term); if(constraint) m_cond=new Constraint(constraint); else m_cond=NULL; } ~DataTerm() { if(m_term) abac_term_free(m_term); if(m_cond) free(m_cond); } /* this is for named principal data term */ DataTerm(char *sha) { if(debug) printf("adding a Dataterm named principal(%s)\n",sha); int isnamed=1; int type=e_TERM_PRINCIPAL; m_term=abac_term_named_create(type,sha); } /* can be an a variable data term or a specific value */ DataTerm(char* typenm, char *name, Constraint *cond=NULL) { int type=abac_verify_term_type(typenm); if(debug) printf("adding a Dataterm (%s)\n",name); if(type==ABAC_TERM_FAIL) abac_errx(1, "DataTerm, fail to create the term"); if(cond) { m_cond=cond; m_term=abac_term_create(type,name,cond->constraint()); } else { m_cond=NULL; m_term=abac_term_create(type,name,NULL); } } char *string() const { return abac_term_string(m_term); } char *typed_string() const { return abac_term_typed_string(m_term); } bool is_time() const { return abac_term_is_time_type(m_term); } bool is_string() const { return abac_term_is_string_type(m_term); } bool is_urn() const { return abac_term_is_urn_type(m_term); } bool is_integer() const { return abac_term_is_integer_type(m_term); } /* Add a constraint to this term. */ int add_constraint(Constraint& cond) { m_cond=new Constraint(cond); abac_term_add_constraint(m_term, m_cond->constraint()); } int type() { abac_term_type(m_term); } char *name() { return abac_term_name(m_term); } char *value() { /* ??? value XXX */ } Constraint *constraint() { return m_cond; } abac_term_t *term() { return m_term; } private: abac_term_t *m_term; Constraint *m_cond; }; class Role { public: Role() : m_role(NULL) { } // do not use: here for swig Role(abac_aspect_t *role): m_role(abac_aspect_dup(role)) { } Role(char *principal_name) : m_role(abac_aspect_role_principal_create(principal_name)) { } Role(char *principal_name, char *role_name) : m_role(abac_aspect_role_create(principal_name, role_name)) { } Role(const Role &role) { m_role = abac_aspect_dup(role.m_role); } ~Role() { if (m_role) abac_aspect_free(m_role); } bool is_principal() const { return abac_aspect_is_principal(m_role); } bool is_linking() const { return abac_aspect_is_linking(m_role); } char *string() const { return abac_aspect_string(m_role); } char *typed_string() { char* string=abac_aspect_typed_string(m_role); return string; } char *linked_role() const { return abac_aspect_linked_role_name(m_role); } char *role_name() const { return abac_aspect_aspect_name(m_role); } char *principal() const { return abac_aspect_principal_name(m_role); } /* Add a data term to this name. */ int add_data_term(DataTerm& d) { abac_aspect_add_param(m_role, d.term()); return 1; } /* Return the DataTerms bound to this name. If the name is returned in a proof, these will all have values. */ std::vector get_data_terms(bool &success) { abac_term_t **terms, **end; int i; terms = abac_param_list_vectorize(abac_aspect_aspect_params(m_role)); for (i = 0; terms[i] != NULL; ++i) ; end = &terms[i]; std::vector dataterms = std::vector(terms, end); abac_terms_free(terms); success=1; return dataterms; } int add_linking_data_term(DataTerm& d) { abac_aspect_add_linked_param(m_role, d.term()); return 1; } std::vector get_linked_data_terms(bool &success) { abac_term_t **terms, **end; int i; terms = abac_param_list_vectorize(abac_aspect_linked_role_params(m_role)); for (i = 0; terms[i] != NULL; ++i) ; end = &terms[i]; std::vector dataterms = std::vector(terms, end); abac_terms_free(terms); success=1; return dataterms; } abac_aspect_t *role() {return m_role;} private: abac_aspect_t *m_role; }; class Oset { public: Oset() : m_oset(NULL) { } // do not use: here for swig Oset(abac_aspect_t *oset): m_oset(abac_aspect_dup(oset)) { } Oset(char *oset_name) : m_oset(abac_aspect_oset_principal_create(oset_name)) { } Oset(char *principal_name, char *oset_name) : m_oset(abac_aspect_oset_create(principal_name, oset_name)) { } Oset(DataTerm& d) : m_oset(abac_aspect_oset_object_create(d.term())) { } Oset(const Oset &oset) { m_oset =abac_aspect_dup(oset.m_oset); } ~Oset() { if(m_oset) abac_aspect_free(m_oset); } bool is_object() const { return abac_aspect_is_object(m_oset); } bool is_principal() const { return abac_aspect_is_principal(m_oset); } bool is_linking() const { return abac_aspect_is_linking(m_oset); } char *string() const { return abac_aspect_string(m_oset); } char *typed_string() { char* string=abac_aspect_typed_string(m_oset); return string; } char *linked_role() const { return abac_aspect_linked_role_name(m_oset); } char *oset_name() const { return abac_aspect_aspect_name(m_oset); } char *principal() const { return abac_aspect_principal_name(m_oset); } char *object() const { return abac_aspect_object_name(m_oset); } /* Add a data term to this name. */ int add_data_term(DataTerm& d) { abac_aspect_add_param(m_oset, d.term()); return 1; } /* Return the DataTerms bound to this name. If the name is returned in a proof, these will all have values. */ std::vector get_data_terms(bool &success) { abac_term_t **terms, **end; int i; terms = abac_param_list_vectorize(abac_aspect_aspect_params(m_oset)); for (i = 0; terms[i] != NULL; ++i) ; end = &terms[i]; std::vector dataterms = std::vector(terms, end); abac_terms_free(terms); success=1; return dataterms; } int add_linking_data_term(DataTerm& d) { abac_aspect_add_linked_param(m_oset, d.term()); return 1; } std::vector get_linked_data_terms(bool &success) { abac_term_t **terms, **end; int i; terms = abac_param_list_vectorize(abac_aspect_linked_role_params(m_oset)); for (i = 0; terms[i] != NULL; ++i) ; end = &terms[i]; std::vector dataterms = std::vector(terms, end); abac_terms_free(terms); success=1; return dataterms; } abac_aspect_t *oset() {return m_oset;} private: abac_aspect_t *m_oset; }; class ID { public: ID() : m_id(NULL) { } // do not use: here for swig ID(abac_id_t *id): m_id(abac_id_dup(id)) { } ID(abac_id_credential_t *idcred) { if(idcred) m_id=abac_id_dup(abac_id_credential_id(idcred)); else m_id=NULL; } ID(const ID &id) { m_id =abac_id_dup(id.m_id); } ~ID() { if(m_id) abac_id_free(m_id); } /* load an ID cert from a file Will throw an exception if the cert cannot be loaded */ ID(char *filename) { m_id=abac_id_from_file(filename); if(m_id==NULL) abac_errx(1, "Id creation from filename failed"); } /* generates a new ID with the supplied CN and validity period - CN must be alphanumeric and begin with a letter - validity must be at least one second Will throw an exception if either of the above is violated */ ID(char *cn, int validity) { int rt=abac_id_generate(&m_id, cn, validity); if(rt != ABAC_ID_SUCCESS) abac_errx(1, "Id creation failed"); } /* loads the private key associated with the cert will throw an exception if the key cannot be loaded */ void load_privkey(char *filename) { int rt=abac_id_load_privkey_file(m_id, filename); if(rt != 1) abac_errx(1, "Failed to load private key"); } abac_id_t *id() { return m_id; } /* returns the SHA1 keyid of the cert */ char *keyid() { return abac_id_keyid(m_id); } /* returns the CN */ char *name() { return abac_id_cn(m_id); } /* returns true if the ID has an associated private key */ bool has_privkey() { return abac_id_has_privkey(m_id); } /* writes a PEM-encoded cert to the file handle */ void write_cert(FILE *out) { abac_id_write_cert(m_id, out); } /* writes a PEM-encoded cert to a file named out */ void write_cert(char *filename) { FILE *out = fopen(filename, "a"); write_cert(out); fclose(out); } /* writes a PEM-encoded private key to the file handle throws an exception if no private key is loaded */ void write_privkey(FILE *out) { if(!has_privkey()) abac_errx(1, "No privkey to write"); abac_id_write_privkey(m_id, out); } /* writes a PEM-encoded private key a file named out throws an exception if no private key is loaded */ void write_privkey(char *filename) { FILE *out = fopen(filename, "a"); write_privkey(out); fclose(out); } /* returns a DER-encoded binary representation of the X.509 ID cert associated with this ID. can be passed to libabac's Context::load_id_chunk() */ abac_chunk_t cert_chunk() { return abac_id_cert_chunk(m_id); } char *string() { char *tmp=NULL; if(has_privkey()) asprintf(&tmp,"(%s,%s,y)",abac_id_name(m_id),abac_id_idtype_string(m_id)); else asprintf(&tmp,"(%s,%s,n)",abac_id_name(m_id),abac_id_idtype_string(m_id)); return tmp; } public: abac_id_t *m_id; }; /* N.B., The way you use this class is by instantiating the object, adding subjects to it, and then baking it. Only once it's baked can you access the X.509 cert. Once it's been baked you can no longer add subjects to it. */ class Attribute { public: Attribute() : m_attr(NULL) { } // do not use: here for swig Attribute(abac_attribute_t *attr): m_attr(abac_attribute_dup(attr)) { } Attribute(abac_credential_t *cred) { m_attr=abac_attribute_dup(abac_credential_attribute(cred)); } Attribute(const Attribute &id) { m_attr =abac_attribute_dup(id.m_attr); } ~Attribute() { if(m_attr) abac_attribute_free(m_attr); } /* Create an object to be signed by the given issuer with the given role and validity period An exception will be thrown if: - the issuer has no private key - the Head is invalid - the validity period is invalid (must be >= 1 second) */ Attribute(Role& head, int validity) { int rt=abac_attribute_create(&m_attr, head.role(), NULL, validity); if(rt!=ABAC_ATTRIBUTE_SUCCESS) abac_errx(1, "attribute(role), unable to make an attribute"); } Attribute(Oset& head, int validity) { int rt=abac_attribute_create(&m_attr, head.oset(), NULL, validity); if(rt!=ABAC_ATTRIBUTE_SUCCESS) abac_errx(1, "attribute(oset), unable to make an attribute"); } bool add_tail(Role& tail) { if(abac_attribute_add_tail(m_attr, tail.role())) return 1; else return 0; } bool add_tail(Oset& tail) { if(abac_attribute_add_tail(m_attr, tail.oset())) return 1; else return 0; } char *head_string() { abac_aspect_t *head=abac_attribute_head(m_attr); char *string=abac_aspect_string(head); return string; } char *tail_string() { abac_aspect_t *tail=abac_attribute_tail(m_attr); char *string=abac_aspect_string(tail); return string; } char *head_typed_string() { abac_aspect_t *head=abac_attribute_head(m_attr); char *string=abac_aspect_typed_string(head); return string; } char *tail_typed_string() { abac_aspect_t *tail=abac_attribute_tail(m_attr); char *string=abac_aspect_typed_string(tail); return string; } char *string() { char *head=head_string(); char *tail=tail_string(); if(head==NULL || tail==NULL) abac_errx(1, "attribute string, head and tail can not be NULL"); char *tmp=NULL; asprintf(&tmp,"%s<-%s",head,tail); return tmp; } char *typed_string() { char *head=head_typed_string(); char *tail=tail_typed_string(); if(head==NULL || tail==NULL) abac_errx(1, "attribute string, head and tail can not be NULL"); char *tmp=NULL; asprintf(&tmp,"%s<-%s",head,tail); return tmp; } const Role &role_head() { abac_aspect_t *head=abac_attribute_head(m_attr); static Role role=Role(head); return role; } const Oset &oset_head() { abac_aspect_t *head=abac_attribute_tail(m_attr); static Oset oset=Oset(head); return oset; } std::vector role_tails(bool &success) { abac_aspect_t **tails, **end; int i; tails = abac_attribute_tail_vectorized(m_attr); for (i = 0; tails[i] != NULL; ++i) ; end = &tails[i]; std::vector roles = std::vector(tails, end); abac_aspects_free(tails); success=1; return roles; } std::vector oset_tails(bool &success) { abac_aspect_t **tails, **end; int i; tails = abac_attribute_tail_vectorized(m_attr); for (i = 0; tails[i] != NULL; ++i) ; end = &tails[i]; std::vector osets = std::vector(tails, end); success=1; abac_aspects_free(tails); return osets; } abac_attribute_t *attribute() { return m_attr; } /* Generate the cert. Call this after you've added subjects to your cert. This returns false if there are no subjects This will throw an exception if the cert's already been baked. */ bool bake() { /* can not bake in ABAC_CN mode */ if(USE("ABAC_CN")) abac_errx(1, "bake, can not bake the cert with env(ABAC_CN) set"); int rt=abac_attribute_bake(m_attr); if(rt!=1) abac_errx(1, "bake, can not bake the cert"); } /* Returns true iff the cert has been baked. */ bool baked() { return abac_attribute_baked(m_attr); } /* Write the DER-encoded X.509 attribute cert to the open file handle Throws an exception if the cert isn't baked */ void write_cert(FILE *out) { int rt= abac_attribute_write(m_attr,out); if(!rt) abac_errx(1, "write, cert is not baked"); } /* Write the DER-encoded X.509 attribute cert to a file named out Throws an exception if the cert isn't baked */ void write_cert(char *filename) { FILE *out = fopen(filename, "w"); printf("writing to %s\n", filename); write_cert(out); printf("done writing to %s\n", filename); fclose(out); } /* returns a DER-encoded binary representation of the X.509 attribute cert associated with this cert Throws an exception if the cert isn't baked the chunk can be passed to libabac's Context::load_attribute_chunk() */ abac_chunk_t cert_chunk() { return abac_attribute_cert_chunk(m_attr); } /* generate yap clauses and injected into db */ int consume() { /* attribute needs to be baked */ if(!baked()) { return ABAC_ATTRIBUTE_FAIL; } } private: abac_attribute_t *m_attr; }; class Context { public: Context() { m_ctx = abac_context_new(); m_abac_version=strdup("1.0"); } Context(const Context &context) { m_ctx = abac_context_dup(context.m_ctx); m_abac_version=strdup(context.m_abac_version); } ~Context() { abac_context_free(m_ctx); if(m_abac_version) free(m_abac_version); } /* load an identity certificate, returns: ABAC_CERT_SUCCESS successfully loaded ABAC_CERT_INVALID invalid certificate (or file not found) ABAC_CERT_BAD_SIG invalid signature */ void dump_yap() { show_yap_db("dump_yap"); } int load_id(ABAC::ID& id) { return abac_context_load_id_id(m_ctx, id.id()); } int load_id_file(char *filename) { return abac_context_load_id_idkey_file(m_ctx, filename); } int load_id_file(char *filename, char *keyfilename) { return abac_context_load_id_id_file_key_file(m_ctx, filename, keyfilename); } int load_id_chunk(abac_chunk_t cert) { return abac_context_load_id_chunk(m_ctx, cert); } /* load an attribute certificate, returns the same values as above * additionally can return ABAC_CERT_MISSING_ISSUER if the issuer certificate has not been loaded */ int load_attribute(ABAC::Attribute& a) { return abac_context_load_attribute_attribute(m_ctx, a.attribute()); } int load_attribute_file(char *filename) { return abac_context_load_attribute_file(m_ctx, filename); } int load_attribute_chunk(abac_chunk_t cert) { return abac_context_load_attribute_chunk(m_ctx, cert); } /* load a directory full of certificates: first: ${path}/*_ID.{der,pem} as identity certificates then: ${path}/*_attr.der as attribute certificates */ void load_directory(char *path) { abac_context_load_directory(m_ctx, path); } /* run the query: role <-?- principal returns true/false in success returns a proof upon success, partial proof on failure */ /* the string version is for query that is composed by hand with SHA or in non ABAC_CN mode */ std::vector query(char *role, char *principal, bool &success) { abac_credential_t **creds, **end; int i, success_int; creds = abac_context_query(m_ctx, role, principal, &success_int); success = success_int; for (i = 0; creds[i] != NULL; ++i) ; end = &creds[i]; std::vector attributes = std::vector(creds, end); if(debug) printf("query, got rules(%d)\n", i); abac_context_credentials_free(creds); return attributes; } /* another way */ std::vector query(Role &role, Role &p_role, bool &success) { abac_credential_t **creds, **end; int i, success_int; creds = abac_context_query_with_structure(m_ctx, role.role(), p_role.role(), &success_int); success = success_int; for (i = 0; creds[i] != NULL; ++i) ; end = &creds[i]; std::vector attributes = std::vector(creds, end); abac_context_credentials_free(creds); return attributes; } std::vector query(Oset &oset, Oset &p_oset, bool &success) { abac_credential_t **creds, **end; int i, success_int; creds = abac_context_query_with_structure(m_ctx, oset.oset(), p_oset.oset(), &success_int); success = success_int; for (i = 0; creds[i] != NULL; ++i) ; end = &creds[i]; std::vector attributes = std::vector(creds, end); if(debug) printf("query, returning rules(%d)\n", i); abac_context_credentials_free(creds); return attributes; } /* returns a vector of all the credentials loaded in the context */ std::vector context_credentials(bool &success) { abac_credential_t **creds, **end; int i; success = 1; creds = abac_context_credentials(m_ctx); for (i = 0; creds[i] != NULL; ++i) ; end = &creds[i]; std::vector attributes = std::vector(creds, end); if(debug) printf("credentials, got (%d)\n", i); abac_context_credentials_free(creds); if(debug) show_yap_db("calling from context_credentials"); return attributes; } /* returns a vector of all the principals loaded in the context */ std::vector context_principals(bool &success) { abac_id_credential_t **ids, **end; int i; success = 1; ids = abac_context_principals(m_ctx); for (i = 0; ids[i] != NULL; ++i) ; end = &ids[i]; std::vector principals = std::vector(ids, end); if(debug) printf("principals, got (%d)\n", i); abac_context_principals_free(ids); return principals; } char *version() const { return m_abac_version; } private: abac_context_t *m_ctx; char *m_abac_version; }; Constraint::Constraint(Role *role) { m_constraint=abac_condition_create_from_aspect(role->role()); } Constraint::Constraint(Oset *oset) { m_constraint=abac_condition_create_from_aspect(oset->oset()); } } #endif /* __ABAC_HH__ */