diff options
author | Sumit Bose <sbose@redhat.com> | 2017-02-14 22:47:08 +0100 |
---|---|---|
committer | Lukas Slebodnik <lslebodn@redhat.com> | 2017-09-05 15:04:00 +0200 |
commit | a20fb9cbd5f42a6ca895aea1b84347fdfea34b89 (patch) | |
tree | a06ea90fba92b7d19293028dbd6af2a53bfac4f6 /src/lib | |
parent | 781d231a12eb0c772353cc15e2c3ae1e7545e663 (diff) | |
download | sssd-a20fb9cbd5f42a6ca895aea1b84347fdfea34b89.tar.gz sssd-a20fb9cbd5f42a6ca895aea1b84347fdfea34b89.tar.xz sssd-a20fb9cbd5f42a6ca895aea1b84347fdfea34b89.zip |
certmap: add OpenSSL implementation
The OpenSSL 1.1 API is used but there is a short macro block which
should added the needed compatibility if and older OpenSSL version is
used.
Related to https://pagure.io/SSSD/sssd/issue/3050
Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/certmap/sss_cert_content_common.c | 199 | ||||
-rw-r--r-- | src/lib/certmap/sss_cert_content_crypto.c | 778 | ||||
-rw-r--r-- | src/lib/certmap/sss_cert_content_nss.c | 105 | ||||
-rw-r--r-- | src/lib/certmap/sss_certmap.c | 93 | ||||
-rw-r--r-- | src/lib/certmap/sss_certmap_attr_names.c | 83 | ||||
-rw-r--r-- | src/lib/certmap/sss_certmap_int.h | 25 |
6 files changed, 1064 insertions, 219 deletions
diff --git a/src/lib/certmap/sss_cert_content_common.c b/src/lib/certmap/sss_cert_content_common.c new file mode 100644 index 000000000..429193352 --- /dev/null +++ b/src/lib/certmap/sss_cert_content_common.c @@ -0,0 +1,199 @@ +/* + SSSD - certificate handling utils + The calls defined here should be useable outside of SSSD as well, e.g. in + libsss_certmap. + + Copyright (C) Sumit Bose <sbose@redhat.com> 2017 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdbool.h> +#include <errno.h> +#include <string.h> + +#include "lib/certmap/sss_certmap_int.h" + +int get_short_name(TALLOC_CTX *mem_ctx, const char *full_name, + char delim, char **short_name) +{ + char *at; + char *s; + + if (full_name == NULL || delim == '\0' || short_name == NULL) { + return EINVAL; + } + + at = strchr(full_name, delim); + if (at != NULL) { + s = talloc_strndup(mem_ctx, full_name, (at - full_name)); + } else { + s = talloc_strdup(mem_ctx, full_name); + } + if (s == NULL) { + return ENOMEM; + } + + *short_name = s; + + return 0; +} + +int add_to_san_list(TALLOC_CTX *mem_ctx, bool is_bin, + enum san_opt san_opt, const uint8_t *data, size_t len, + struct san_list **item) +{ + struct san_list *i; + + if (data == NULL || len == 0 || san_opt == SAN_INVALID) { + return EINVAL; + } + + i = talloc_zero(mem_ctx, struct san_list); + if (i == NULL) { + return ENOMEM; + } + + i->san_opt = san_opt; + if (is_bin) { + i->bin_val = talloc_memdup(i, data, len); + i->bin_val_len = len; + } else { + i->val = talloc_strndup(i, (const char *) data, len); + } + if (i->val == NULL) { + talloc_free(i); + return ENOMEM; + } + + *item = i; + + return 0; +} + +int add_principal_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt, + const char *princ, struct san_list **item) +{ + struct san_list *i = NULL; + int ret; + + i = talloc_zero(mem_ctx, struct san_list); + if (i == NULL) { + return ENOMEM; + } + i->san_opt = san_opt; + + i->val = talloc_strdup(i, princ); + if (i->val == NULL) { + ret = ENOMEM; + goto done; + } + + ret = get_short_name(i, i->val, '@', &(i->short_name)); + if (ret != 0) { + goto done; + } + + ret = 0; + +done: + if (ret == 0) { + *item = i; + } else { + talloc_free(i); + } + + return ret; +} + +int rdn_list_2_dn_str(TALLOC_CTX *mem_ctx, const char *conversion, + const char **rdn_list, char **result) +{ + char *str = NULL; + size_t c; + int ret; + char *conv = NULL; + + str = talloc_strdup(mem_ctx, ""); + if (str == NULL) { + ret = ENOMEM; + goto done; + } + if (conversion == NULL || strcmp(conversion, "nss_ldap") == 0 + || strcmp(conversion, "nss") == 0) { + for (c = 0; rdn_list[c] != NULL; c++); + while (c != 0) { + c--; + str = talloc_asprintf_append(str, "%s%s", + (rdn_list[c + 1] == NULL) ? "" : ",", + rdn_list[c]); + if (str == NULL) { + ret = ENOMEM; + goto done; + } + }; + } else if (strcmp(conversion, "ad_ldap") == 0) { + for (c = 0; rdn_list[c] != NULL; c++); + while (c != 0) { + c--; + conv = check_ad_attr_name(str, rdn_list[c]); + str = talloc_asprintf_append(str, "%s%s", + (rdn_list[c + 1] == NULL) ? "" : ",", + conv == NULL ? rdn_list[c] : conv); + talloc_free(conv); + conv = NULL; + if (str == NULL) { + ret = ENOMEM; + goto done; + } + }; + } else if (strcmp(conversion, "nss_x500") == 0) { + for (c = 0; rdn_list[c] != NULL; c++) { + str = talloc_asprintf_append(str, "%s%s", (c == 0) ? "" : ",", + rdn_list[c]); + if (str == NULL) { + ret = ENOMEM; + goto done; + } + } + } else if (strcmp(conversion, "ad_x500") == 0 + || strcmp(conversion, "ad") == 0) { + for (c = 0; rdn_list[c] != NULL; c++) { + conv = check_ad_attr_name(str, rdn_list[c]); + str = talloc_asprintf_append(str, "%s%s", + (c == 0) ? "" : ",", + conv == NULL ? rdn_list[c] : conv); + talloc_free(conv); + conv = NULL; + if (str == NULL) { + ret = ENOMEM; + goto done; + } + } + } else { + ret = EINVAL; + goto done; + } + + ret = 0; + +done: + if (ret == 0) { + *result = str; + } else { + talloc_free(str); + } + + return ret; +} diff --git a/src/lib/certmap/sss_cert_content_crypto.c b/src/lib/certmap/sss_cert_content_crypto.c index bddcf9bce..23e065a3c 100644 --- a/src/lib/certmap/sss_cert_content_crypto.c +++ b/src/lib/certmap/sss_cert_content_crypto.c @@ -19,14 +19,788 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <errno.h> +#include "config.h" +#include <talloc.h> +#include <openssl/x509v3.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/err.h> +#include <openssl/stack.h> +#include <openssl/safestack.h> + +#include "util/crypto/sss_crypto.h" +#include "util/cert.h" #include "lib/certmap/sss_certmap.h" #include "lib/certmap/sss_certmap_int.h" +/* backward compatible macros for OpenSSL < 1.1 */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#define ASN1_STRING_get0_data(o) ASN1_STRING_data(o) +#define X509_get_extension_flags(o) ((o)->ex_flags) +#define X509_get_key_usage(o) ((o)->ex_kusage) +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + +typedef struct PrincipalName_st { + ASN1_INTEGER *name_type; + STACK_OF(ASN1_GENERALSTRING) *name_string; +} PrincipalName; + +ASN1_SEQUENCE(PrincipalName) = { + ASN1_EXP(PrincipalName, name_type, ASN1_INTEGER, 0), + ASN1_EXP_SEQUENCE_OF(PrincipalName, name_string, ASN1_GENERALSTRING, 1) +} ASN1_SEQUENCE_END(PrincipalName) + +IMPLEMENT_ASN1_FUNCTIONS(PrincipalName) + +typedef struct KRB5PrincipalName_st { + ASN1_STRING *realm; + PrincipalName *principal_name; +} KRB5PrincipalName; + +ASN1_SEQUENCE(KRB5PrincipalName) = { + ASN1_EXP(KRB5PrincipalName, realm, ASN1_GENERALSTRING, 0), + ASN1_EXP(KRB5PrincipalName, principal_name, PrincipalName, 1) +} ASN1_SEQUENCE_END(KRB5PrincipalName) + +IMPLEMENT_ASN1_FUNCTIONS(KRB5PrincipalName) + +enum san_opt openssl_name_type_to_san_opt(int type) +{ + switch (type) { + case GEN_OTHERNAME: + return SAN_OTHER_NAME; + case GEN_EMAIL: + return SAN_RFC822_NAME; + case GEN_DNS: + return SAN_DNS_NAME; + case GEN_X400: + return SAN_X400_ADDRESS; + case GEN_DIRNAME: + return SAN_DIRECTORY_NAME; + case GEN_EDIPARTY: + return SAN_EDIPART_NAME; + case GEN_URI: + return SAN_URI; + case GEN_IPADD: + return SAN_IP_ADDRESS; + case GEN_RID: + return SAN_REGISTERED_ID; + default: + return SAN_INVALID; + } +} + +static int add_string_other_name_to_san_list(TALLOC_CTX *mem_ctx, + enum san_opt san_opt, + OTHERNAME *other_name, + struct san_list **item) +{ + struct san_list *i = NULL; + int ret; + char oid_buf[128]; /* FIXME: any other size ?? */ + int len; + unsigned char *p; + + len = OBJ_obj2txt(oid_buf, sizeof(oid_buf), other_name->type_id, 1); + if (len <= 0) { + return EINVAL; + } + + i = talloc_zero(mem_ctx, struct san_list); + if (i == NULL) { + return ENOMEM; + } + i->san_opt = san_opt; + + i->other_name_oid = talloc_strndup(i, oid_buf, len); + if (i->other_name_oid == NULL) { + ret = ENOMEM; + goto done; + } + + len = i2d_ASN1_TYPE(other_name->value, NULL); + if (len <= 0) { + ret = EINVAL; + goto done; + } + + i->bin_val = talloc_size(mem_ctx, len); + if (i->bin_val == NULL) { + ret = ENOMEM; + goto done; + } + + /* i2d_TYPE increment the second argument so that it points to the end of + * the written data hence we cannot use i->bin_val directly. */ + p = i->bin_val; + i->bin_val_len = i2d_ASN1_TYPE(other_name->value, &p); + + ret = 0; + +done: + if (ret == 0) { + *item = i; + } else { + talloc_free(i); + } + + return ret; +} + +static int add_nt_princ_to_san_list(TALLOC_CTX *mem_ctx, + enum san_opt san_opt, + GENERAL_NAME *current, + struct san_list **item) +{ + struct san_list *i = NULL; + int ret; + OTHERNAME *other_name = current->d.otherName; + + if (ASN1_TYPE_get(other_name->value) != V_ASN1_UTF8STRING) { + return EINVAL; + } + + i = talloc_zero(mem_ctx, struct san_list); + if (i == NULL) { + return ENOMEM; + } + i->san_opt = san_opt; + + i->val = talloc_strndup(i, + (const char *) ASN1_STRING_get0_data( + other_name->value->value.utf8string), + ASN1_STRING_length(other_name->value->value.utf8string)); + if (i->val == NULL) { + ret = ENOMEM; + goto done; + } + + ret = get_short_name(i, i->val, '@', &(i->short_name)); + if (ret != 0) { + goto done; + } + + ret = 0; + +done: + if (ret == 0) { + *item = i; + } else { + talloc_free(i); + } + + return ret; +} + +void *ASN1_TYPE_unpack_sequence(const ASN1_ITEM *it, const ASN1_TYPE *t) +{ + if (t == NULL || t->type != V_ASN1_SEQUENCE || t->value.sequence == NULL) + return NULL; + return ASN1_item_unpack(t->value.sequence, it); +} + +static int add_pkinit_princ_to_san_list(TALLOC_CTX *mem_ctx, + enum san_opt san_opt, + GENERAL_NAME *current, + struct san_list **item) +{ + struct san_list *i = NULL; + int ret; + KRB5PrincipalName *princ = NULL; + size_t c; + const unsigned char *p; + const ASN1_STRING *oct; + ASN1_GENERALSTRING *name_comp; + + oct = current->d.otherName->value->value.sequence; + p = oct->data; + princ = d2i_KRB5PrincipalName(NULL, &p, oct->length); + if (princ == NULL) { + return EINVAL; + } + + if (princ->realm == NULL + || princ->principal_name == NULL + || princ->principal_name->name_string == NULL + || sk_ASN1_GENERALSTRING_num(princ->principal_name->name_string) + == 0) { + ret = EINVAL; + goto done; + } + + i = talloc_zero(mem_ctx, struct san_list); + if (i == NULL) { + ret = ENOMEM; + goto done; + } + i->san_opt = san_opt; + + i->val = talloc_strdup(i, ""); + if (i->val == NULL) { + ret = ENOMEM; + goto done; + } + + for (c = 0; + c < sk_ASN1_GENERALSTRING_num(princ->principal_name->name_string); + c++) { + + if (c > 0) { + i->val = talloc_strdup_append(i->val, "/"); + if (i->val == NULL) { + ret = ENOMEM; + goto done; + } + } + + name_comp = sk_ASN1_GENERALSTRING_value( + princ->principal_name->name_string, c); + i->val = talloc_strndup_append(i->val, + (const char *) ASN1_STRING_get0_data(name_comp), + ASN1_STRING_length(name_comp)); + if (i->val == NULL) { + ret = ENOMEM; + goto done; + } + } + + i->val = talloc_asprintf_append(i->val, "@%.*s", + ASN1_STRING_length(princ->realm), + ASN1_STRING_get0_data(princ->realm)); + if (i->val == NULL) { + ret = ENOMEM; + goto done; + } + + ret = get_short_name(i, i->val, '@', &(i->short_name)); + if (ret != 0) { + goto done; + } + + ret = 0; + +done: + KRB5PrincipalName_free(princ); + if (ret == 0) { + *item = i; + } else { + talloc_free(i); + } + + return ret; +} + +static int add_ip_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt, + const uint8_t *data, size_t len, + struct san_list **item) +{ + struct san_list *i = NULL; + + i = talloc_zero(mem_ctx, struct san_list); + if (i == NULL) { + return ENOMEM; + } + i->san_opt = san_opt; + + i->val = talloc_strndup(i, (const char *) data, len); + if (i->val == NULL) { + talloc_free(i); + return ENOMEM; + } + + *item = i; + return 0; +} + +static int get_rdn_list(TALLOC_CTX *mem_ctx, X509_NAME *name, + const char ***rdn_list) +{ + int ret; + size_t c; + const char **list = NULL; + X509_NAME_ENTRY *e; + ASN1_STRING *rdn_str; + ASN1_OBJECT *rdn_name; + BIO *bio_mem = NULL; + char *tmp_str; + long tmp_str_size; + + int nid; + const char *sn; + + bio_mem = BIO_new(BIO_s_mem()); + if (bio_mem == NULL) { + ret = ENOMEM; + goto done; + } + + list = talloc_zero_array(mem_ctx, const char *, + X509_NAME_entry_count(name) + 1); + if (list == NULL) { + ret = ENOMEM; + goto done; + } + + for (c = 0; c < X509_NAME_entry_count(name); c++) { + e = X509_NAME_get_entry(name, c); + rdn_str = X509_NAME_ENTRY_get_data(e); + + ret = ASN1_STRING_print_ex(bio_mem, rdn_str, ASN1_STRFLGS_RFC2253); + if (ret < 0) { + ret = EIO; + goto done; + } + + tmp_str_size = BIO_get_mem_data(bio_mem, &tmp_str); + if (tmp_str_size == 0) { + ret = EINVAL; + goto done; + } + + rdn_name = X509_NAME_ENTRY_get_object(e); + nid = OBJ_obj2nid(rdn_name); + sn = OBJ_nid2sn(nid); + + list[c] = talloc_asprintf(list, "%s=%.*s", openssl_2_nss_attr_name(sn), + (int) tmp_str_size, tmp_str); + ret = BIO_reset(bio_mem); + if (ret != 1) { + /* BIO_reset() for BIO_s_mem returns 1 for sucess */ + ret = ENOMEM; + goto done; + } + if (list[c] == NULL) { + ret = ENOMEM; + goto done; + } + } + + ret = 0; + +done: + BIO_free_all(bio_mem); + if (ret == 0) { + *rdn_list = list; + } else { + talloc_free(list); + } + + return ret; +} + +static int add_rdn_list_to_san_list(TALLOC_CTX *mem_ctx, + enum san_opt san_opt, + X509_NAME *name, + struct san_list **item) +{ + struct san_list *i = NULL; + int ret; + + i = talloc_zero(mem_ctx, struct san_list); + if (i == NULL) { + return ENOMEM; + } + i->san_opt = san_opt; + + ret = get_rdn_list(i, name, &(i->rdn_list)); + if (ret != 0) { + talloc_free(i); + return ret; + } + + *item = i; + return 0; +} + +static int add_oid_to_san_list(TALLOC_CTX *mem_ctx, + enum san_opt san_opt, + ASN1_OBJECT *oid, + struct san_list **item) +{ + struct san_list *i = NULL; + char oid_buf[128]; /* FIXME: any other size ?? */ + int len; + + len = OBJ_obj2txt(oid_buf, sizeof(oid_buf), oid, 1); + if (len <= 0) { + return EINVAL; + } + + i = talloc_zero(mem_ctx, struct san_list); + if (i == NULL) { + return ENOMEM; + } + i->san_opt = san_opt; + + i->val = talloc_strndup(i, oid_buf, len); + if (i->val == NULL) { + talloc_free(i); + return ENOMEM; + } + + *item = i; + return 0; +} + +static int get_san(TALLOC_CTX *mem_ctx, X509 *cert, struct san_list **san_list) +{ + STACK_OF(GENERAL_NAME) *extsan = NULL; + GENERAL_NAME *current; + size_t c; + int ret; + int crit; + struct san_list *list = NULL; + struct san_list *item = NULL; + struct san_list *item_s = NULL; + struct san_list *item_p = NULL; + struct san_list *item_pb = NULL; + int len; + unsigned char *data; + unsigned char *p; + + extsan = X509_get_ext_d2i(cert, NID_subject_alt_name, &crit, NULL); + if (extsan == NULL) { + if (crit == -1) { /* extension could not be found */ + return EOK; + } else { + return EINVAL; + } + } + + for (c = 0; c < sk_GENERAL_NAME_num(extsan); c++) { + current = sk_GENERAL_NAME_value(extsan, c); + switch (current->type) { + case GEN_OTHERNAME: + ret = add_string_other_name_to_san_list(mem_ctx, + SAN_STRING_OTHER_NAME, + current->d.otherName, + &item_s); + if (ret != 0) { + goto done; + } + DLIST_ADD(list, item_s); + + item_p = NULL; + if (strcmp(item_s->other_name_oid, NT_PRINCIPAL_OID) == 0) { + ret = add_nt_princ_to_san_list(mem_ctx, SAN_NT, current, + &item_p); + if (ret != 0) { + goto done; + } + DLIST_ADD(list, item_p); + } else if (strcmp(item_s->other_name_oid, PKINIT_OID) == 0) { + ret = add_pkinit_princ_to_san_list(mem_ctx, SAN_PKINIT, + current, &item_p); + if (ret != 0) { + goto done; + } + DLIST_ADD(list, item_p); + } + + if (item_p != NULL) { + ret = add_principal_to_san_list(mem_ctx, SAN_PRINCIPAL, + item_p->val, &item_pb); + if (ret != 0) { + goto done; + } + DLIST_ADD(list, item_pb); + } + + break; + break; + case GEN_EMAIL: + ret = add_to_san_list(mem_ctx, false, + openssl_name_type_to_san_opt(current->type), + ASN1_STRING_get0_data(current->d.rfc822Name), + ASN1_STRING_length(current->d.rfc822Name), + &item); + if (ret != 0) { + goto done; + } + + ret = get_short_name(item, item->val, '@', &(item->short_name)); + if (ret != 0) { + goto done; + } + + DLIST_ADD(list, item); + break; + case GEN_DNS: + ret = add_to_san_list(mem_ctx, false, + openssl_name_type_to_san_opt(current->type), + ASN1_STRING_get0_data(current->d.dNSName), + ASN1_STRING_length(current->d.dNSName), + &item); + if (ret != 0) { + goto done; + } + + ret = get_short_name(item, item->val, '.', &(item->short_name)); + if (ret != 0) { + goto done; + } + + DLIST_ADD(list, item); + break; + case GEN_URI: + ret = add_to_san_list(mem_ctx, false, + openssl_name_type_to_san_opt(current->type), + ASN1_STRING_get0_data(current->d.uniformResourceIdentifier), + ASN1_STRING_length(current->d.uniformResourceIdentifier), + &item); + if (ret != 0) { + goto done; + } + break; + case GEN_IPADD: + ret = add_ip_to_san_list(mem_ctx, + openssl_name_type_to_san_opt(current->type), + ASN1_STRING_get0_data(current->d.iPAddress), + ASN1_STRING_length(current->d.iPAddress), + &item); + if (ret != 0) { + goto done; + } + DLIST_ADD(list, item); + break; + case GEN_DIRNAME: + ret = add_rdn_list_to_san_list(mem_ctx, + openssl_name_type_to_san_opt(current->type), + current->d.directoryName, &item); + if (ret != 0) { + goto done; + } + DLIST_ADD(list, item); + break; + case GEN_RID: + ret = add_oid_to_san_list(mem_ctx, + openssl_name_type_to_san_opt(current->type), + current->d.registeredID, &item); + if (ret != 0) { + goto done; + } + DLIST_ADD(list, item); + break; + case GEN_X400: + len = i2d_ASN1_TYPE(current->d.x400Address, NULL); + if (len <= 0) { + ret = EINVAL; + goto done; + } + + data = talloc_size(mem_ctx, len); + if (data == NULL) { + ret = ENOMEM; + goto done; + } + + /* i2d_TYPE increment the second argument so that it points to the end of + * the written data hence we cannot use i->bin_val directly. */ + p = data; + len = i2d_ASN1_TYPE(current->d.x400Address, &p); + + ret = add_to_san_list(mem_ctx, true, + openssl_name_type_to_san_opt(current->type), + data, len, &item); + if (ret != 0) { + goto done; + } + DLIST_ADD(list, item); + break; + case GEN_EDIPARTY: + len = i2d_EDIPARTYNAME(current->d.ediPartyName, NULL); + if (len <= 0) { + ret = EINVAL; + goto done; + } + + data = talloc_size(mem_ctx, len); + if (data == NULL) { + ret = ENOMEM; + goto done; + } + + /* i2d_TYPE increment the second argument so that it points to the end of + * the written data hence we cannot use i->bin_val directly. */ + p = data; + len = i2d_EDIPARTYNAME(current->d.ediPartyName, &data); + + ret = add_to_san_list(mem_ctx, true, + openssl_name_type_to_san_opt(current->type), + data, len, &item); + if (ret != 0) { + goto done; + } + DLIST_ADD(list, item); + break; + default: + ret = EINVAL; + goto done; + } + } + +done: + GENERAL_NAMES_free(extsan); + + if (ret == EOK) { + *san_list = list; + } + + return ret; +} + +static int get_extended_key_usage_oids(TALLOC_CTX *mem_ctx, + X509 *cert, + const char ***_oids) +{ + const char **oids_list = NULL; + size_t c; + int ret; + char oid_buf[128]; /* FIXME: any other size ?? */ + int len; + EXTENDED_KEY_USAGE *extusage = NULL; + + extusage = X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL); + if (extusage == NULL) { + return EIO; + } + + oids_list = talloc_zero_array(mem_ctx, const char *, + sk_ASN1_OBJECT_num(extusage) + 1); + if (oids_list == NULL) { + return ENOMEM; + } + + for (c = 0; c < sk_ASN1_OBJECT_num(extusage); c++) { + len = OBJ_obj2txt(oid_buf, sizeof(oid_buf), + sk_ASN1_OBJECT_value(extusage, c), 1); + if (len < 0) { + return EIO; + } + + oids_list[c] = talloc_strndup(oids_list, oid_buf, len); + if (oids_list[c] == NULL) { + ret = ENOMEM; + goto done; + } + } + + ret = 0; + +done: + sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free); + if (ret == 0) { + *_oids = oids_list; + } else { + talloc_free(oids_list); + } + + return ret; +} + int sss_cert_get_content(TALLOC_CTX *mem_ctx, const uint8_t *der_blob, size_t der_size, struct sss_cert_content **content) { - return EINVAL; + int ret; + struct sss_cert_content *cont = NULL; + X509 *cert = NULL; + const unsigned char *der; + BIO *bio_mem = NULL; + X509_NAME *tmp_name; + + if (der_blob == NULL || der_size == 0) { + return EINVAL; + } + + cont = talloc_zero(mem_ctx, struct sss_cert_content); + if (cont == NULL) { + return ENOMEM; + } + + bio_mem = BIO_new(BIO_s_mem()); + if (bio_mem == NULL) { + ret = ENOMEM; + goto done; + } + + der = (const unsigned char *) der_blob; + cert = d2i_X509(NULL, &der, (int) der_size); + if (cert == NULL) { + ret = EINVAL; + goto done; + } + + tmp_name = X509_get_issuer_name(cert); + + ret = get_rdn_list(cont, tmp_name, &cont->issuer_rdn_list); + if (ret != 0) { + goto done; + } + + ret = rdn_list_2_dn_str(cont, NULL, cont->issuer_rdn_list, + &cont->issuer_str); + if (ret != 0) { + goto done; + } + + tmp_name = X509_get_subject_name(cert); + + ret = get_rdn_list(cont, tmp_name, &cont->subject_rdn_list); + if (ret != 0) { + goto done; + } + + ret = rdn_list_2_dn_str(cont, NULL, cont->subject_rdn_list, + &cont->subject_str); + if (ret != 0) { + goto done; + } + + ret = X509_check_purpose(cert, -1, -1); + if (ret < 0) { + ret = EIO; + goto done; + } + if (!(X509_get_extension_flags(cert) & EXFLAG_KUSAGE)) { + ret = EINVAL; + goto done; + } + cont->key_usage = X509_get_key_usage(cert); + + ret = get_extended_key_usage_oids(cont, cert, + &(cont->extended_key_usage_oids)); + if (ret != 0) { + goto done; + } + + ret = get_san(cont, cert, &(cont->san_list)); + if (ret != 0) { + goto done; + } + + cont->cert_der = talloc_memdup(cont, der_blob, der_size); + if (cont->cert_der == NULL) { + ret = ENOMEM; + goto done; + } + + cont->cert_der_size = der_size; + + ret = EOK; + +done: + + X509_free(cert); + BIO_free_all(bio_mem); + CRYPTO_cleanup_all_ex_data(); + + if (ret == EOK) { + *content = cont; + } else { + talloc_free(cont); + } + + return ret; } diff --git a/src/lib/certmap/sss_cert_content_nss.c b/src/lib/certmap/sss_cert_content_nss.c index d31828954..9b9409797 100644 --- a/src/lib/certmap/sss_cert_content_nss.c +++ b/src/lib/certmap/sss_cert_content_nss.c @@ -278,38 +278,6 @@ enum san_opt nss_name_type_to_san_opt(CERTGeneralNameType type) } } -static int add_to_san_list(TALLOC_CTX *mem_ctx, bool is_bin, - enum san_opt san_opt, uint8_t *data, size_t len, - struct san_list **item) -{ - struct san_list *i; - - if (data == NULL || len == 0 || san_opt == SAN_INVALID) { - return EINVAL; - } - - i = talloc_zero(mem_ctx, struct san_list); - if (i == NULL) { - return ENOMEM; - } - - i->san_opt = san_opt; - if (is_bin) { - i->bin_val = talloc_memdup(i, data, len); - i->bin_val_len = len; - } else { - i->val = talloc_strndup(i, (char *) data, len); - } - if (i->val == NULL) { - talloc_free(i); - return ENOMEM; - } - - *item = i; - - return 0; -} - /* taken from pkinit_crypto_nss.c of MIT Kerberos */ /* KerberosString: RFC 4120, 5.2.1. */ static const SEC_ASN1Template kerberos_string_template[] = { @@ -397,9 +365,6 @@ static const SEC_ASN1Template kerberos_principal_name_template[] = { {0, 0, NULL, 0} }; -#define PKINIT_OID "1.3.6.1.5.2.2" -#define NT_PRINCIPAL_OID "1.3.6.1.4.1.311.20.2.3" - static int add_string_other_name_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt, CERTGeneralName *current, @@ -451,31 +416,6 @@ done: return ret; } -static int get_short_name(TALLOC_CTX *mem_ctx, const char *full_name, - char delim, char **short_name) -{ - char *at; - char *s; - - if (full_name == NULL || delim == '\0' || short_name == NULL) { - return EINVAL; - } - - at = strchr(full_name, delim); - if (at != NULL) { - s = talloc_strndup(mem_ctx, full_name, (at - full_name)); - } else { - s = talloc_strdup(mem_ctx, full_name); - } - if (s == NULL) { - return ENOMEM; - } - - *short_name = s; - - return 0; -} - static int add_nt_princ_to_san_list(TALLOC_CTX *mem_ctx, PLArenaPool *pool, enum san_opt san_opt, @@ -532,7 +472,7 @@ static int add_pkinit_princ_to_san_list(TALLOC_CTX *mem_ctx, { struct san_list *i = NULL; SECStatus rv; - struct kerberos_principal_name kname; + struct kerberos_principal_name kname = { 0 }; int ret; size_t c; @@ -571,9 +511,9 @@ static int add_pkinit_princ_to_san_list(TALLOC_CTX *mem_ctx, goto done; } } - i->val = talloc_strndup_append(i->val, - (char *) kname.realm.data, - kname.realm.len); + i->val = talloc_asprintf_append(i->val, "@%.*s", + kname.realm.len, + (char *) kname.realm.data); if (i->val == NULL) { ret = ENOMEM; goto done; @@ -706,42 +646,6 @@ static int add_ip_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt, *item = i; return 0; } -static int add_principal_to_san_list(TALLOC_CTX *mem_ctx, - enum san_opt san_opt, - const char *princ, - struct san_list **item) -{ - struct san_list *i = NULL; - int ret; - - i = talloc_zero(mem_ctx, struct san_list); - if (i == NULL) { - return ENOMEM; - } - i->san_opt = san_opt; - - i->val = talloc_strdup(i, princ); - if (i->val == NULL) { - ret = ENOMEM; - goto done; - } - - ret = get_short_name(i, i->val, '@', &(i->short_name)); - if (ret != 0) { - goto done; - } - - ret = 0; - -done: - if (ret == 0) { - *item = i; - } else { - talloc_free(i); - } - - return ret; -} static int get_san(TALLOC_CTX *mem_ctx, CERTCertificate *cert, struct san_list **san_list) @@ -887,6 +791,7 @@ static int get_san(TALLOC_CTX *mem_ctx, CERTCertificate *cert, break; default: ret = EINVAL; + goto done; } current = CERT_GetNextGeneralName(current); diff --git a/src/lib/certmap/sss_certmap.c b/src/lib/certmap/sss_certmap.c index 37bbc5e7c..f6f6f9804 100644 --- a/src/lib/certmap/sss_certmap.c +++ b/src/lib/certmap/sss_certmap.c @@ -311,87 +311,6 @@ done: return ret; } -static int get_dn_str(struct sss_certmap_ctx *ctx, const char *conversion, - const char **rdn_list, char **result) -{ - char *str = NULL; - size_t c; - int ret; - char *conv = NULL; - - str = talloc_strdup(ctx, ""); - if (str == NULL) { - ret = ENOMEM; - goto done; - } - if (conversion == NULL || strcmp(conversion, "nss_ldap") == 0 - || strcmp(conversion, "nss") == 0) { - for (c = 0; rdn_list[c] != NULL; c++); - while (c != 0) { - c--; - str = talloc_asprintf_append(str, "%s%s", - (rdn_list[c + 1] == NULL) ? "" : ",", - rdn_list[c]); - if (str == NULL) { - ret = ENOMEM; - goto done; - } - }; - } else if (strcmp(conversion, "ad_ldap") == 0) { - for (c = 0; rdn_list[c] != NULL; c++); - while (c != 0) { - c--; - conv = check_ad_attr_name(str, rdn_list[c]); - str = talloc_asprintf_append(str, "%s%s", - (rdn_list[c + 1] == NULL) ? "" : ",", - conv == NULL ? rdn_list[c] : conv); - talloc_free(conv); - conv = NULL; - if (str == NULL) { - ret = ENOMEM; - goto done; - } - }; - } else if (strcmp(conversion, "nss_x500") == 0) { - for (c = 0; rdn_list[c] != NULL; c++) { - str = talloc_asprintf_append(str, "%s%s", (c == 0) ? "" : ",", - rdn_list[c]); - if (str == NULL) { - ret = ENOMEM; - goto done; - } - } - } else if (strcmp(conversion, "ad_x500") == 0 - || strcmp(conversion, "ad") == 0) { - for (c = 0; rdn_list[c] != NULL; c++) { - conv = check_ad_attr_name(str, rdn_list[c]); - str = talloc_asprintf_append(str, "%s%s", - (c == 0) ? "" : ",", - conv == NULL ? rdn_list[c] : conv); - talloc_free(conv); - conv = NULL; - if (str == NULL) { - ret = ENOMEM; - goto done; - } - } - } else { - ret = EINVAL; - goto done; - } - - ret = 0; - -done: - if (ret == 0) { - *result = str; - } else { - talloc_free(str); - } - - return ret; -} - static int expand_san_blob(struct sss_certmap_ctx *ctx, enum san_opt san_opt, struct san_list *san_list, char **expanded) { @@ -458,7 +377,7 @@ static int expand_san_rdn_list(struct sss_certmap_ctx *ctx, DLIST_FOR_EACH(item, san_list) { if (item->san_opt == san_opt) { - ret = get_dn_str(ctx, conversion, item->rdn_list, &exp); + ret = rdn_list_2_dn_str(ctx, conversion, item->rdn_list, &exp); if (ret != 0) { return ret; } @@ -528,11 +447,11 @@ static int expand_template(struct sss_certmap_ctx *ctx, char *exp = NULL; if (strcmp("issuer_dn", parsed_template->name) == 0) { - ret = get_dn_str(ctx, parsed_template->conversion, - cert_content->issuer_rdn_list, &exp); + ret = rdn_list_2_dn_str(ctx, parsed_template->conversion, + cert_content->issuer_rdn_list, &exp); } else if (strcmp("subject_dn", parsed_template->name) == 0) { - ret = get_dn_str(ctx, parsed_template->conversion, - cert_content->subject_rdn_list, &exp); + ret = rdn_list_2_dn_str(ctx, parsed_template->conversion, + cert_content->subject_rdn_list, &exp); } else if (strncmp("subject_", parsed_template->name, 8) == 0) { ret = expand_san(ctx, parsed_template, cert_content->san_list, &exp); } else if (strcmp("cert", parsed_template->name) == 0) { @@ -629,7 +548,7 @@ static bool check_san_regexp(struct sss_certmap_ctx *ctx, if (item->san_opt == san_opt) { if (item->san_opt == SAN_DIRECTORY_NAME) { /* use LDAP order for matching */ - ret = get_dn_str(ctx, NULL, item->rdn_list, &tmp_str); + ret = rdn_list_2_dn_str(ctx, NULL, item->rdn_list, &tmp_str); if (ret != 0 || tmp_str == NULL) { return false; } diff --git a/src/lib/certmap/sss_certmap_attr_names.c b/src/lib/certmap/sss_certmap_attr_names.c index a28a46491..65c0f9109 100644 --- a/src/lib/certmap/sss_certmap_attr_names.c +++ b/src/lib/certmap/sss_certmap_attr_names.c @@ -30,44 +30,48 @@ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa376556%28v=vs.85%29.aspx * and wine source code dlls/crypt32/oid.c and include/wincrypt.h . */ +/* OpenSSL data taken from include/openssl/obj_mac.h */ + #include <stdbool.h> #include <string.h> #include <talloc.h> struct oid_attr_name_map { bool nss_ad_differ; + bool nss_openssl_differ; const char *oid; const char *nss; const char *ad; + const char *openssl; } oid_attr_name_map[] = { - { false, "2.5.4.3", "CN", "CN"}, - { true, "2.5.4.8", "ST", "S"}, - { false, "2.5.4.10", "O", "O"}, - { false, "2.5.4.11", "OU", "OU"}, - { false, "2.5.4.46", "dnQualifier", "dnQualifier"}, - { false, "2.5.4.6", "C", "C"}, - { true, "2.5.4.5", "serialNumber", "SERIALNUMBER"}, - { false, "2.5.4.7", "L", "L"}, - { true, "2.5.4.12", "title", "T"}, - { false, "2.5.4.4", "SN", "SN"}, - { true, "2.5.4.42", "givenName", "G"}, - { true, "2.5.4.43", "initials", "I"}, - { true, "2.5.4.44", "generationQualifier", "OID.2.5.4.44"}, - { false, "0.9.2342.19200300.100.1.25", "DC", "DC"}, - { true, "0.9.2342.19200300.100.1.3", "MAIL", "OID,0.9.2342.19200300.100.1.3"}, - { true, "0.9.2342.19200300.100.1.1", "UID", "OID.0.9.2342.19200300.100.1.1"}, - { true, "2.5.4.13", "OID.2.5.4.13", "Description"}, - { true, "2.5.4.16", "postalAddress", "OID.2.5.4.16"}, - { true, "2.5.4.17", "postalCode", "PostalCode"}, - { true, "2.5.4.18", "postOfficeBox", "POBox"}, - { true, "2.5.4.51", "houseIdentifier", "OID.2.5.4.51"}, - { false, "1.2.840.113549.1.9.1", "E", "E"}, - { false, "2.5.4.9", "STREET", "STREET"}, - { true, "2.5.4.65", "pseudonym", "OID.2.5.4.65"}, - { true, "2.5.4.15", "businessCategory", "OID.2.5.4.15"}, - { true, "2.5.4.41", "name", "OID.2.5.4.41"}, - - { false, NULL, NULL, NULL} + { false, false, "2.5.4.3", "CN", "CN", "CN"}, + { true, false, "2.5.4.8", "ST", "S", "ST"}, + { false, false, "2.5.4.10", "O", "O", "O"}, + { false, false, "2.5.4.11", "OU", "OU", "OU"}, + { false, false, "2.5.4.46", "dnQualifier", "dnQualifier", "dnQualifier"}, + { false, false, "2.5.4.6", "C", "C", "C"}, + { true, false, "2.5.4.5", "serialNumber", "SERIALNUMBER", "serialNumber"}, + { false, false, "2.5.4.7", "L", "L", "L"}, + { true, false, "2.5.4.12", "title", "T", "title"}, + { false, false, "2.5.4.4", "SN", "SN", "SN"}, + { true, true, "2.5.4.42", "givenName", "G", "GN"}, + { true, false, "2.5.4.43", "initials", "I", "initials"}, + { true, false, "2.5.4.44", "generationQualifier", "OID.2.5.4.44", "generationQualifier"}, + { false, false, "0.9.2342.19200300.100.1.25", "DC", "DC", "DC"}, + { true, true, "0.9.2342.19200300.100.1.3", "MAIL", "OID,0.9.2342.19200300.100.1.3", "mail"}, + { true, false, "0.9.2342.19200300.100.1.1", "UID", "OID.0.9.2342.19200300.100.1.1", "UID"}, + { true, true, "2.5.4.13", "OID.2.5.4.13", "Description", "description"}, + { true, false, "2.5.4.16", "postalAddress", "OID.2.5.4.16", "postalAddress"}, + { true, false, "2.5.4.17", "postalCode", "PostalCode", "postalCode"}, + { true, false, "2.5.4.18", "postOfficeBox", "POBox", "postOfficeBox"}, + { true, false, "2.5.4.51", "houseIdentifier", "OID.2.5.4.51", "houseIdentifier"}, + { false, true, "1.2.840.113549.1.9.1", "E", "E", "emailAddress"}, + { false, true, "2.5.4.9", "STREET", "STREET", "street"}, + { true, false, "2.5.4.65", "pseudonym", "OID.2.5.4.65", "pseudonym"}, + { true, false, "2.5.4.15", "businessCategory", "OID.2.5.4.15", "businessCategory"}, + { true, false, "2.5.4.41", "name", "OID.2.5.4.41", "name"}, + + { false, false, NULL, NULL, NULL, NULL} }; char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn) @@ -105,3 +109,26 @@ char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn) return NULL; } + +const char *openssl_2_nss_attr_name(const char *attr) +{ + size_t c; + + if (attr == NULL) { + return NULL; + } + + for (c = 0; oid_attr_name_map[c].oid != NULL; c++) { + if (!oid_attr_name_map[c].nss_openssl_differ) { + continue; + } + + if (strcmp(attr, oid_attr_name_map[c].openssl) != 0) { + continue; + } + + return oid_attr_name_map[c].nss; + } + + return attr; +} diff --git a/src/lib/certmap/sss_certmap_int.h b/src/lib/certmap/sss_certmap_int.h index 0b4cda736..479cc1606 100644 --- a/src/lib/certmap/sss_certmap_int.h +++ b/src/lib/certmap/sss_certmap_int.h @@ -28,8 +28,11 @@ #include <sys/types.h> #include <regex.h> #include <stdint.h> +#include <stdbool.h> #include <talloc.h> +#include "lib/certmap/sss_certmap.h" + #define CM_DEBUG(cm_ctx, format, ...) do { \ if (cm_ctx != NULL && cm_ctx->debug != NULL) { \ cm_ctx->debug(cm_ctx->debug_priv, __FILE__, __LINE__, __FUNCTION__, \ @@ -40,6 +43,9 @@ #define DEFAULT_MATCH_RULE "<KU>digitalSignature<EKU>clientAuth" #define DEFAULT_MAP_RULE "LDAP:(userCertificate;binary={cert!bin})" +#define PKINIT_OID "1.3.6.1.5.2.2" +#define NT_PRINCIPAL_OID "1.3.6.1.4.1.311.20.2.3" + enum san_opt { SAN_OTHER_NAME = 0, SAN_RFC822_NAME, @@ -161,9 +167,9 @@ struct san_list { #define SSS_KU_DECIPHER_ONLY 0x8000 struct sss_cert_content { - const char *issuer_str; + char *issuer_str; const char **issuer_rdn_list; - const char *subject_str; + char *subject_str; const char **subject_rdn_list; uint32_t key_usage; const char **extended_key_usage_oids; @@ -179,6 +185,8 @@ int sss_cert_get_content(TALLOC_CTX *mem_ctx, char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn); +char *openssl_2_nss_attr_name(const char *attr); + int parse_krb5_match_rule(struct sss_certmap_ctx *ctx, const char *rule_start, struct krb5_match_rule **match_rule); @@ -186,4 +194,17 @@ int parse_krb5_match_rule(struct sss_certmap_ctx *ctx, int parse_ldap_mapping_rule(struct sss_certmap_ctx *ctx, const char *rule_start, struct ldap_mapping_rule **mapping_rule); + +int get_short_name(TALLOC_CTX *mem_ctx, const char *full_name, + char delim, char **short_name); + +int add_to_san_list(TALLOC_CTX *mem_ctx, bool is_bin, + enum san_opt san_opt, const uint8_t *data, size_t len, + struct san_list **item); + +int add_principal_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt, + const char *princ, struct san_list **item); + +int rdn_list_2_dn_str(TALLOC_CTX *mem_ctx, const char *conversion, + const char **rdn_list, char **result); #endif /* __SSS_CERTMAP_INT_H__ */ |