/** ** abac_id.c **/ #define _GNU_SOURCE #include #include #include #include #include "abac_util.h" #include "abac_key.h" #include "abac_verifier.h" #include "abac_util_cert.h" static int debug=0; /* load id into a session when id is being created instead of when id is added into a context */ static int early_load_id=1; /* idtypename[idtype] */ static const char * const _idtypename[] = { "badidtype", "keyid" }; static int _idtypename_cnt=1; // abac_id object struct _abac_id_t { int idtype; /* default to string 'keyid' for now */ char *keyid; /* sha */ char *cn; certificate_t *cert; abac_key_t *key; int refcount; }; #define KEY_SUFFIX "_private.pem" #define CERT_SUFFIX "_ID.pem" /* local */ char *abac_id_keyid(abac_id_t*); abac_id_t *abac_id_copy(abac_id_t *id); /****************************************************************/ // // Helper functions below // int abac_idtype_lookup(char *type) { int i; for (i = 1; i <= _idtypename_cnt ; i++) if(strcmp(type,_idtypename[i])==0) return i; return 0; } char *abac_idtype_string(int i) { if(i > _idtypename_cnt) { printf("bad idtypename idx %d\n", i); panic("abac_idtype_string: went out of range on idtypename"); } return (char*) _idtypename[i]; } static char *_create_dn(char *cn) { #define DN "cn=" char *dn = abac_xmalloc(sizeof(DN) + strlen(cn)); memcpy(dn, DN, sizeof(DN)); strcat(dn, cn); return dn; } void abac_id_dump_from_id(abac_id_t *id) { fprintf(stderr,"abac_id_dump_id: \n"); fprintf(stderr,"idtype: %d\n",id->idtype); fprintf(stderr,"keyid: %s\n",id->keyid); fprintf(stderr,"cn: %s\n", id->cn); fprintf(stderr,"refcount: %d\n",id->refcount); if(id->key !=NULL) fprintf(stderr,"has private key\n"); if(id->cert !=NULL) fprintf(stderr,"has cert\n"); } void abac_id_dump_from_cert(certificate_t *cert) { char *ret; fprintf(stderr,"abac_id_dump_from_cert: \n"); int rv = asprintf(&ret, "%Y", cert->get_issuer(cert)); fprintf(stderr,"issuer: %s\n",ret); fprintf(stderr,"keyid: %s\n",cert_get_keyid(cert)); } /** * * Preload ID into sessions' master list before being * loaded into some context * **/ static int _add_into_id_credentials(abac_id_t *id) { char *keyid=abac_id_keyid(id); abac_id_credential_t *idcred=abac_id_credential_lookup(keyid); if(idcred !=NULL) { if(debug) fprintf(stderr,"_add_into_id_credentials: something in there for %s\n",keyid); idcred=abac_id_credential_dup(idcred); } else { if(debug) fprintf(stderr,"_add_into_id_credentials: adding %s\n",keyid); if(early_load_id) { /* this id needed to be deep copyed, because it is going into our session id credential list */ abac_id_t *nid = abac_id_copy(id); idcred=abac_id_credential_new(nid); abac_add_id_cred(idcred); } } } /** * Generate ID certificate. * * validity is measured in seconds (as of 0.2.0) */ static certificate_t *_generate_cert(abac_key_t *keyptr, char *cn, int validity) { // build the DN char *dn_string = _create_dn(cn); libabac_init(); DEBUG_PRINTF("\n_generate_cert: id cert for cn(%s)with dn(%s)\n", cn, dn_string); identification_t *iden = identification_create_from_string(dn_string); if (iden == NULL) errx(1, "couldn't create ID from DN %s", dn_string); free(dn_string); // get the private key private_key_t *private = abac_key_private_key(keyptr); if(private == NULL) errx(1, "couldn't create ID without private key"); public_key_t *public = private->get_public_key(private); if (public == NULL) errx(1, "couldn't get public key from private key"); // create a serial (stolen from strongswan pki) rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); if (!rng) errx(1, "no random number generator"); // random serial chunk_t serial = abac_generate_serial(); // validity period time_t not_before = time(NULL); time_t not_after = not_before + validity; // create! certificate_t *cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_SIGNING_KEY, private, BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, iden, BUILD_NOT_BEFORE_TIME, not_before, BUILD_NOT_AFTER_TIME, not_after, BUILD_SERIAL, serial, BUILD_DIGEST_ALG, HASH_SHA1, BUILD_X509_FLAG, X509_CA, BUILD_PATHLEN, X509_NO_CONSTRAINT, BUILD_END ); if (cert == NULL) errx(1, "couldn't build cert :("); DESTROY_IF(iden); DESTROY_IF(public); DESTROY_IF(private); free(serial.ptr); return cert; } /* from libstrongswan, chunk.h chunk_t chunk_from_base64(chunk_t base64, char *buf); chunk_t chunk_to_base64(chunk_t chunk, char *buf); chunk_t b64 = chunk_to_base64(chunk, NULL); chunk_t chk = chunk_from_base64(base64, NULL); */ #define BYTES_PER_LINE 64 // thx libstrongswan static void _encode_base64(FILE *out, chunk_t encoding) { int start; chunk_t b64 = chunk_to_base64(encoding, NULL); fprintf(out, "-----BEGIN CERTIFICATE-----\n"); for (start = 0; start < b64.len; start += BYTES_PER_LINE) { int left = b64.len - start; int len = left < BYTES_PER_LINE ? left : BYTES_PER_LINE; fwrite(b64.ptr + start, len, 1, out); fprintf(out, "\n"); } fprintf(out, "-----END CERTIFICATE-----\n"); free(b64.ptr); } /****************************************************************/ int abac_id_idtype(abac_id_t *id) { assert(id != NULL); return id->idtype; } char* abac_id_idtype_string(abac_id_t *id) { assert(id != NULL); return abac_idtype_string(id->idtype); } char *abac_id_keyid(abac_id_t *id) { assert(id != NULL); return id->keyid; } char *abac_id_cn(abac_id_t *id) { assert(id != NULL); return id->cn; } static int _abac_id_set_cn(abac_id_t *id, char* cn) { if(cn) { if(id->cn != NULL) free(id->cn); id->cn = abac_xstrdup(cn); } return 0; } char *abac_id_name(abac_id_t *id) { assert(id != NULL); if(USE("ABAC_CN")) return abac_id_cn(id); else return abac_id_keyid(id); } char *abac_id_string(abac_id_t *id) { char *tmp=NULL; if(abac_id_has_privkey(id)) asprintf(&tmp,"(%s,%s,y)",abac_id_name(id),abac_id_idtype_string(id)); else asprintf(&tmp,"(%s,%s,n)",abac_id_name(id),abac_id_idtype_string(id)); return tmp; } /** * Get the issuer of an ID cert. * Returns a malloc'd string that must be free'd. */ char *abac_id_issuer(abac_id_t *id) { char *ret; int rv = asprintf(&ret, "%Y", id->cert->get_issuer(id->cert)); if (rv < 0) err(1, "couldn't malloc string for issuer\n"); return ret; } int abac_id_lastone(abac_id_t *ptr) { assert(ptr); if(ptr->refcount == 1) return 1; return 0; } /** * Gets the subject DN of an ID cert. * Returns a malloc'd string that must be free'd. */ char *abac_id_subject(abac_id_t *id) { char *ret; int rv = asprintf(&ret, "%Y", id->cert->get_subject(id->cert)); if (rv < 0) err(1, "couldn't malloc string for subject\n"); return ret; } /** * Get the validity period. */ void abac_id_validity(abac_id_t *id, time_t *not_before, time_t *not_after) { id->cert->get_validity(id->cert, NULL, not_before, not_after); } certificate_t *abac_id_cert(abac_id_t *id) { assert(id != NULL); return id->cert; } private_key_t *abac_id_privkey(abac_id_t *id) { assert(id != NULL); if(id->key !=0) return abac_key_private_key(id->key); return NULL; } abac_key_t *abac_id_keyptr(abac_id_t *id) { assert(id!=NULL); return id->key; } int abac_id_has_privkey(abac_id_t *id) { if(id && id->key !=0) return 1; return 0; } // convert a chunk to a lowercase binary string // malloc's the string static char *_chunk_to_string(chunk_t chunk) { int i; char *ret = abac_xmalloc(chunk.len * 2 + 1); for (i = 0; i < chunk.len; ++i) sprintf(ret + 2 * i, "%02x", chunk.ptr[i]); return ret; } /** * Write the ID cert to an open file pointer. */ void abac_id_write_cert(abac_id_t *id, FILE *out) { assert(id != NULL); chunk_t encoding = chunk_empty; int rc=id->cert->get_encoding(id->cert,CERT_ASN1_DER,&encoding); if(rc) { _encode_base64(out, encoding); } } /** * Write the private key to a file. * Returns false if there's no private key loaded */ int abac_id_write_privkey(abac_id_t *id, FILE *out) { return abac_key_write_privkey(id->key, out); } /** * Get a DER-encoded chunk representing the cert. */ abac_chunk_t abac_id_cert_chunk(abac_id_t *id) { assert(id->cert); chunk_t encoding = chunk_empty; int rc= id->cert->get_encoding(id->cert, CERT_ASN1_DER, &encoding); abac_chunk_t ret= { encoding.ptr, encoding.len }; return ret; } /** * Get a PEM-encoded chunk representing the private key */ abac_chunk_t abac_id_privkey_chunk(abac_id_t *id) { chunk_t tmp=chunk_empty; if(id->key) tmp=abac_key_chunk(id->key); abac_chunk_t ret= {tmp.ptr, tmp.len}; return ret; } void abac_id_print_key_chunk(abac_id_t *id) { if(id->key) print_abac_key("abac_id_print_key_chunk",id->key); } /** * Copy an abac ID. Actually just increases its reference count. */ abac_id_t *abac_id_dup(abac_id_t *id) { if(id) ++id->refcount; return id; } void abac_id_free(abac_id_t *id) { if (id == NULL) return; DEBUG_PRINTF("\nabac_id_free: deleting id with cn(%s)with sha(%s) at(%ld)\n", id->cn, id->keyid, (long)id); --id->refcount; if (id->refcount > 0) return; // free once the reference count reaches 0 DESTROY_IF(id->cert); if(id->key) { DEBUG_PRINTF("abac_id_free: deleting id with cn(%s)at(%ld)key(%ld)\n", id->cn, (long)id, (long)id->key); abac_key_free(id->key); } free(id->keyid); free(id->cn); free(id); } /****************************************************************/ /** * Default private key filename. Value must be freed by caller. */ char *abac_id_privkey_filename(abac_id_t *id) { assert(id != NULL); assert(id->cn != NULL); /* this cn has pCN attached to it */ char *filename = NULL; char* ptr=id->cn; asprintf(&filename, "%s%s",(ptr+1),KEY_SUFFIX); return filename; } /** * Get the default filename for the cert. Value must be freed by caller. */ char *abac_id_cert_filename(abac_id_t *id) { assert(id != NULL); assert(id->cn != NULL); /* this cn has pCN attached to it */ char *filename = NULL; char* ptr=id->cn; asprintf(&filename, "%s%s",(ptr+1),CERT_SUFFIX); return filename; } /****************************************************************/ /* ** Create an ID from tidbits info */ abac_id_t *abac_id_new(int idtype,char *keyid, char *cn, certificate_t *cert) { if(debug) fprintf(stderr,"abac_id_new, keyid(%s), cn(%s) keyid from cert(%s)\n", keyid,cn, cert_get_keyid(cert)); abac_id_t *id = abac_xmalloc(sizeof(abac_id_t)); id->idtype = idtype; id->keyid = abac_xstrdup(keyid); id->cn = abac_xstrdup(cn); id->cert = cert; id->key = NULL; id->refcount = 1; _add_into_id_credentials(id); return id; } static certificate_t *_certificate_copy(certificate_t *cert) { chunk_t encoding = chunk_empty; int rc=cert->get_encoding(cert,CERT_ASN1_DER,&encoding); certificate_t *ncert = cert_get_id_cert_from_chunk(encoding); return ncert; } /* hard duplicate of an id This is used by the libabac interface for case the where abac_context_load_id(abac_id_t *id) is called. We want the structure being kept in libabac to be locally held so that we are not subject to destructor being called on the structure by some other system or being called in certain way that is not fully controlled by libabac. */ abac_id_t *abac_id_copy(abac_id_t *id) { assert(id); abac_id_t *nid = abac_xmalloc(sizeof(abac_id_t)); nid->idtype = id->idtype; nid->keyid = abac_xstrdup(id->keyid); nid->cn = abac_xstrdup(id->cn); nid->cert = _certificate_copy(id->cert); if(id->key) nid->key = abac_key_copy(id->key); else nid->key=NULL; nid->refcount = id->refcount+1; return nid; } abac_id_t *abac_id_keyid_new(char *keyid, char *cn, certificate_t *cert) { return abac_id_new(e_KEYID,keyid,cn,cert); } static abac_id_t *_abac_id_from_cert(certificate_t *cert) { abac_id_t *id = abac_xmalloc(sizeof(abac_id_t)); id->idtype = e_KEYID; id->keyid = NULL; id->cn = NULL; id->cert = cert; id->key = NULL; id->keyid = cert_get_keyid(id->cert); // get the CN from the cert id_part_t type; chunk_t data; identification_t *cert_id = id->cert->get_subject(id->cert); enumerator_t *id_enum = cert_id->create_part_enumerator(cert_id); while (id_enum->enumerate(id_enum, &type, &data)) if (type == ID_PART_RDN_CN) { id->cn = abac_xmalloc(data.len + 2); id->cn[0]='p'; memcpy(&id->cn[1], data.ptr, data.len); id->cn[data.len+1] = 0; } id_enum->destroy(id_enum); if(id->cn != NULL) { if(debug) fprintf(stderr, "_abac_id_from_cert, found cn (%s)\n", id->cn); } else { /* have to prepend a p */ char *tmp=NULL; asprintf(&tmp,"p%s",id->keyid); _abac_id_set_cn(id,tmp); free(tmp); if(debug) fprintf(stderr,"_abac_id_from_cert, did not find cn, set it to (%s)\n", id->cn); } id->refcount = 1; _add_into_id_credentials(id); return id; } /** * Create an ID cert from a file. * this one does not add id into id hash list */ abac_id_t *abac_id_from_file(char *filename) { /* make sure file exists */ if(!file_exist(filename)) return NULL; libabac_init(); certificate_t *cert = cert_get_id_cert_from_file(filename); if (cert == NULL) return NULL; return _abac_id_from_cert(cert); } /** * Create an ID cert from a chunk. */ abac_id_t *abac_id_from_chunk(abac_chunk_t achunk) { chunk_t chunk = { .ptr = achunk.ptr, .len = achunk.len }; certificate_t *cert = cert_get_id_cert_from_chunk(chunk); if (cert == NULL) return NULL; return _abac_id_from_cert(cert); } /** * Generate an ID with the specified CN and validity. * * validity is measured in seconds (as of 0.2.0) */ int abac_id_generate(abac_id_t **ret, char *cn, int validity) { if (cn == NULL || !abac_validate_clean_name(cn)) return ABAC_ID_GENERATE_INVALID_CN; DEBUG_PRINTF("abac_id_generate: generating id with cn(%s)\n",cn); if (validity < 0) return ABAC_ID_GENERATE_INVALID_VALIDITY; if (validity == 0) validity = 1080 * 86400; abac_id_t *id = abac_xmalloc(sizeof(abac_id_t)); char *tmp=NULL; asprintf(&tmp,"p%s",cn); id->idtype = e_KEYID; id->cn = tmp; id->key = abac_key_generate(); id->cert = _generate_cert(id->key, cn, validity); id->keyid = cert_get_keyid(id->cert); id->refcount = 1; _add_into_id_credentials(id); *ret = id; return ABAC_ID_SUCCESS; } /** * Generate an ID with the specified CN and validity and a passphrase. * * validity is measured in seconds (as of 0.2.0) */ int abac_id_generate_with_key(abac_id_t **ret, char *cn, int validity, char *filename, char *pfile) { if (cn == NULL || !abac_validate_clean_name(cn)) return ABAC_ID_GENERATE_INVALID_CN; if(debug) { fprintf(stderr,"abac_id_generate: generating id with cn(%s) and privkey(%s)", cn, filename); if(pfile) fprintf(stderr,"pfile(%s)\n",pfile); else fprintf(stderr,"\n"); } if (validity < 0) return ABAC_ID_GENERATE_INVALID_VALIDITY; if (validity == 0) validity = 1080 * 86400; chunk_t pp=chunk_empty; if(pfile && strlen(pfile)!=0) { if(!file_exist(pfile)) errx(1, "passphrase file does not exist!!\n"); pp=extract_potato(filename,pfile); } abac_id_t *id = abac_xmalloc(sizeof(abac_id_t)); char *tmp=NULL; asprintf(&tmp,"p%s",cn); id->idtype = e_KEYID; id->cn = tmp; id->key = abac_key_file_new(filename,pp); id->cert = _generate_cert(id->key, cn, validity); id->keyid = cert_get_keyid(id->cert); id->refcount = 1; _add_into_id_credentials(id); *ret = id; return ABAC_ID_SUCCESS; } /* special handling, need to update the key of the id copy stored in session's id credential list */ static void _load_privkey_to_master_list(char *keyid, abac_key_t *key) { abac_id_credential_t *idcred=abac_id_credential_lookup(keyid); if(idcred==NULL) { panic(1,"_load_privkey_to_master_list: missing id in masterlist(%s)\n", keyid); } abac_id_t *id=abac_id_credential_id(idcred); id->key = abac_key_copy(key); } int abac_id_load_privkey_file(abac_id_t *ptr, char *keyfile) { assert(ptr); ptr->key=abac_key_file_new(keyfile, chunk_empty); _load_privkey_to_master_list(abac_id_keyid(ptr), ptr->key); return 1; } int abac_id_load_enc_privkey_file(abac_id_t *ptr, char *keyfile, char *pfile) { assert(ptr); chunk_t pp=extract_potato(keyfile,pfile); ptr->key=abac_key_file_new(keyfile, pp); _load_privkey_to_master_list(abac_id_keyid(ptr), ptr->key); return 1; } int abac_id_load_privkey_chunk(abac_id_t *ptr, chunk_t keychunk) { assert(ptr); ptr->key=abac_key_chunk_new(keychunk, chunk_empty); _load_privkey_to_master_list(abac_id_keyid(ptr), ptr->key); return 1; } int abac_id_load_enc_privkey_chunk(abac_id_t *ptr, chunk_t keychunk, char *pfile) { assert(ptr); chunk_t pp=extract_potato(NULL,pfile); ptr->key=abac_key_chunk_new(keychunk, pp); _load_privkey_to_master_list(abac_id_keyid(ptr), ptr->key); return 1; }