#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(Role &) constructor that takes a constraining role [role:?R[{role-constraint}] Constraint(Oset &) constructor that takes a constraining oset [oset:?O[{oset-constraint}] [urn:?F[keyid:$alpha_keyid].oset:documents([string:?P])] Constraint(abac_condition_t *) constructor that takes a abac_condition_t structure Constraint(char *) constructor that takes one of following string as its vartype to set up a range constraint: "integer" "urn" "float" "boolean" "time" "string" It should be followed with one or many of following utility calls. ***/ Constraint(Role& role); Constraint(Oset& oset); Constraint(abac_condition_t *constraint): m_constraint(abac_condition_dup(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); } /*** void add_constraint_time_max(char*) void add_constraint_time_min(char*) utility routines to setup a time range constraint, takes quoted string values, beyond T is optional [time:?M["20201101T182930"]] [time:?M["20201101T"]] void add_constraint_time_target(char*) utility routine to setup a time list constraint [time:?F["20120228T080000" .. "20120228T090000"]] ***/ 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); } /*** void add_constraint_urn_target(char*) utility routine to setup a an urn list constraint [urn:?U["fileA","http://fileB"]] void add_constraint_string_target(char*) utility routine to setup a a string list constraint [string:?S["abc",'efg',"hij"]] void add_constraint_boolean_target(char*) utility routine to setup a a boolean list constraint [boolean:?B['true']] ***/ void add_constraint_urn_target(char* val) { abac_condition_add_range_urn_item(m_constraint,val); } void add_constraint_string_target(char* val) { abac_condition_add_range_string_item(m_constraint,val); } void add_constraint_boolean_target(char* val) { abac_condition_add_range_boolean_item(m_constraint,val); } /*** char *string() const returns literal string of the constraint char *typed_string() const returns typed literal string of the constraint ***/ char *string() const { return abac_condition_string(m_constraint); } char *typed_string() const { return abac_condition_typed_string(m_constraint); } /*** abac_condition_t *constraint() returns internal constraint structure ***/ abac_condition_t *constraint() { return m_constraint; } private: abac_condition_t *m_constraint; }; /*** ABAC::DataTerm A data term is associated with Role and Oset as a parameter that maybe be instantiated or uninstantiated but being constrained, or as a principal oset term (standalone right handside of an oset policy rule). It holds a pointer to a abac_term_t structure and an optional constraint that may be imposed on this data term if it is indeed an unistantiated variable. ***/ class DataTerm { public: /*** DataTerm() default constructure, do not use, for swig only DataTerm(const DataTerm &) copy constructor, used for cloning a data term ~DataTerm() default destructor ***/ DataTerm() : m_term(NULL) { } // do not use: here for swig DataTerm(const DataTerm &dterm) { m_term =abac_term_dup(dterm.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); } /*** ??? DataTerm(abac_term_t *) constructor to make data term from abac_term_t structure ***/ 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(char*) constructor to make named principal data term for the oset RHS DataTerm(char*, char*, Constraint*) constructor for making a variable data term or an instantiated 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); } 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 returns literal string of the data term char *typed_string() const returns typed literal string of the data term ***/ char *string() const { return abac_term_string(m_term); } char *typed_string() const { return abac_term_typed_string(m_term); } /*** bool is_time() const bool is_string() const bool is_urn() const bool is_integer() const returns true if data term is of certain type ***/ 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); } /*** int add_constraint(const Contraint&) Utiltiy routine to add a constraint to this data term ***/ int add_constraint(const Constraint& cond) { m_cond=new Constraint(cond); abac_term_add_constraint(m_term, m_cond->constraint()); } /*** int type() const returns subtype of the data term char *name() const returns the name of the data term ***/ int type() const { abac_term_type(m_term); } char *name() const { return abac_term_name(m_term); } /*** ??? value char *value() const Not implemented ***/ char *value() const { } /*** abac_term_t *term() returns internal data term structure Constraint *constraint() returns internal constraint structure to the data term ***/ abac_term_t *term() { return m_term; } Constraint *constraint() { return m_cond; } 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__ */