char * hx509_get_error_string(hx509_context context, int error_code) { heim_error_t msg = context->error; heim_string_t s; char *str = NULL; if (msg == NULL || heim_error_get_code(msg) != error_code) { const char *cstr; cstr = com_right(context->et_list, error_code); if (cstr) return strdup(cstr); cstr = strerror(error_code); if (cstr) return strdup(cstr); if (asprintf(&str, "<unknown error: %d>", error_code) == -1) return NULL; return str; } s = heim_error_copy_string(msg); if (s) { const char *cstr = heim_string_get_utf8(s); if (cstr) str = strdup(cstr); heim_release(s); } return str; }
static int any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs) { int ret; size_t i; if (sd->certificates == NULL) return 0; for (i = 0; i < sd->certificates->len; i++) { heim_error_t error; hx509_cert c; c = hx509_cert_init_data(context, sd->certificates->val[i].data, sd->certificates->val[i].length, &error); if (c == NULL) { ret = heim_error_get_code(error); heim_release(error); return ret; } ret = hx509_certs_add(context, certs, c); hx509_cert_free(c); if (ret) return ret; } return 0; }
static int read_json(const char *dbname, heim_object_t *out, heim_error_t *error) { struct stat st; char *str = NULL; int ret; int fd = -1; ssize_t bytes; *out = NULL; ret = open_file(dbname, 0, 0, &fd, error); if (ret) return ret; ret = fstat(fd, &st); if (ret == -1) { (void) close(fd); return HEIM_ERROR(error, errno, (ret, N_("Could not stat JSON DB %s: %s", ""), dbname, strerror(errno))); } if (st.st_size == 0) { (void) close(fd); return 0; } str = malloc(st.st_size + 1); if (str == NULL) { (void) close(fd); return HEIM_ENOMEM(error); } bytes = read(fd, str, st.st_size); (void) close(fd); if (bytes != st.st_size) { free(str); if (bytes >= 0) errno = EINVAL; /* ?? */ return HEIM_ERROR(error, errno, (ret, N_("Could not read JSON DB %s: %s", ""), dbname, strerror(errno))); } str[st.st_size] = '\0'; *out = heim_json_create(str, 10, 0, error); free(str); if (*out == NULL) return (error && *error) ? heim_error_get_code(*error) : EINVAL; return 0; }
static int parse_certificate(hx509_context context, const char *fn, struct hx509_collector *c, const hx509_pem_header *headers, const void *data, size_t len, const AlgorithmIdentifier *ai) { heim_error_t error = NULL; hx509_cert cert; int ret; cert = hx509_cert_init_data(context, data, len, &error); if (cert == NULL) { ret = heim_error_get_code(error); heim_release(error); return ret; } ret = _hx509_collector_certs_add(context, c, cert); hx509_cert_free(cert); return ret; }
static int json_db_sync(void *db, heim_error_t *error) { json_db_t jsondb = db; size_t len, bytes; heim_error_t e; heim_string_t json; const char *json_text = NULL; int ret = 0; int fd = -1; #ifdef WIN32 int tries = 3; #endif heim_assert(jsondb->fd > -1, "DB not locked when sync attempted"); json = heim_json_copy_serialize(jsondb->dict, 0, &e); if (json == NULL) { if (error) *error = e; else heim_release(e); return heim_error_get_code(e); } json_text = heim_string_get_utf8(json); len = strlen(json_text); errno = 0; #ifdef WIN32 while (tries--) { ret = open_file(heim_string_get_utf8(jsondb->dbname), 1, 0, &fd, error); if (ret == 0) break; sleep(1); } if (ret) { heim_release(json); return ret; } #else fd = jsondb->fd; #endif /* WIN32 */ bytes = write(fd, json_text, len); heim_release(json); if (bytes != len) return errno ? errno : EIO; ret = fsync(fd); if (ret) return ret; #ifdef WIN32 ret = close(fd); if (ret) return GetLastError(); #else ret = rename(heim_string_get_utf8(jsondb->bkpname), heim_string_get_utf8(jsondb->dbname)); if (ret == 0) { jsondb->locked_needs_unlink = 0; return 0; } #endif /* WIN32 */ return errno; }
static krb5_error_code an2ln_def_plug_an2ln(void *plug_ctx, krb5_context context, const char *rule, krb5_const_principal aname, set_result_f set_res_f, void *set_res_ctx) { krb5_error_code ret; const char *an2ln_db_fname; heim_db_t dbh = NULL; heim_dict_t db_options; heim_data_t k, v; heim_error_t error; char *unparsed = NULL; char *value = NULL; _krb5_load_db_plugins(context); heim_base_once_f(&sorted_text_db_init_once, NULL, sorted_text_db_init_f); if (strncmp(rule, "DB:", strlen("DB:") != 0)) return KRB5_PLUGIN_NO_HANDLE; an2ln_db_fname = &rule[strlen("DB:")]; if (!*an2ln_db_fname) return KRB5_PLUGIN_NO_HANDLE; ret = krb5_unparse_name(context, aname, &unparsed); if (ret) return ret; db_options = heim_dict_create(11); if (db_options != NULL) heim_dict_set_value(db_options, HSTR("read-only"), heim_number_create(1)); dbh = heim_db_create(NULL, an2ln_db_fname, db_options, &error); if (dbh == NULL) { krb5_set_error_message(context, heim_error_get_code(error), N_("Couldn't open aname2lname-text-db", "")); ret = KRB5_PLUGIN_NO_HANDLE; goto cleanup; } /* Binary search; file should be sorted (in C locale) */ k = heim_data_ref_create(unparsed, strlen(unparsed), NULL); if (k == NULL) return krb5_enomem(context); v = heim_db_copy_value(dbh, NULL, k, &error); heim_release(k); if (v == NULL && error != NULL) { krb5_set_error_message(context, heim_error_get_code(error), N_("Lookup in aname2lname-text-db failed", "")); ret = heim_error_get_code(error); goto cleanup; } else if (v == NULL) { ret = KRB5_PLUGIN_NO_HANDLE; goto cleanup; } else { /* found */ if (heim_data_get_length(v) == 0) { krb5_set_error_message(context, ret, N_("Principal mapped to empty username", "")); ret = KRB5_NO_LOCALNAME; goto cleanup; } ret = set_res_f(set_res_ctx, heim_data_get_ptr(v)); heim_release(v); } cleanup: heim_release(dbh); free(unparsed); free(value); return ret; }
static int collect_cert(hx509_context context, struct p11_module *p, struct p11_slot *slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, void *ptr, CK_ATTRIBUTE *query, int num_query) { struct hx509_collector *collector = ptr; heim_error_t error = NULL; hx509_cert cert; int ret; if ((CK_LONG)query[0].ulValueLen == -1 || (CK_LONG)query[1].ulValueLen == -1) { return 0; } cert = hx509_cert_init_data(context, query[1].pValue, query[1].ulValueLen, &error); if (cert == NULL) { ret = heim_error_get_code(error); heim_release(error); return ret; } if (p->ref == 0) _hx509_abort("pkcs11 ref == 0 on alloc"); p->ref++; if (p->ref == UINT_MAX) _hx509_abort("pkcs11 ref to high"); _hx509_cert_set_release(cert, p11_cert_release, p); { heim_octet_string data; data.data = query[0].pValue; data.length = query[0].ulValueLen; _hx509_set_cert_attribute(context, cert, &asn1_oid_id_pkcs_9_at_localKeyId, &data); } if ((CK_LONG)query[2].ulValueLen != -1) { char *str; ret = asprintf(&str, "%.*s", (int)query[2].ulValueLen, (char *)query[2].pValue); if (ret != -1 && str) { hx509_cert_set_friendly_name(cert, str); free(str); } } ret = _hx509_collector_certs_add(context, collector, cert); hx509_cert_free(cert); return ret; }
static int keychain_iter(hx509_context context, hx509_certs certs, void *data, void *cursor, hx509_cert *cert) { SecKeychainAttributeList *attrs = NULL; SecKeychainAttributeInfo attrInfo; UInt32 attrFormat[1] = { 0 }; SecKeychainItemRef itemRef; SecItemAttr item[1]; heim_error_t error = NULL; struct iter *iter = cursor; OSStatus ret; UInt32 len; void *ptr = NULL; if (iter->certs) return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert); *cert = NULL; ret = SecKeychainSearchCopyNext(iter->searchRef, &itemRef); if (ret == errSecItemNotFound) return 0; else if (ret != 0) return EINVAL; /* * Pick out certificate and matching "keyid" */ item[0] = kSecPublicKeyHashItemAttr; attrInfo.count = 1; attrInfo.tag = item; attrInfo.format = attrFormat; ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrs, &len, &ptr); if (ret) return EINVAL; *cert = hx509_cert_init_data(context, ptr, len, &error); if (*cert == NULL) { ret = heim_error_get_code(error); heim_release(error); goto out; } /* * Find related private key if there is one by looking at * kSecPublicKeyHashItemAttr == kSecKeyLabel */ { SecKeychainSearchRef search; SecKeychainAttribute attrKeyid; SecKeychainAttributeList attrList; attrKeyid.tag = kSecKeyLabel; attrKeyid.length = attrs->attr[0].length; attrKeyid.data = attrs->attr[0].data; attrList.count = 1; attrList.attr = &attrKeyid; ret = SecKeychainSearchCreateFromAttributes(NULL, CSSM_DL_DB_RECORD_PRIVATE_KEY, &attrList, &search); if (ret) { ret = 0; goto out; } ret = SecKeychainSearchCopyNext(search, &itemRef); CFRelease(search); if (ret == errSecItemNotFound) { ret = 0; goto out; } else if (ret) { ret = EINVAL; goto out; } set_private_key(context, itemRef, *cert); } out: SecKeychainItemFreeAttributesAndData(attrs, ptr); return ret; }
static int ca_sign(hx509_context context, hx509_ca_tbs tbs, hx509_private_key signer, const AuthorityKeyIdentifier *ai, const Name *issuername, hx509_cert *certificate) { heim_error_t error = NULL; heim_octet_string data; Certificate c; TBSCertificate *tbsc; size_t size; int ret; const AlgorithmIdentifier *sigalg; time_t notBefore; time_t notAfter; unsigned key_usage; sigalg = tbs->sigalg; if (sigalg == NULL) sigalg = _hx509_crypto_default_sig_alg; memset(&c, 0, sizeof(c)); /* * Default values are: Valid since 24h ago, valid one year into * the future, KeyUsage digitalSignature and keyEncipherment set, * and keyCertSign for CA certificates. */ notBefore = tbs->notBefore; if (notBefore == 0) notBefore = time(NULL) - 3600 * 24; notAfter = tbs->notAfter; if (notAfter == 0) notAfter = time(NULL) + 3600 * 24 * 365; key_usage = tbs->key_usage; if (key_usage == 0) { KeyUsage ku; memset(&ku, 0, sizeof(ku)); ku.digitalSignature = 1; ku.keyEncipherment = 1; key_usage = KeyUsage2int(ku); } if (tbs->flags.ca) { KeyUsage ku; memset(&ku, 0, sizeof(ku)); ku.keyCertSign = 1; ku.cRLSign = 1; key_usage |= KeyUsage2int(ku); } /* * */ tbsc = &c.tbsCertificate; if (tbs->flags.key == 0) { ret = EINVAL; hx509_set_error_string(context, 0, ret, "No public key set"); return ret; } /* * Don't put restrictions on proxy certificate's subject name, it * will be generated below. */ if (!tbs->flags.proxy) { if (tbs->subject == NULL) { hx509_set_error_string(context, 0, EINVAL, "No subject name set"); return EINVAL; } if (hx509_name_is_null_p(tbs->subject) && tbs->san.len == 0) { hx509_set_error_string(context, 0, EINVAL, "NULL subject and no SubjectAltNames"); return EINVAL; } } if (tbs->flags.ca && tbs->flags.proxy) { hx509_set_error_string(context, 0, EINVAL, "Can't be proxy and CA " "at the same time"); return EINVAL; } if (tbs->flags.proxy) { if (tbs->san.len > 0) { hx509_set_error_string(context, 0, EINVAL, "Proxy certificate is not allowed " "to have SubjectAltNames"); return EINVAL; } } /* version [0] Version OPTIONAL, -- EXPLICIT nnn DEFAULT 1, */ tbsc->version = calloc(1, sizeof(*tbsc->version)); if (tbsc->version == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } *tbsc->version = rfc3280_version_3; /* serialNumber CertificateSerialNumber, */ if (tbs->flags.serial) { ret = der_copy_heim_integer(&tbs->serial, &tbsc->serialNumber); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } } else { tbsc->serialNumber.length = 20; tbsc->serialNumber.data = malloc(tbsc->serialNumber.length); if (tbsc->serialNumber.data == NULL){ ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } /* XXX diffrent */ RAND_bytes(tbsc->serialNumber.data, tbsc->serialNumber.length); ((unsigned char *)tbsc->serialNumber.data)[0] &= 0x7f; } /* signature AlgorithmIdentifier, */ ret = copy_AlgorithmIdentifier(sigalg, &tbsc->signature); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy sigature alg"); goto out; } /* issuer Name, */ if (issuername) ret = copy_Name(issuername, &tbsc->issuer); else ret = hx509_name_to_Name(tbs->subject, &tbsc->issuer); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy issuer name"); goto out; } /* validity Validity, */ tbsc->validity.notBefore.element = choice_Time_generalTime; tbsc->validity.notBefore.u.generalTime = notBefore; tbsc->validity.notAfter.element = choice_Time_generalTime; tbsc->validity.notAfter.u.generalTime = notAfter; /* subject Name, */ if (tbs->flags.proxy) { ret = build_proxy_prefix(context, &tbsc->issuer, &tbsc->subject); if (ret) goto out; } else { ret = hx509_name_to_Name(tbs->subject, &tbsc->subject); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy subject name"); goto out; } } /* subjectPublicKeyInfo SubjectPublicKeyInfo, */ ret = copy_SubjectPublicKeyInfo(&tbs->spki, &tbsc->subjectPublicKeyInfo); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy spki"); goto out; } /* issuerUniqueID [1] IMPLICIT BIT STRING OPTIONAL */ if (tbs->issuerUniqueID.length) { tbsc->issuerUniqueID = calloc(1, sizeof(*tbsc->issuerUniqueID)); if (tbsc->issuerUniqueID == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ret = der_copy_bit_string(&tbs->issuerUniqueID, tbsc->issuerUniqueID); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } } /* subjectUniqueID [2] IMPLICIT BIT STRING OPTIONAL */ if (tbs->subjectUniqueID.length) { tbsc->subjectUniqueID = calloc(1, sizeof(*tbsc->subjectUniqueID)); if (tbsc->subjectUniqueID == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ret = der_copy_bit_string(&tbs->subjectUniqueID, tbsc->subjectUniqueID); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } } /* extensions [3] EXPLICIT Extensions OPTIONAL */ tbsc->extensions = calloc(1, sizeof(*tbsc->extensions)); if (tbsc->extensions == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } /* Add the text BMP string Domaincontroller to the cert */ if (tbs->flags.domaincontroller) { data.data = rk_UNCONST("\x1e\x20\x00\x44\x00\x6f\x00\x6d" "\x00\x61\x00\x69\x00\x6e\x00\x43" "\x00\x6f\x00\x6e\x00\x74\x00\x72" "\x00\x6f\x00\x6c\x00\x6c\x00\x65" "\x00\x72"); data.length = 34; ret = add_extension(context, tbsc, 0, &asn1_oid_id_ms_cert_enroll_domaincontroller, &data); if (ret) goto out; } /* add KeyUsage */ { KeyUsage ku; ku = int2KeyUsage(key_usage); ASN1_MALLOC_ENCODE(KeyUsage, data.data, data.length, &ku, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 1, &asn1_oid_id_x509_ce_keyUsage, &data); free(data.data); if (ret) goto out; } /* add ExtendedKeyUsage */ if (tbs->eku.len > 0) { ASN1_MALLOC_ENCODE(ExtKeyUsage, data.data, data.length, &tbs->eku, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_extKeyUsage, &data); free(data.data); if (ret) goto out; } /* add Subject Alternative Name */ if (tbs->san.len > 0) { ASN1_MALLOC_ENCODE(GeneralNames, data.data, data.length, &tbs->san, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_subjectAltName, &data); free(data.data); if (ret) goto out; } /* Add Authority Key Identifier */ if (ai) { ASN1_MALLOC_ENCODE(AuthorityKeyIdentifier, data.data, data.length, ai, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_authorityKeyIdentifier, &data); free(data.data); if (ret) goto out; } /* Add Subject Key Identifier */ { SubjectKeyIdentifier si; unsigned char hash[SHA_DIGEST_LENGTH]; { EVP_MD_CTX *ctx; ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); EVP_DigestUpdate(ctx, tbs->spki.subjectPublicKey.data, tbs->spki.subjectPublicKey.length / 8); EVP_DigestFinal_ex(ctx, hash, NULL); EVP_MD_CTX_destroy(ctx); } si.data = hash; si.length = sizeof(hash); ASN1_MALLOC_ENCODE(SubjectKeyIdentifier, data.data, data.length, &si, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_subjectKeyIdentifier, &data); free(data.data); if (ret) goto out; } /* Add BasicConstraints */ { BasicConstraints bc; int aCA = 1; unsigned int path; memset(&bc, 0, sizeof(bc)); if (tbs->flags.ca) { bc.cA = &aCA; if (tbs->pathLenConstraint >= 0) { path = tbs->pathLenConstraint; bc.pathLenConstraint = &path; } } ASN1_MALLOC_ENCODE(BasicConstraints, data.data, data.length, &bc, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); /* Critical if this is a CA */ ret = add_extension(context, tbsc, tbs->flags.ca, &asn1_oid_id_x509_ce_basicConstraints, &data); free(data.data); if (ret) goto out; } /* add Proxy */ if (tbs->flags.proxy) { ProxyCertInfo info; memset(&info, 0, sizeof(info)); if (tbs->pathLenConstraint >= 0) { info.pCPathLenConstraint = malloc(sizeof(*info.pCPathLenConstraint)); if (info.pCPathLenConstraint == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } *info.pCPathLenConstraint = tbs->pathLenConstraint; } ret = der_copy_oid(&asn1_oid_id_pkix_ppl_inheritAll, &info.proxyPolicy.policyLanguage); if (ret) { free_ProxyCertInfo(&info); hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ASN1_MALLOC_ENCODE(ProxyCertInfo, data.data, data.length, &info, &size, ret); free_ProxyCertInfo(&info); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_pkix_pe_proxyCertInfo, &data); free(data.data); if (ret) goto out; } if (tbs->crldp.len) { ASN1_MALLOC_ENCODE(CRLDistributionPoints, data.data, data.length, &tbs->crldp, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, FALSE, &asn1_oid_id_x509_ce_cRLDistributionPoints, &data); free(data.data); if (ret) goto out; } ASN1_MALLOC_ENCODE(TBSCertificate, data.data, data.length,tbsc, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "malloc out of memory"); goto out; } if (data.length != size) _hx509_abort("internal ASN.1 encoder error"); ret = _hx509_create_signature_bitstring(context, signer, sigalg, &data, &c.signatureAlgorithm, &c.signatureValue); free(data.data); if (ret) goto out; *certificate = hx509_cert_init(context, &c, &error); if (*certificate == NULL) { ret = heim_error_get_code(error); heim_release(error); goto out; } free_Certificate(&c); return 0; out: free_Certificate(&c); return ret; }