From: Nikos Mavrogiannopoulos Date: Tue, 15 Jan 2013 22:32:26 +0000 (+0100) Subject: Added functions to directly set the DN in a certificate or request from an RFC4514... X-Git-Tag: gnutls_3_1_7~111 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=98cdc97bd080cfaba6a6ada0ab9addb4adef66e0;p=thirdparty%2Fgnutls.git Added functions to directly set the DN in a certificate or request from an RFC4514 string. --- diff --git a/NEWS b/NEWS index 54d5aa60bb..9037d8ceb5 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,12 @@ See the end for copying conditions. * Version 3.1.7 (unreleased) +** certtool: Added option "dn" which allows to directly set the DN +in a template from an RFC4514 string. + +** libgnutls: Added functions to directly set the DN in a certificate +or request from an RFC4514 string. + ** libgnutls: Simplified the DTLS sliding window implementation. ** libgnutls: Added the gnutls_sbuf_t structure and accompanying @@ -20,6 +26,8 @@ gnutls_sbuf_deinit: Added gnutls_sbuf_init: Added gnutls_sbuf_flush: Added gnutls_sbuf_queue: Added +gnutls_x509_crt_set_dn: Added +gnutls_x509_crq_set_dn: Added * Version 3.1.6 (released 2013-01-02) diff --git a/doc/TODO b/doc/TODO index 165f568ee6..0b7d0e1b78 100644 --- a/doc/TODO +++ b/doc/TODO @@ -3,6 +3,8 @@ anything), contact the developer's mailing list (gnutls-dev@lists.gnupg.org), in order to avoid having people working on the same thing. Current list: +* Add gnutls_x509_crt_set_dn() to allow setting a DN using an RFC4514 + string (as well as the x509_crq_set_dn). * When importing a PKCS #11 certificate, check for its issuers to generate a chain (e.g. use the DN to retrieve possible signers). * Think about supporting the groups in RFC3526 and RFC5114. If other diff --git a/doc/cha-cert-auth2.texi b/doc/cha-cert-auth2.texi index b49c201740..369326f7bc 100644 --- a/doc/cha-cert-auth2.texi +++ b/doc/cha-cert-auth2.texi @@ -35,7 +35,7 @@ subject's information and finally self signing it. The last step ensures that the requester is in possession of the private key. -@showfuncE{gnutls_x509_crq_set_version,gnutls_x509_crq_set_dn_by_oid,gnutls_x509_crq_set_key_usage,gnutls_x509_crq_set_key_purpose_oid,gnutls_x509_crq_set_basic_constraints} +@showfuncF{gnutls_x509_crq_set_version,gnutls_x509_crq_set_dn,gnutls_x509_crq_set_dn_by_oid,gnutls_x509_crq_set_key_usage,gnutls_x509_crq_set_key_purpose_oid,gnutls_x509_crq_set_basic_constraints} The @funcref{gnutls_x509_crq_set_key} and @funcref{gnutls_x509_crq_sign2} functions associate the request with a private key and sign it. If a diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index 0737863f3c..0cded3cd60 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -397,6 +397,8 @@ extern "C" /* X.509 Certificate writing. */ + int gnutls_x509_crt_set_dn (gnutls_x509_crt_t crt, const char *dn, const char** err); + int gnutls_x509_crt_set_dn_by_oid (gnutls_x509_crt_t crt, const char *oid, unsigned int raw_flag, @@ -918,6 +920,7 @@ extern "C" const char *oid, int indx, unsigned int raw_flag, void *buf, size_t * sizeof_buf); + int gnutls_x509_crq_set_dn (gnutls_x509_crq_t crq, const char *dn, const char** err); int gnutls_x509_crq_set_dn_by_oid (gnutls_x509_crq_t crq, const char *oid, unsigned int raw_flag, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 99c97322da..e86e827635 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -881,6 +881,8 @@ GNUTLS_3_1_0 { gnutls_sbuf_init; gnutls_sbuf_flush; gnutls_sbuf_queue; + gnutls_x509_crt_set_dn; + gnutls_x509_crq_set_dn; } GNUTLS_3_0_0; GNUTLS_PRIVATE { diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am index c80fc43906..93fbd24057 100644 --- a/lib/x509/Makefile.am +++ b/lib/x509/Makefile.am @@ -53,7 +53,7 @@ libgnutls_x509_la_SOURCES = \ rfc2818_hostname.c \ sign.c \ verify.c \ - x509.c \ + x509.c x509_dn.c \ x509_int.h \ x509_write.c \ verify-high.c \ diff --git a/lib/x509/common.c b/lib/x509/common.c index 02f9fdacf4..5f89fa0a1d 100644 --- a/lib/x509/common.c +++ b/lib/x509/common.c @@ -47,15 +47,14 @@ struct oid_to_string static const struct oid_to_string _oid2str[] = { /* PKIX */ - {"1.3.6.1.5.5.7.9.1", "dateOfBirth", NULL, ASN1_ETYPE_GENERALIZED_TIME}, {"1.3.6.1.5.5.7.9.2", "placeOfBirth", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, {"1.3.6.1.5.5.7.9.3", "gender", NULL, ASN1_ETYPE_PRINTABLE_STRING}, {"1.3.6.1.5.5.7.9.4", "countryOfCitizenship", NULL, ASN1_ETYPE_PRINTABLE_STRING}, {"1.3.6.1.5.5.7.9.5", "countryOfResidence", NULL, ASN1_ETYPE_PRINTABLE_STRING}, {"2.5.4.6", "C", NULL, ASN1_ETYPE_PRINTABLE_STRING}, - {"2.5.4.9", "STREET", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, - {"2.5.4.12", "T", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, + {"2.5.4.9", "street", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, + {"2.5.4.12", "title", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, {"2.5.4.10", "O", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, {"2.5.4.11", "OU", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, {"2.5.4.3", "CN", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, @@ -71,7 +70,7 @@ static const struct oid_to_string _oid2str[] = { {"2.5.4.65", "pseudonym", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, {"2.5.4.46", "dnQualifier", NULL, ASN1_ETYPE_PRINTABLE_STRING}, {"2.5.4.17", "postalCode", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, - {"2.5.4.41", "Name", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, + {"2.5.4.41", "name", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, {"2.5.4.15", "businessCategory", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID}, {"0.9.2342.19200300.100.1.25", "DC", NULL, ASN1_ETYPE_IA5_STRING}, @@ -116,9 +115,26 @@ static const struct oid_to_string* get_oid_entry (const char* oid) while (_oid2str[i].oid != NULL); return NULL; +} + +const char* _gnutls_ldap_string_to_oid (const char* str, unsigned str_len) +{ + unsigned int i = 0; + + do + { + if ((_oid2str[i].ldap_desc != NULL) && + (str_len == strlen(_oid2str[i].ldap_desc)) && + (strncasecmp (_oid2str[i].ldap_desc, str, str_len) == 0)) + return _oid2str[i].oid; + i++; + } + while (_oid2str[i].oid != NULL); + return NULL; } + /** * gnutls_x509_dn_oid_known: * @oid: holds an Object Identifier in a null terminated string diff --git a/lib/x509/common.h b/lib/x509/common.h index faf29a12b2..023c22c016 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -78,6 +78,8 @@ int _gnutls_x509_encode_string(unsigned int etype, int _gnutls_x509_dn_to_string (const char *OID, void *value, int value_size, gnutls_datum_t* out); +const char* _gnutls_ldap_string_to_oid (const char* str, unsigned str_len); + int _gnutls_x509_data2hex (const void * data, size_t data_size, void * out, size_t * sizeof_out); diff --git a/lib/x509/x509_dn.c b/lib/x509/x509_dn.c new file mode 100644 index 0000000000..740c42d183 --- /dev/null +++ b/lib/x509/x509_dn.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2013 Nikos Mavrogiannopoulos + * Copyright (C) 2005 Andrew Suffield + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + */ + +/* This file contains functions to handle X.509 certificate generation. + */ + +#include + +#include +#include +#include +#include +#include +#include + +enum { CRQ, CRT }; + +static +int dn_attr_crt_set( unsigned crt_type, void* crt, const char *attr, unsigned attr_len, + const char *value, unsigned value_len) +{ + char _oid[MAX_OID_SIZE]; + const char *oid; + int ret; + + if (value_len == 0 || attr_len == 0) + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + + if (isdigit(attr[0]) != 0) + { + if (attr_len >= sizeof(_oid)) + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + + memcpy(_oid, attr, attr_len); + _oid[attr_len] = 0; + + oid = _oid; + + if (gnutls_x509_dn_oid_known(oid) == 0) + { + _gnutls_debug_log("Unknown OID: '%s'\n", oid); + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + } + } + else + { + oid = _gnutls_ldap_string_to_oid(attr, attr_len); + } + + if (oid == NULL) + { + _gnutls_debug_log("Unknown DN attribute: '%.*s'\n", attr_len, attr); + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + } + + if (value[0] == '#') + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + + ret = GNUTLS_E_INTERNAL_ERROR; + if (crt_type == CRT) + ret = gnutls_x509_crt_set_dn_by_oid(crt, oid, 0, value, value_len); + else if (crt_type == CRQ) + ret = gnutls_x509_crq_set_dn_by_oid(crt, oid, 0, value, value_len); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +static int +crt_set_dn (unsigned crt_type, void* crt, const char *dn, const char** err) +{ +const char *p = dn; +const char *name_start; +const char *name_end; +const char *value_start; +const char *value_end; +unsigned name_len; +unsigned value_len; +int ret; + + if (crt == NULL || dn == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + /* For each element */ + while (*p != 0 && *p != '\n') + { + /* Skip leading whitespace */ + while (isspace(*p)) + p++; + + if (err) + *err = p; + + /* Attribute name */ + name_start = p; + while (*p != '=' && *p != 0) + p++; + name_end = p; + + /* Whitespace */ + while (isspace(*p)) + p++; + + /* Equals sign */ + if (*p != '=') + { + *err = p; + return GNUTLS_E_PARSING_ERROR; + } + p++; + + /* Whitespace */ + while (isspace(*p)) + p++; + + /* Attribute value */ + value_start = p; + while (*p != 0 && (*p != ',' || (*p == ',' && *(p-1) == '\\')) && *p != '\n') + p++; + value_end = p; + while (value_end > value_start && isspace(value_end[-1])) + value_end--; + + /* Comma, or the end of the string */ + if (*p) + p++; + + name_len = name_end - name_start; + value_len = value_end - value_start; + + ret = dn_attr_crt_set(crt_type, crt, name_start, name_len, value_start, value_len); + if (ret < 0) + return gnutls_assert_val(ret); + } + + return 0; +} + + +/** + * gnutls_x509_crt_set_dn: + * @crt: a certificate of type #gnutls_x509_crt_t + * @dn: a comma separated DN string (RFC4514) + * @err: indicates the error position (if any) + * + * This function will set the DN on the provided certificate. + * The input string should be plain ASCII or UTF-8 encoded. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_dn (gnutls_x509_crt_t crt, const char *dn, const char** err) +{ + return crt_set_dn( CRT, crt, dn, err); +} + +/** + * gnutls_x509_crq_set_dn: + * @crq: a certificate of type #gnutls_x509_crq_t + * @dn: a comma separated DN string (RFC4514) + * @err: indicates the error position (if any) + * + * This function will set the DN on the provided certificate. + * The input string should be plain ASCII or UTF-8 encoded. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_dn (gnutls_x509_crq_t crq, const char *dn, const char** err) +{ + return crt_set_dn( CRQ, crq, dn, err); +} diff --git a/src/certtool-args.c b/src/certtool-args.c index 39b62c5ed5..3f861b7bac 100644 --- a/src/certtool-args.c +++ b/src/certtool-args.c @@ -2,7 +2,7 @@ * * DO NOT EDIT THIS FILE (certtool-args.c) * - * It has been AutoGen-ed December 15, 2012 at 11:11:26 AM by AutoGen 5.16 + * It has been AutoGen-ed January 15, 2013 at 11:30:45 PM by AutoGen 5.16 * From the definitions certtool-args.def * and the template file options * diff --git a/src/certtool-args.def b/src/certtool-args.def index 07e6406b8d..8275359986 100644 --- a/src/certtool-args.def +++ b/src/certtool-args.def @@ -557,6 +557,18 @@ cn = "Cindy Lauper" # certificates. # pkcs9_email = "none@@none.org" +# An alternative way to set the certificate's distinguished name directly +# is with the "dn" option. The attribute names allowed are: +# C (country), street, O (organization), OU (unit), title, CN (common name), +# L (locality), ST (state), placeOfBirth, gender, countryOfCitizenship, +# countryOfResidence, serialNumber, telephoneNumber, surName, initials, +# generationQualifier, givenName, pseudonym, dnQualifier, postalCode, name, +# businessCategory, DC, UID, jurisdictionOfIncorporationLocalityName, +# jurisdictionOfIncorporationStateOrProvinceName, +# jurisdictionOfIncorporationCountryName, XmppAddr, and numeric OIDs. + +#dn = "cn=Nik,st=Attiki,C=GR,surName=Mavrogiannopoulos,2.5.4.9=Arkadias" + # The serial number of the certificate serial = 007 diff --git a/src/certtool-args.h b/src/certtool-args.h index bd1f67090c..1fa15df473 100644 --- a/src/certtool-args.h +++ b/src/certtool-args.h @@ -2,7 +2,7 @@ * * DO NOT EDIT THIS FILE (certtool-args.h) * - * It has been AutoGen-ed December 15, 2012 at 11:11:26 AM by AutoGen 5.16 + * It has been AutoGen-ed January 15, 2013 at 11:30:45 PM by AutoGen 5.16 * From the definitions certtool-args.def * and the template file options * diff --git a/src/certtool-cfg.c b/src/certtool-cfg.c index b1113c61fe..4cace7bb36 100644 --- a/src/certtool-cfg.c +++ b/src/certtool-cfg.c @@ -57,6 +57,7 @@ typedef struct _cfg_ctx char *unit; char *locality; char *state; + char *dn; char *cn; char *uid; char *challenge_password; @@ -213,6 +214,10 @@ template_parse (const char *template) if (val != NULL && val->valType == OPARG_TYPE_STRING) cfg.state = strdup(val->v.strVal); + val = optionGetValue(pov, "dn"); + if (val != NULL && val->valType == OPARG_TYPE_STRING) + cfg.dn = strdup(val->v.strVal); + val = optionGetValue(pov, "cn"); if (val != NULL && val->valType == OPARG_TYPE_STRING) cfg.cn = strdup(val->v.strVal); @@ -663,6 +668,26 @@ get_cn_crt_set (gnutls_x509_crt_t crt) } +void +get_dn_crt_set (gnutls_x509_crt_t crt) +{ + int ret; + const char* err; + + if (batch) + { + if (!cfg.dn) + return; + ret = + gnutls_x509_crt_set_dn (crt, cfg.dn, &err); + if (ret < 0) + { + fprintf (stderr, "set_dn: %s at: %s\n", gnutls_strerror (ret), err); + exit (1); + } + } +} + void get_uid_crt_set (gnutls_x509_crt_t crt) { @@ -1668,6 +1693,26 @@ get_locality_crq_set (gnutls_x509_crq_t crq) } +void +get_dn_crq_set (gnutls_x509_crq_t crq) +{ + int ret; + const char* err; + + if (batch) + { + if (!cfg.dn) + return; + ret = + gnutls_x509_crq_set_dn (crq, cfg.dn, &err); + if (ret < 0) + { + fprintf (stderr, "set_dn: %s at: %s\n", gnutls_strerror (ret), err); + exit (1); + } + } +} + void get_cn_crq_set (gnutls_x509_crq_t crq) { diff --git a/src/certtool-cfg.h b/src/certtool-cfg.h index bcb96d7428..87b5be1eb4 100644 --- a/src/certtool-cfg.h +++ b/src/certtool-cfg.h @@ -44,6 +44,8 @@ void get_unit_crt_set (gnutls_x509_crt_t crt); void get_state_crt_set (gnutls_x509_crt_t crt); void get_locality_crt_set (gnutls_x509_crt_t crt); void get_cn_crt_set (gnutls_x509_crt_t crt); +void get_dn_crt_set (gnutls_x509_crt_t crt); +void get_dn_crq_set (gnutls_x509_crq_t crt); void get_uid_crt_set (gnutls_x509_crt_t crt); void get_pkcs9_email_crt_set (gnutls_x509_crt_t crt); void get_oid_crt_set (gnutls_x509_crt_t crt); diff --git a/src/certtool.c b/src/certtool.c index ab9c7bae2f..e76f197641 100644 --- a/src/certtool.c +++ b/src/certtool.c @@ -271,18 +271,22 @@ generate_certificate (gnutls_privkey_t * ret_key, error (EXIT_FAILURE, 0, "set_proxy_dn: %s", gnutls_strerror (result)); + get_dn_crt_set (crt); get_cn_crt_set (crt); } else { + get_dn_crt_set (crt); + get_country_crt_set (crt); + get_state_crt_set (crt); + get_locality_crt_set (crt); get_organization_crt_set (crt); get_unit_crt_set (crt); - get_locality_crt_set (crt); - get_state_crt_set (crt); get_cn_crt_set (crt); get_dc_set (TYPE_CRT, crt); get_uid_crt_set (crt); + get_oid_crt_set (crt); get_key_purpose_set (crt); @@ -1778,11 +1782,13 @@ generate_request (common_info_st * cinfo) /* Set the DN. */ + get_dn_crq_set (crq); + get_country_crq_set (crq); + get_state_crq_set (crq); + get_locality_crq_set (crq); get_organization_crq_set (crq); get_unit_crq_set (crq); - get_locality_crq_set (crq); - get_state_crq_set (crq); get_cn_crq_set (crq); get_dc_set (TYPE_CRQ, crq); get_uid_crq_set (crq);