diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-01-11 15:09:23 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-01-12 15:42:23 +0100 |
commit | 332475e9cf8cf7afe117e93555dfe24df898bc4b (patch) | |
tree | df45239e64d003cfa3992c2efbbec7071a868b17 | |
parent | 0a92ec601c3d33d6b939e2cd2e22302584fe8eea (diff) | |
download | gnutls-332475e9cf8cf7afe117e93555dfe24df898bc4b.tar.gz |
x509: introduced GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL
That allows to print and write KRB5PrincipalName othernames
in subject alternative name.
-rw-r--r-- | lib/gnutls.asn | 11 | ||||
-rw-r--r-- | lib/gnutls_asn1_tab.c | 13 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 7 | ||||
-rw-r--r-- | lib/x509/Makefile.am | 3 | ||||
-rw-r--r-- | lib/x509/common.h | 1 | ||||
-rw-r--r-- | lib/x509/krb5.c | 324 | ||||
-rw-r--r-- | lib/x509/krb5.h | 22 | ||||
-rw-r--r-- | lib/x509/output.c | 6 | ||||
-rw-r--r-- | lib/x509/virt-san.c | 23 |
9 files changed, 405 insertions, 5 deletions
diff --git a/lib/gnutls.asn b/lib/gnutls.asn index 6342995330..76bad6fbb6 100644 --- a/lib/gnutls.asn +++ b/lib/gnutls.asn @@ -111,4 +111,15 @@ ECPrivateKey ::= SEQUENCE { publicKey [1] BIT STRING OPTIONAL } +-- Structures used for the PKINIT othername variables +PrincipalName ::= SEQUENCE { + name-type [0] INTEGER, + name-string [1] SEQUENCE OF GeneralString +} + +KRB5PrincipalName ::= SEQUENCE { + realm [0] GeneralString, + principalName [1] PrincipalName +} + END diff --git a/lib/gnutls_asn1_tab.c b/lib/gnutls_asn1_tab.c index 5da8f7d742..bc0e85ef5d 100644 --- a/lib/gnutls_asn1_tab.c +++ b/lib/gnutls_asn1_tab.c @@ -68,12 +68,23 @@ const asn1_static_node gnutls_asn1_tab[] = { { "ECPoint", 1073741831, NULL }, { "ECParameters", 1610612754, NULL }, { "namedCurve", 12, NULL }, - { "ECPrivateKey", 536870917, NULL }, + { "ECPrivateKey", 1610612741, NULL }, { "Version", 1073741827, NULL }, { "privateKey", 1073741831, NULL }, { "parameters", 1610637314, "ECParameters"}, { NULL, 2056, "0"}, { "publicKey", 536895494, NULL }, { NULL, 2056, "1"}, + { "PrincipalName", 1610612741, NULL }, + { "name-type", 1610620931, NULL }, + { NULL, 2056, "0"}, + { "name-string", 536879115, NULL }, + { NULL, 1073743880, "1"}, + { NULL, 27, NULL }, + { "KRB5PrincipalName", 536870917, NULL }, + { "realm", 1610620955, NULL }, + { NULL, 2056, "0"}, + { "principalName", 536879106, "PrincipalName"}, + { NULL, 2056, "1"}, { NULL, 0, NULL } }; diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 759a0d6a4f..e9e613918d 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -2030,8 +2030,8 @@ gnutls_psk_set_server_params_function(gnutls_psk_server_credentials_t * @GNUTLS_SAN_IPADDRESS: IP address SAN. * @GNUTLS_SAN_OTHERNAME: OtherName SAN. * @GNUTLS_SAN_DN: DN SAN. - * @GNUTLS_SAN_OTHERNAME_XMPP: Virtual SAN, used by - * gnutls_x509_crt_get_subject_alt_othername_oid. + * @GNUTLS_SAN_OTHERNAME_XMPP: Virtual SAN, used by certain functions for convenience. + * @GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: Virtual SAN, used by certain functions for convenience. * * Enumeration of different subject alternative names types. */ @@ -2045,7 +2045,8 @@ typedef enum gnutls_x509_subject_alt_name_t { /* The following are "virtual" subject alternative name types, in that they are represented by an otherName value and an OID. Used by gnutls_x509_crt_get_subject_alt_othername_oid. */ - GNUTLS_SAN_OTHERNAME_XMPP = 1000 + GNUTLS_SAN_OTHERNAME_XMPP = 1000, + GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL } gnutls_x509_subject_alt_name_t; struct gnutls_openpgp_crt_int; diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am index c64f7cf9ba..34bb2384a9 100644 --- a/lib/x509/Makefile.am +++ b/lib/x509/Makefile.am @@ -65,7 +65,8 @@ libgnutls_x509_la_SOURCES = \ pkcs7-output.c \ virt-san.c \ virt-san.h \ - x509_ext_int.h + x509_ext_int.h \ + krb5.c krb5.h if ENABLE_OCSP libgnutls_x509_la_SOURCES += ocsp.c ocsp_output.c diff --git a/lib/x509/common.h b/lib/x509/common.h index 702c22bcae..445c3ddd10 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -70,6 +70,7 @@ #define ISO_SIG_RSA_SHA1_OID "1.3.14.3.2.29" #define XMPP_OID "1.3.6.1.5.5.7.8.5" +#define KRB5_PRINCIPAL_OID "1.3.6.1.5.2.2" #define ASN1_NULL "\x05\x00" #define ASN1_NULL_SIZE 2 diff --git a/lib/x509/krb5.c b/lib/x509/krb5.c new file mode 100644 index 0000000000..45b71ba2b9 --- /dev/null +++ b/lib/x509/krb5.c @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This file is part of GnuTLS. + * + * GnuTLS 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. + * + * GnuTLS 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 <config.h> +#include <gnutls/gnutls.h> +#include <libtasn1.h> +#include <string.h> +#include <stdint.h> +#include <stdlib.h> +#include <errors.h> +#include "krb5.h" +#include "common.h" + +#define _gnutls_asn2err(x) GNUTLS_E_ASN1_DER_ERROR + +#define MAX_COMPONENTS 6 + +typedef struct krb5_principal_data { + char *realm; + char *data[MAX_COMPONENTS]; + uint32_t length; + int8_t type; +} krb5_principal_data; + +extern const asn1_static_node krb5_asn1_tab[]; + +static void cleanup_principal(krb5_principal_data *princ) +{ + unsigned i; + if (princ) { + gnutls_free(princ->realm); + for (i=0;i<princ->length;i++) + gnutls_free(princ->data[i]); + memset(princ, 0, sizeof(*princ)); + gnutls_free(princ); + } +} + +static krb5_principal_data* name_to_principal(const char *_name) +{ + krb5_principal_data *princ; + char *p, *p2, *sp; + unsigned pos = 0; + char *name = NULL; + + princ = gnutls_calloc(1, sizeof(struct krb5_principal_data)); + if (princ == NULL) + return NULL; + + name = gnutls_strdup(_name); + if (name == NULL) { + gnutls_assert(); + goto fail; + } + + p = strrchr(name, '@'); + p2 = strchr(name, '@'); + if (p == NULL) { + /* unknown name type */ + gnutls_assert(); + goto fail; + } + + princ->realm = gnutls_strdup(p+1); + if (princ->realm == NULL) { + gnutls_assert(); + goto fail; + } + *p = 0; + + if (p == p2) { + p = strtok_r(name, "/", &sp); + while(p) { + if (pos == MAX_COMPONENTS) { + _gnutls_debug_log("%s: Cannot parse names with more than %d components\n", __func__, MAX_COMPONENTS); + goto fail; + } + + princ->data[pos] = gnutls_strdup(p); + if (princ->data[pos] == NULL) { + gnutls_assert(); + goto fail; + } + + princ->length++; + pos++; + + p = strtok_r(NULL, "/", &sp); + } + + if ((princ->length == 2) && (strcmp (princ->data[0], "krbtgt") == 0)) { + princ->type = 2; /* KRB_NT_SRV_INST */ + } else { + princ->type = 1; /* KRB_NT_PRINCIPAL */ + } + } else { /* enterprise */ + princ->data[0] = gnutls_strdup(name); + if (princ->data[0] == NULL) { + gnutls_assert(); + goto fail; + } + + princ->length++; + princ->type = 10; /* KRB_NT_ENTERPRISE */ + } + + goto cleanup; + fail: + cleanup_principal(princ); + princ = NULL; + + cleanup: + gnutls_free(name); + return princ; +} + +int _gnutls_krb5_principal_to_der(const char *name, gnutls_datum * der) +{ + int ret, result; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + krb5_principal_data * princ; + unsigned i; + + princ = name_to_principal(name); + if (princ == NULL) { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + + result = asn1_create_element(_gnutls_get_gnutls_asn(), "GNUTLS.KRB5PrincipalName", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(c2, "realm", princ->realm, strlen(princ->realm)); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(c2, "principalName.name-type", &princ->type, + 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + for (i = 0; i < princ->length; i++) { + result = + asn1_write_value(c2, "principalName.name-string", + "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(c2, + "principalName.name-string.?LAST", + princ->data[i], strlen(princ->data[i])); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + } + + ret = _gnutls_x509_der_encode(c2, "", der, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + cleanup_principal(princ); + asn1_delete_structure(&c2); + return ret; +} + +static int principal_to_str(ASN1_TYPE c2, gnutls_buffer_st *str) +{ + gnutls_datum_t realm = {NULL, 0}; + gnutls_datum_t component = {NULL, 0}; + unsigned char name_type[2]; + int ret, result, len; + unsigned i; + char val[128]; + + ret = _gnutls_x509_read_value(c2, "realm", &realm); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + len = sizeof(name_type); + result = asn1_read_value(c2, "principalName.name-type", name_type, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if (len != 1 || (name_type[0] != 1 && name_type[0] != 2 && name_type[0] != 10)) { + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + for (i=0;;i++) { + snprintf(val, sizeof(val), "principalName.name-string.?%u", i+1); + ret = _gnutls_x509_read_value(c2, val, &component); + if (ret == GNUTLS_E_ASN1_VALUE_NOT_FOUND || ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + break; + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (i>0) { + ret = _gnutls_buffer_append_data(str, "/", 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = _gnutls_buffer_append_data(str, component.data, component.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_free_datum(&component); + } + + ret = _gnutls_buffer_append_data(str, "@", 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_buffer_append_data(str, realm.data, realm.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + _gnutls_free_datum(&component); + gnutls_free(realm.data); + return ret; +} + +int _gnutls_krb5_der_to_principal(const gnutls_datum * der, gnutls_datum_t *name) +{ + int ret, result; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + gnutls_buffer_st str; + + result = asn1_create_element(_gnutls_get_gnutls_asn(), "GNUTLS.KRB5PrincipalName", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_der_decoding(&c2, der->data, der->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + _gnutls_buffer_init(&str); + + ret = principal_to_str(c2, &str); + if (ret < 0) { + /* for some reason we cannot convert to a human readable string + * the principal. Then we use the #HEX format. + */ + _gnutls_buffer_reset(&str); + ret = _gnutls_buffer_append_data(&str, "#", 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_buffer_hexprint(&str, der->data, der->size); + } + + asn1_delete_structure(&c2); + return _gnutls_buffer_to_datum(&str, name, 1); + + cleanup: + _gnutls_buffer_clear(&str); + asn1_delete_structure(&c2); + return ret; +} diff --git a/lib/x509/krb5.h b/lib/x509/krb5.h new file mode 100644 index 0000000000..94d4867ec9 --- /dev/null +++ b/lib/x509/krb5.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This file is part of GnuTLS. + * + * GnuTLS 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. + * + * GnuTLS 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/>. + */ + +int _gnutls_krb5_principal_to_der(const char *name, gnutls_datum * der); +int _gnutls_krb5_der_to_principal(const gnutls_datum * der, gnutls_datum_t *name); diff --git a/lib/x509/output.c b/lib/x509/output.c index 8c4c24e16f..6134e5b554 100644 --- a/lib/x509/output.c +++ b/lib/x509/output.c @@ -81,6 +81,7 @@ unsigned i; #endif if ((type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_OTHERNAME_XMPP + || type == GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL || type == GNUTLS_SAN_RFC822NAME || type == GNUTLS_SAN_URI) && sname != NULL && strlen(sname) != name->size) { adds(str, @@ -140,6 +141,11 @@ unsigned i; case GNUTLS_SAN_OTHERNAME_XMPP: addf(str, _("%sXMPP Address: %.*s\n"), prefix, name->size, NON_NULL(name->data)); break; + + case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: + addf(str, _("%sKRB5Principal: %.*s\n"), prefix, name->size, NON_NULL(name->data)); + break; + default: addf(str, _("%sUnknown name: "), prefix); _gnutls_buffer_hexprint(str, name->data, name->size); diff --git a/lib/x509/virt-san.c b/lib/x509/virt-san.c index cefcee68a0..83e5414479 100644 --- a/lib/x509/virt-san.c +++ b/lib/x509/virt-san.c @@ -26,6 +26,7 @@ #include "gnutls_int.h" #include "x509_int.h" #include "common.h" +#include "krb5.h" #include "virt-san.h" static @@ -35,6 +36,9 @@ int san_othername_to_virtual(const char *oid, size_t size) if ((unsigned) size == (sizeof(XMPP_OID)-1) && memcmp(oid, XMPP_OID, sizeof(XMPP_OID)-1) == 0) return GNUTLS_SAN_OTHERNAME_XMPP; + else if ((unsigned) size == (sizeof(KRB5_PRINCIPAL_OID)-1) + && memcmp(oid, KRB5_PRINCIPAL_OID, sizeof(KRB5_PRINCIPAL_OID)-1) == 0) + return GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL; } return GNUTLS_SAN_OTHERNAME; @@ -46,6 +50,8 @@ const char * virtual_to_othername_oid(unsigned type) switch(type) { case GNUTLS_SAN_OTHERNAME_XMPP: return XMPP_OID; + case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: + return KRB5_PRINCIPAL_OID; default: return NULL; } @@ -89,6 +95,16 @@ int _gnutls_alt_name_assign_virt_type(struct name_st *name, unsigned type, gnutl name->othername_oid.size = strlen(oid); break; + case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: + ret = _gnutls_krb5_principal_to_der((char*)san->data, &name->san); + if (ret < 0) + return gnutls_assert_val(ret); + + name->othername_oid.data = (void*)gnutls_strdup(oid); + name->othername_oid.size = strlen(oid); + name->type = GNUTLS_SAN_OTHERNAME; + break; + default: return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); } @@ -138,6 +154,13 @@ int gnutls_x509_othername_to_virtual(const char *oid, return ret; } return 0; + case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: + ret = _gnutls_krb5_der_to_principal(othername, virt); + if (ret < 0) { + gnutls_assert(); + return ret; + } + return 0; default: return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } |