db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose SSSD - certificate handling utils - NSS version
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose The calls defined here should be useable outside of SSSD as well, e.g. in
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose libsss_certmap.
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose Copyright (C) Sumit Bose <sbose@redhat.com> 2017
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose This program is free software; you can redistribute it and/or modify
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose it under the terms of the GNU General Public License as published by
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose the Free Software Foundation; either version 3 of the License, or
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose (at your option) any later version.
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose This program is distributed in the hope that it will be useful,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose but WITHOUT ANY WARRANTY; without even the implied warranty of
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose GNU General Public License for more details.
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose You should have received a copy of the GNU General Public License
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose along with this program. If not, see <http://www.gnu.org/licenses/>.
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose/* The following two functions are copied from NSS's lib/certdb/secname.c
346d6d8bf5fdb446921d754c07c8a7d913a048d5René Genz * because CERT_AddAVA is not exported. I just renamed it and made it static
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose * to avoid issues if the call gets exported some time in future. */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic void **
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit BoseAddToArray(PLArenaPool *arena, void **array, void *element)
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose /* Count up number of slots already in use in the array */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose while (*ap++) {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose array = (void**) PORT_ArenaAlloc(arena, (count + 2) * sizeof(void *));
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosesss_CERT_AddAVA(PLArenaPool *arena, CERTRDN *rdn, CERTAVA *ava)
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose rdn->avas = (CERTAVA**) AddToArray(arena, (void**) rdn->avas, ava);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosecert_get_ext_by_tag(CERTCertificate *cert, SECOidTag tag)
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose (cert->extensions != NULL) && (cert->extensions[i] != NULL);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose if (SECITEM_ItemsAreEqual(&cert->extensions[i]->id, &oid->oid))
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic int get_extended_key_usage_oids(TALLOC_CTX *mem_ctx,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose const char ***_oids)
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose ext = cert_get_ext_by_tag(cert, SEC_OID_X509_EXT_KEY_USAGE);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose for (c = 0; (oids != NULL && oids[c] != NULL); c++);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose oids_list = talloc_zero_array(mem_ctx, const char *, c + 1);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose for (c = 0; (oids != NULL && oids[c] != NULL); c++) {
346d6d8bf5fdb446921d754c07c8a7d913a048d5René Genz /* it is expected that NSS OID strings start with "OID." but we
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose * prefer the plain dotted-decimal version so the prefix is skipped */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose oids_list[c] = talloc_strdup(oids_list, tmp_str + 4);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic int get_rdn_str(TALLOC_CTX *mem_ctx, CERTAVA **avas,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose const char **rdn_str)
346d6d8bf5fdb446921d754c07c8a7d913a048d5René Genz /* Multiple AVAs should be avoided because there is no general ordering
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose * rule and the RDN strings are not reproducible */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic int get_rdn_list(TALLOC_CTX *mem_ctx, CERTRDN **rdns,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose const char ***rdn_list)
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose list = talloc_zero_array(mem_ctx, const char *, c + 1);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Boseenum san_opt nss_name_type_to_san_opt(CERTGeneralNameType type)
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose/* taken from pkinit_crypto_nss.c of MIT Kerberos */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose/* KerberosString: RFC 4120, 5.2.1. */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic const SEC_ASN1Template kerberos_string_template[] = {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose/* Realm: RFC 4120, 5.2.2. */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic const SEC_ASN1Template realm_template[] = {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose/* PrincipalName: RFC 4120, 5.2.2. */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic const SEC_ASN1Template sequence_of_kerberos_string_template[] = {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic const SEC_ASN1Template principal_name_template[] = {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose sizeof(struct principal_name),
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose SEC_ASN1_CONTEXT_SPECIFIC | 0 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose sizeof(struct SECItem **),
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose/* KRB5PrincipalName: RFC 4556, 3.2.2. */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic const SEC_ASN1Template kerberos_principal_name_template[] = {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose SEC_ASN1_CONTEXT_SPECIFIC | 0 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose sizeof(struct realm),
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose offsetof(struct kerberos_principal_name, principal_name),
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose sizeof(struct principal_name),
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic int add_string_other_name_to_san_list(TALLOC_CTX *mem_ctx,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose tmp_str = CERT_GetOidString(&(current->name.OthName.oid));
346d6d8bf5fdb446921d754c07c8a7d913a048d5René Genz /* it is expected that NSS OID strings start with "OID." but we
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose * prefer the plain dotted-decimal version so the prefix is skipped */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose i->other_name_oid = talloc_strdup(i, tmp_str + 4);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose i->bin_val = talloc_memdup(i, current->name.OthName.name.data,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic int add_nt_princ_to_san_list(TALLOC_CTX *mem_ctx,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose i->val = talloc_strndup(i, (char *) tmp_secitem.data,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose ret = get_short_name(i, i->val, '@', &(i->short_name));
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic int add_pkinit_princ_to_san_list(TALLOC_CTX *mem_ctx,
22abbb479e00438ec4ab19735824cc6e79dd9aafLukas Slebodnik /* To avoid 'Wmissing-braces' warnings with older versions of
22abbb479e00438ec4ab19735824cc6e79dd9aafLukas Slebodnik * gcc kerberos_principal_name cannot be initialized with { 0 }
22abbb479e00438ec4ab19735824cc6e79dd9aafLukas Slebodnik * but must be initialized with memset().
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose for (c = 0; kname.principal_name.name_string[c] != NULL; c++) {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose if (c > 0) {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose (char *) kname.principal_name.name_string[c]->data,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose ret = get_short_name(i, i->val, '@', &(i->short_name));
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic int add_oid_to_san_list(TALLOC_CTX *mem_ctx,
346d6d8bf5fdb446921d754c07c8a7d913a048d5René Genz /* it is expected that NSS OID strings start with "OID." but we
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose * prefer the plain dotted-decimal version so the prefix is skipped */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic int add_rdn_list_to_san_list(TALLOC_CTX *mem_ctx,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose ret = get_rdn_list(i, name.rdns, &(i->rdn_list));
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic int add_ip_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose if (data == NULL || len == 0 || san_opt == SAN_INVALID) {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose /* taken from secu_PrintIPAddress() */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose if (PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped)) {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose /* convert to IPv4. */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose memcpy(&addr.inet.ip, &addr.ipv6.ip.pr_s6_addr[12], 4);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose memset(&addr.inet.pad[0], 0, sizeof addr.inet.pad);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose st = PR_NetAddrToString(&addr, addrBuf, sizeof addrBuf);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bosestatic int get_san(TALLOC_CTX *mem_ctx, CERTCertificate *cert,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose && PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose name_list = CERT_DecodeAltNameExtension(pool, &subAltName);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose if (strcmp(item_s->other_name_oid, NT_PRINCIPAL_OID) == 0) {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose ret = add_nt_princ_to_san_list(mem_ctx, pool, SAN_NT, current,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose } else if (strcmp(item_s->other_name_oid, PKINIT_OID) == 0) {
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose ret = add_pkinit_princ_to_san_list(mem_ctx, pool, SAN_PKINIT,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose ret = add_principal_to_san_list(mem_ctx, SAN_PRINCIPAL,
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose /* Don't free nameList, it's part of the arena. */
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose nss_ctx = NSS_InitContext("", "", "", "", NULL, NSS_INIT_READONLY
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose cont = talloc_zero(mem_ctx, struct sss_cert_content);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose cert = CERT_NewTempCertificate(handle, &der_item, NULL, PR_FALSE, PR_TRUE);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose cont->issuer_str = talloc_strdup(cont, cert->issuerName);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose ret = get_rdn_list(cont, cert->issuer.rdns, &cont->issuer_rdn_list);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose cont->subject_str = talloc_strdup(cont, cert->subjectName);
db36dca3d45e6eefbb30042ee65876566f1a6014Sumit Bose ret = get_rdn_list(cont, cert->subject.rdns, &cont->subject_rdn_list);