/** ** abac_id.c **/ #include #include #include // include the GNU extension of asprintf #define _GNU_SOURCE #include #include #include "abac_internal.h" #include "abac_util.h" static int debug=0; static int yes=1; #define KEY_SUFFIX "_private.pem" #define CERT_SUFFIX "_ID.pem" extern private_key_t *abac_key_private_key(abac_key_t *ptr); extern abac_key_t *abac_key_file_new(char *filename, chunk_t potato); extern abac_key_t *abac_key_chunk_new(chunk_t keyblob, chunk_t potato); extern chunk_t abac_key_chunk(abac_key_t *); static int _abac_id_set_cn(abac_id_t *id, char* cn); // // 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; }; /****************************************************************/ // // Helper functions below // 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); } else { if(debug) fprintf(stderr,"_add_into_id_credentials: adding %s\n",keyid); if(yes) { abac_verifier_add_id_credential(abac_id_dup(id)); } } } static char *_get_keyid(certificate_t *cert) { // get the keyid x509_t *x509 = (x509_t *)cert; chunk_t keyid = x509->get_subjectKeyIdentifier(x509); chunk_t string = chunk_to_hex(keyid, NULL, 0); return (char *)string.ptr; } 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; } /** * 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(); identification_t *id = identification_create_from_string(dn_string); if (id == 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, id, 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(id); DESTROY_IF(public); DESTROY_IF(private); free(serial.ptr); return cert; } #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) { return id->key; } int abac_id_has_privkey(abac_id_t *id) { if(id && id->key !=0) return 1; return 0; } /** * 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); free(encoding.ptr); } } /** * 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; --id->refcount; if (id->refcount > 0) return; if(debug) fprintf(stderr,"making abac_id_free call on (%s)!!!\n", id->cn); // free once the reference count reaches 0 DESTROY_IF(id->cert); if(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) { 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; if(debug) fprintf(stderr,"abac_id_new: made a new id, %ld\n",(long) id); _add_into_id_credentials(id); return id; } 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 = _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 = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, filename, BUILD_X509_FLAG, X509_AA, BUILD_END ); 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 = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_BLOB_ASN1_DER, chunk, BUILD_X509_FLAG, X509_AA, BUILD_END ); 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; if(debug) fprintf(stderr,"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)); /* add p to cn here */ 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 = _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 = _get_keyid(id->cert); id->refcount = 1; _add_into_id_credentials(id); *ret = id; return ABAC_ID_SUCCESS; } int abac_id_load_privkey_file(abac_id_t *ptr, char *keyfile) { assert(ptr); ptr->key=abac_key_file_new(keyfile, chunk_empty); 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); 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); if(debug) print_abac_key("after abac_id_load_privkey_chunk", 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); return 1; }