diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 07:33:12 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 07:33:12 +0000 |
commit | 36082a2fe36ecd800d784ae44c14f1f18c66a7e9 (patch) | |
tree | 6c68e0c0097987aff85a01dabddd34b862309a7c /src/certtool-cfg.c | |
parent | Initial commit. (diff) | |
download | gnutls28-upstream.tar.xz gnutls28-upstream.zip |
Adding upstream version 3.7.9.upstream/3.7.9upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/certtool-cfg.c')
-rw-r--r-- | src/certtool-cfg.c | 2981 |
1 files changed, 2981 insertions, 0 deletions
diff --git a/src/certtool-cfg.c b/src/certtool-cfg.c new file mode 100644 index 0000000..a8a135a --- /dev/null +++ b/src/certtool-cfg.c @@ -0,0 +1,2981 @@ +/* + * Copyright (C) 2004-2014 Free Software Foundation, Inc. + * Copyright (C) 2013,2014 Nikos Mavrogiannopoulos + * + * 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 + * <https://www.gnu.org/licenses/>. + * + * Written by Nikos Mavrogiannopoulos <nmav@gnutls.org>. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <certtool-cfg.h> +#include <gnutls/x509.h> +#include <gnutls/x509-ext.h> +#include <string.h> +#include <limits.h> +#include <time.h> +#include <timespec.h> +#include <parse-datetime.h> +#include "cfg.h" +#include <intprops.h> +#include <gnutls/crypto.h> +#include <libtasn1.h> + +/* for inet_pton */ +#include <sys/types.h> + +#if HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#elif HAVE_WS2TCPIP_H +#include <ws2tcpip.h> +#endif + +/* From gnulib for inet_pton() */ +#include <arpa/inet.h> + +/* Gnulib portability files. */ +#include <getpass.h> +#include "certtool-common.h" + +/* to print uint64_t */ +# define __STDC_FORMAT_MACROS +# include <inttypes.h> + +extern int batch; +extern int ask_pass; + +#define MAX_ENTRIES 128 +#define MAX_POLICIES 8 + +#define CHECK_MALLOC(x) \ + if (x == NULL) { \ + fprintf(stderr, "memory error\n"); \ + exit(1); \ + } + +#define PRINT_TIME_T_ERROR \ + if (sizeof(time_t) < 8) \ + fprintf(stderr, "This system expresses time with a 32-bit time_t; that prevents dates after 2038 to be expressed by GnuTLS.\n") + +enum option_types { OPTION_NUMERIC, OPTION_STRING, OPTION_BOOLEAN, OPTION_MULTI_LINE }; + +struct cfg_options { + const char *name; + unsigned type; + + /* used when parsing */ + unsigned found; +}; + +static struct cfg_options available_options[] = { + { .name = "unit", .type = OPTION_MULTI_LINE }, + { .name = "ou", .type = OPTION_MULTI_LINE }, + { .name = "organization", .type = OPTION_MULTI_LINE }, + { .name = "o", .type = OPTION_MULTI_LINE }, + { .name = "dc", .type = OPTION_MULTI_LINE }, + { .name = "dns_name", .type = OPTION_MULTI_LINE }, + { .name = "ip_address", .type = OPTION_MULTI_LINE }, + { .name = "email", .type = OPTION_MULTI_LINE }, + { .name = "krb5_principal", .type = OPTION_MULTI_LINE }, + { .name = "other_name", .type = OPTION_MULTI_LINE }, + { .name = "other_name_utf8", .type = OPTION_MULTI_LINE }, + { .name = "other_name_octet", .type = OPTION_MULTI_LINE }, + { .name = "xmpp_name", .type = OPTION_MULTI_LINE }, + { .name = "key_purpose_oid", .type = OPTION_MULTI_LINE }, + { .name = "nc_exclude_dns", .type = OPTION_MULTI_LINE }, + { .name = "nc_exclude_ip", .type = OPTION_MULTI_LINE }, + { .name = "nc_exclude_email", .type = OPTION_MULTI_LINE }, + { .name = "nc_permit_dns", .type = OPTION_MULTI_LINE }, + { .name = "nc_permit_ip", .type = OPTION_MULTI_LINE }, + { .name = "nc_permit_email", .type = OPTION_MULTI_LINE }, + { .name = "dn_oid", .type = OPTION_MULTI_LINE }, + { .name = "add_extension", .type = OPTION_MULTI_LINE }, + { .name = "add_critical_extension", .type = OPTION_MULTI_LINE }, + { .name = "crl_dist_points", .type = OPTION_MULTI_LINE }, + { .name = "uri", .type = OPTION_MULTI_LINE }, + { .name = "ocsp_uri", .type = OPTION_MULTI_LINE }, + { .name = "ca_issuers_uri", .type = OPTION_MULTI_LINE }, + { .name = "locality", .type = OPTION_STRING }, + { .name = "state", .type = OPTION_STRING }, + { .name = "dn", .type = OPTION_STRING }, + { .name = "cn", .type = OPTION_STRING }, + { .name = "uid", .type = OPTION_STRING }, + { .name = "subject_unique_id", .type = OPTION_STRING }, + { .name = "issuer_unique_id", .type = OPTION_STRING }, + { .name = "challenge_password", .type = OPTION_STRING }, + { .name = "password", .type = OPTION_STRING }, + { .name = "pkcs9_email", .type = OPTION_STRING }, + { .name = "country", .type = OPTION_STRING }, + { .name = "expiration_date", .type = OPTION_STRING }, + { .name = "activation_date", .type = OPTION_STRING }, + { .name = "crl_revocation_date", .type = OPTION_STRING }, + { .name = "crl_this_update_date", .type = OPTION_STRING }, + { .name = "crl_next_update_date", .type = OPTION_STRING }, + { .name = "policy*", .type = OPTION_MULTI_LINE }, /* not a multi-line but there are multi as it is a wildcard */ + { .name = "inhibit_anypolicy_skip_certs", .type = OPTION_NUMERIC }, + { .name = "pkcs12_key_name", .type = OPTION_STRING }, + { .name = "proxy_policy_language", .type = OPTION_STRING }, + { .name = "serial", .type = OPTION_STRING }, + { .name = "expiration_days", .type = OPTION_NUMERIC }, + { .name = "crl_next_update", .type = OPTION_NUMERIC }, + { .name = "crl_number", .type = OPTION_STRING }, + { .name = "path_len", .type = OPTION_NUMERIC }, + { .name = "ca", .type = OPTION_BOOLEAN }, + { .name = "honor_crq_extensions", .type = OPTION_BOOLEAN }, + { .name = "honor_crq_ext", .type = OPTION_MULTI_LINE }, + { .name = "tls_www_client", .type = OPTION_BOOLEAN }, + { .name = "tls_www_server", .type = OPTION_BOOLEAN }, + { .name = "signing_key", .type = OPTION_BOOLEAN }, + { .name = "encryption_key", .type = OPTION_BOOLEAN }, + { .name = "cert_signing_key", .type = OPTION_BOOLEAN }, + { .name = "crl_signing_key", .type = OPTION_BOOLEAN }, + { .name = "code_signing_key", .type = OPTION_BOOLEAN }, + { .name = "ocsp_signing_key", .type = OPTION_BOOLEAN }, + { .name = "time_stamping_key", .type = OPTION_BOOLEAN }, + { .name = "email_protection_key", .type = OPTION_BOOLEAN }, + { .name = "ipsec_ike_key", .type = OPTION_BOOLEAN }, + { .name = "key_agreement", .type = OPTION_BOOLEAN }, + { .name = "data_encipherment", .type = OPTION_BOOLEAN }, + { .name = "non_repudiation", .type = OPTION_BOOLEAN }, + { .name = "tls_feature", .type = OPTION_MULTI_LINE }, +}; + +typedef struct _cfg_ctx { + char **organization; + char **unit; + char *locality; + char *state; + char *dn; + char *cn; + char *uid; + uint8_t *subject_unique_id; + unsigned subject_unique_id_size; + uint8_t *issuer_unique_id; + unsigned issuer_unique_id_size; + char *challenge_password; + char *pkcs9_email; + char *country; + char *policy_oid[MAX_POLICIES]; + char *policy_txt[MAX_POLICIES]; + char *policy_url[MAX_POLICIES]; + char **dc; + char **dns_name; + char **uri; + char **ip_addr; + char **email; + char **krb5_principal; + char **other_name; + char **other_name_utf8; + char **other_name_octet; + char **xmpp_name; + char **dn_oid; + char **extensions; + char **crit_extensions; + char **permitted_nc_ip; + char **excluded_nc_ip; + char **permitted_nc_dns; + char **excluded_nc_dns; + char **permitted_nc_email; + char **excluded_nc_email; + char **crl_dist_points; + char *password; + char *pkcs12_key_name; + char *expiration_date; + char *activation_date; + char *revocation_date; + char *this_update_date; + char *next_update_date; + uint8_t *serial; + unsigned serial_size; + int expiration_days; + int skip_certs; /* from inhibit anypolicy */ + int ca; + int path_len; + int tls_www_client; + int tls_www_server; + int signing_key; + int encryption_key; + int cert_sign_key; + int crl_sign_key; + int non_repudiation; + int data_encipherment; + int key_agreement; + int code_sign_key; + int ocsp_sign_key; + int time_stamping_key; + int email_protection_key; + int ipsec_ike_key; + char **key_purpose_oids; + int crl_next_update; + uint8_t *crl_number; + unsigned crl_number_size; + int honor_crq_extensions; + char *proxy_policy_language; + char **exts_to_honor; + char **ocsp_uris; + char **ca_issuers_uris; + char **tls_features; +} cfg_ctx; + +cfg_ctx cfg; + +void cfg_init(void) +{ + memset(&cfg, 0, sizeof(cfg)); + cfg.path_len = -1; + cfg.skip_certs = -1; +} + +#define READ_MULTI_LINE(k_name, s_name) \ + val = cfg_next(pov, k_name); \ + if (val != NULL) \ + { \ + if (s_name == NULL) { \ + i = 0; \ + s_name = malloc(sizeof(char*)*MAX_ENTRIES); \ + CHECK_MALLOC(s_name); \ + do { \ + if (val && strcmp(val->name, k_name)!=0) \ + continue; \ + s_name[i] = strdup(val->value); \ + i++; \ + if (i>=MAX_ENTRIES) \ + break; \ + } while((val = cfg_next(val + 1, val->name)) != NULL); \ + s_name[i] = NULL; \ + } \ + } + +#define READ_MULTI_LINE_TOKENIZED(k_name, s_name) \ + val = cfg_next(pov, k_name); \ + if (val != NULL) \ + { \ + char *str; \ + char *p; \ + if (s_name == NULL) { \ + i = 0; \ + s_name = malloc(sizeof(char*)*MAX_ENTRIES); \ + CHECK_MALLOC(s_name); \ + do { \ + if (val && strcmp(val->name, k_name)!=0) \ + continue; \ + str = strdup(val->value); \ + CHECK_MALLOC(str); \ + if ((p=strchr(str, ' ')) == NULL && (p=strchr(str, '\t')) == NULL) { \ + fprintf(stderr, "Error parsing %s\n", k_name); \ + exit(1); \ + } \ + p[0] = 0; \ + p++; \ + s_name[i] = strdup(str); \ + while(*p==' ' || *p == '\t') p++; \ + if (p[0] == 0) { \ + fprintf(stderr, "Error (2) parsing %s\n", k_name); \ + exit(1); \ + } \ + s_name[i+1] = strdup(p); \ + i+=2; \ + free(str); \ + if (i>=MAX_ENTRIES) \ + break; \ + } while((val = cfg_next(val + 1, val->name)) != NULL); \ + s_name[i] = NULL; \ + } \ + } + +#define READ_BOOLEAN(name, s_name) \ + val = cfg_next(pov, name); \ + if (val != NULL) \ + { \ + s_name = 1; \ + } + +/* READ_NUMERIC only returns a long */ +#define READ_NUMERIC(name, s_name) \ + val = cfg_next(pov, name); \ + if (val != NULL) \ + { \ + s_name = strtol(val->value, NULL, 10); \ + } + +#define HEX_DECODE(hex, output, output_size) \ + { \ + gnutls_datum_t _input = {(void*)hex, strlen(hex)}; \ + gnutls_datum_t _output; \ + ret = gnutls_hex_decode2(&_input, &_output); \ + if (ret < 0) { \ + fprintf(stderr, "error in hex ID: %s\n", hex); \ + exit(1); \ + } \ + output = _output.data; \ + output_size = _output.size; \ + } + +#define SERIAL_DECODE(input, output, output_size) \ + { \ + gnutls_datum_t _output; \ + ret = serial_decode(input, &_output); \ + if (ret < 0) { \ + fprintf(stderr, "error parsing number: %s\n", input); \ + exit(1); \ + } \ + output = _output.data; \ + output_size = _output.size; \ + } + + +static int handle_option(cfg_option_t val) +{ + unsigned j; + unsigned len, cmp; + + for (j=0;j<sizeof(available_options)/sizeof(available_options[0]);j++) { + len = strlen(available_options[j].name); + if (len > 2 && available_options[j].name[len-1] == '*') + cmp = strncasecmp(val->name, available_options[j].name, len-1); + else + cmp = strcasecmp(val->name, available_options[j].name); + + if (cmp == 0) { + if (available_options[j].type != OPTION_MULTI_LINE && + available_options[j].found != 0) { + fprintf(stderr, "Warning: multiple options found for '%s'; only the first will be taken into account.\n", available_options[j].name); + } + available_options[j].found = 1; + return 1; + } + } + + return 0; +} + +int template_parse(const char *template) +{ + /* Parsing return code */ + unsigned int i; + int ret; + cfg_option_t pov; + cfg_option_t val; + char tmpstr[256]; + + pov = cfg_load(template); + if (pov == NULL) { + perror("configFileLoad"); + fprintf(stderr, "Error loading template: %s\n", template); + exit(1); + } + + for (val = pov; val->name; val++) { + if (handle_option(val) == 0) { + fprintf(stderr, "Warning: skipping unknown option '%s'\n", val->name); + } + } + + /* Option variables */ + READ_MULTI_LINE("unit", cfg.unit); + if (cfg.unit == NULL) { + READ_MULTI_LINE("ou", cfg.unit); + } + + READ_MULTI_LINE("organization", cfg.organization); + if (cfg.organization == NULL) { + READ_MULTI_LINE("o", cfg.organization); + } + + val = cfg_next(pov, "locality"); + if (val != NULL) + cfg.locality = strdup(val->value); + + val = cfg_next(pov, "state"); + if (val != NULL) + cfg.state = strdup(val->value); + + val = cfg_next(pov, "dn"); + if (val != NULL) + cfg.dn = strdup(val->value); + + val = cfg_next(pov, "cn"); + if (val != NULL) + cfg.cn = strdup(val->value); + + val = cfg_next(pov, "uid"); + if (val != NULL) + cfg.uid = strdup(val->value); + + val = cfg_next(pov, "issuer_unique_id"); + if (val != NULL) + HEX_DECODE(val->value, cfg.issuer_unique_id, cfg.issuer_unique_id_size); + + val = cfg_next(pov, "subject_unique_id"); + if (val != NULL) + HEX_DECODE(val->value, cfg.subject_unique_id, cfg.subject_unique_id_size); + + val = cfg_next(pov, "challenge_password"); + if (val != NULL) + cfg.challenge_password = strdup(val->value); + + val = cfg_next(pov, "password"); + if (val != NULL) + cfg.password = strdup(val->value); + + val = cfg_next(pov, "pkcs9_email"); + if (val != NULL) + cfg.pkcs9_email = strdup(val->value); + + val = cfg_next(pov, "country"); + if (val != NULL) + cfg.country = strdup(val->value); + + val = cfg_next(pov, "expiration_date"); + if (val != NULL) + cfg.expiration_date = strdup(val->value); + + val = cfg_next(pov, "activation_date"); + if (val != NULL) + cfg.activation_date = strdup(val->value); + + val = cfg_next(pov, "crl_revocation_date"); + if (val != NULL) + cfg.revocation_date = strdup(val->value); + + val = cfg_next(pov, "crl_this_update_date"); + if (val != NULL) + cfg.this_update_date = strdup(val->value); + + val = cfg_next(pov, "crl_next_update_date"); + if (val != NULL) + cfg.next_update_date = strdup(val->value); + + READ_NUMERIC("inhibit_anypolicy_skip_certs", cfg.skip_certs); + + for (i = 0; i < MAX_POLICIES; i++) { + snprintf(tmpstr, sizeof(tmpstr), "policy%d", i + 1); + val = cfg_next(pov, tmpstr); + if (val != NULL) + cfg.policy_oid[i] = strdup(val->value); + + if (cfg.policy_oid[i] != NULL) { + snprintf(tmpstr, sizeof(tmpstr), "policy%d_url", + i + 1); + val = cfg_next(pov, tmpstr); + if (val != NULL) + cfg.policy_url[i] = strdup(val->value); + + snprintf(tmpstr, sizeof(tmpstr), "policy%d_txt", + i + 1); + val = cfg_next(pov, tmpstr); + if (val != NULL) { + cfg.policy_txt[i] = strdup(val->value); + } + } + } + + READ_MULTI_LINE("dc", cfg.dc); + READ_MULTI_LINE("dns_name", cfg.dns_name); + READ_MULTI_LINE("uri", cfg.uri); + READ_MULTI_LINE("krb5_principal", cfg.krb5_principal); + READ_MULTI_LINE_TOKENIZED("other_name", cfg.other_name); + READ_MULTI_LINE_TOKENIZED("other_name_octet", cfg.other_name_octet); + READ_MULTI_LINE_TOKENIZED("other_name_utf8", cfg.other_name_utf8); + + READ_MULTI_LINE("xmpp_name", cfg.xmpp_name); + READ_MULTI_LINE("ip_address", cfg.ip_addr); + READ_MULTI_LINE("email", cfg.email); + READ_MULTI_LINE("key_purpose_oid", cfg.key_purpose_oids); + + READ_MULTI_LINE("nc_exclude_ip", cfg.excluded_nc_ip); + READ_MULTI_LINE("nc_exclude_dns", cfg.excluded_nc_dns); + READ_MULTI_LINE("nc_exclude_email", cfg.excluded_nc_email); + READ_MULTI_LINE("nc_permit_ip", cfg.permitted_nc_ip); + READ_MULTI_LINE("nc_permit_dns", cfg.permitted_nc_dns); + READ_MULTI_LINE("nc_permit_email", cfg.permitted_nc_email); + + READ_MULTI_LINE_TOKENIZED("dn_oid", cfg.dn_oid); + + READ_MULTI_LINE_TOKENIZED("add_extension", cfg.extensions); + READ_MULTI_LINE_TOKENIZED("add_critical_extension", cfg.crit_extensions); + + READ_MULTI_LINE("crl_dist_points", cfg.crl_dist_points); + + val = cfg_next(pov, "pkcs12_key_name"); + if (val != NULL) + cfg.pkcs12_key_name = strdup(val->value); + + + val = cfg_next(pov, "serial"); + if (val != NULL) + SERIAL_DECODE(val->value, cfg.serial, cfg.serial_size); + + READ_NUMERIC("expiration_days", cfg.expiration_days); + READ_NUMERIC("crl_next_update", cfg.crl_next_update); + + val = cfg_next(pov, "crl_number"); + if (val != NULL) + SERIAL_DECODE(val->value, cfg.crl_number, cfg.crl_number_size); + + READ_NUMERIC("path_len", cfg.path_len); + + val = cfg_next(pov, "proxy_policy_language"); + if (val != NULL) + cfg.proxy_policy_language = strdup(val->value); + + READ_MULTI_LINE("ocsp_uri", cfg.ocsp_uris); + READ_MULTI_LINE("ca_issuers_uri", cfg.ca_issuers_uris); + + READ_BOOLEAN("ca", cfg.ca); + READ_BOOLEAN("honor_crq_extensions", cfg.honor_crq_extensions); + READ_MULTI_LINE("honor_crq_ext", cfg.exts_to_honor); + + READ_BOOLEAN("tls_www_client", cfg.tls_www_client); + READ_BOOLEAN("tls_www_server", cfg.tls_www_server); + READ_BOOLEAN("signing_key", cfg.signing_key); + READ_BOOLEAN("encryption_key", cfg.encryption_key); + READ_BOOLEAN("cert_signing_key", cfg.cert_sign_key); + READ_BOOLEAN("crl_signing_key", cfg.crl_sign_key); + READ_BOOLEAN("code_signing_key", cfg.code_sign_key); + READ_BOOLEAN("ocsp_signing_key", cfg.ocsp_sign_key); + READ_BOOLEAN("time_stamping_key", cfg.time_stamping_key); + READ_BOOLEAN("email_protection_key", cfg.email_protection_key); + READ_BOOLEAN("ipsec_ike_key", cfg.ipsec_ike_key); + + READ_BOOLEAN("data_encipherment", cfg.data_encipherment); + READ_BOOLEAN("key_agreement", cfg.key_agreement); + READ_BOOLEAN("non_repudiation", cfg.non_repudiation); + + READ_MULTI_LINE("tls_feature", cfg.tls_features); + + cfg_free(pov); + + return 0; +} + +#define IS_NEWLINE(x) ((x[0] == '\n') || (x[0] == '\r')) +#define MAX_INPUT_SIZE 512 + +static size_t strip_nl(char *str, size_t str_size) +{ + if ((str_size > 0) && (str[str_size - 1] == '\n')) { + str_size--; + str[str_size] = 0; + } + if ((str_size > 0) && (str[str_size - 1] == '\r')) { + str_size--; + str[str_size] = 0; + } + return str_size; +} + +static int copystr_without_nl(char *out, size_t out_size, const char *in, size_t in_size) +{ + if (in_size+1 >= out_size) { + fprintf(stderr, "Too long line to parse in interactive mode; please use templates.\n"); + exit(1); + } + memcpy(out, in, in_size+1); /* copy terminating null */ + strip_nl(out, in_size); + return 0; +} + +void +read_crt_set(gnutls_x509_crt_t crt, const char *input_str, const char *oid) +{ + ssize_t ret; + char *lineptr = NULL; + size_t linesize = 0; + + fputs(input_str, stderr); + ret = getline(&lineptr, &linesize, stdin); + if (ret == -1) + return; + + if (IS_NEWLINE(lineptr)) { + free(lineptr); + return; + } + + linesize = strip_nl(lineptr, ret); + + ret = + gnutls_x509_crt_set_dn_by_oid(crt, oid, 0, lineptr, + linesize); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", gnutls_strerror(ret)); + exit(1); + } + free(lineptr); +} + +void +read_crq_set(gnutls_x509_crq_t crq, const char *input_str, const char *oid) +{ + ssize_t ret; + char *lineptr = NULL; + size_t linesize = 0; + + fputs(input_str, stderr); + ret = getline(&lineptr, &linesize, stdin); + if (ret == -1) + return; + + if (IS_NEWLINE(lineptr)) { + free(lineptr); + return; + } + + linesize = strip_nl(lineptr, ret); + + ret = + gnutls_x509_crq_set_dn_by_oid(crq, oid, 0, lineptr, + linesize); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", gnutls_strerror(ret)); + exit(1); + } + free(lineptr); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +/* The input_str should contain %d or %u to print the default. + */ +static int64_t read_int_with_default(const char *input_str, long def) +{ + char *endptr; + int64_t l; + static char input[MAX_INPUT_SIZE]; + + fprintf(stderr, input_str, def); + if (fgets(input, sizeof(input), stdin) == NULL) + return def; + + if (IS_NEWLINE(input)) + return def; + +#if SIZEOF_LONG < 8 + l = strtoll(input, &endptr, 0); + + if (*endptr != '\0' && *endptr != '\r' && *endptr != '\n') { + fprintf(stderr, "Trailing garbage ignored: `%s'\n", + endptr); + return 0; + } else { + *endptr = 0; + } + + if (l <= LLONG_MIN || l >= LLONG_MAX) { + fprintf(stderr, "Integer out of range: `%s' (max: %llu)\n", input, LLONG_MAX-1); + return 0; + } +#else + l = strtol(input, &endptr, 0); + + if (*endptr != '\0' && *endptr != '\r' && *endptr != '\n') { + fprintf(stderr, "Trailing garbage ignored: `%s'\n", + endptr); + return 0; + } else { + *endptr = 0; + } + + if (l <= LONG_MIN || l >= LONG_MAX) { + fprintf(stderr, "Integer out of range: `%s' (max: %lu)\n", input, LONG_MAX-1); + return 0; + } +#endif + + + + if (input == endptr) + l = def; + + return l; +} +#pragma GCC diagnostic pop + +int64_t read_int(const char *input_str) +{ + return read_int_with_default(input_str, 0); +} + +int serial_decode(const char *input, gnutls_datum_t *output) +{ + int i; + int64_t value; + char *endptr; + int64_t value_limit; + gnutls_datum_t input_datum; + + if (input[0] == '0' && input[1] == 'x') { + input_datum.data = (void *) (input + 2); + input_datum.size = strlen(input + 2); + if (input_datum.size == 0) { + return GNUTLS_E_PARSING_ERROR; + } + return gnutls_hex_decode2(&input_datum, output); + } + +#if SIZEOF_LONG < 8 + value = strtol(input, &endptr, 10); + value_limit = LONG_MAX; +#else + value = strtoll(input, &endptr, 10); + value_limit = LLONG_MAX; +#endif + + if (*endptr != '\0') { + fprintf(stderr, "Trailing garbage: `%s'\n", endptr); + return GNUTLS_E_PARSING_ERROR; + } + + if (value <= 0 || value >= value_limit) { + fprintf(stderr, "Integer out of range: `%s' (min: 1, max: %"PRId64")\n", input, value_limit-1); + return GNUTLS_E_PARSING_ERROR; + } + + output->size = sizeof(int64_t); + output->data = gnutls_malloc(output->size); + if (output->data == NULL) { + output->size = 0; + return GNUTLS_E_MEMORY_ERROR; + } + + for (i = output->size - 1; i >= 0; i--) { + output->data[i] = value & 0xff; + value = value >> 8; + } + + return 0; +} + +const char *read_str(const char *input_str) +{ + static char input[MAX_INPUT_SIZE]; + ssize_t ret; + char *lineptr = NULL; + size_t linesize = 0; + + fputs(input_str, stderr); + ret = getline(&lineptr, &linesize, stdin); + if (ret == -1) + return NULL; + + ret = copystr_without_nl(input, sizeof(input), lineptr, ret); + free(lineptr); + if (ret < 0) + return NULL; + + if (IS_NEWLINE(input)) + return NULL; + + if (input[0] == 0) + return NULL; + + return input; +} + +/* Default is: + * def: 0 -> no + * def: 1 -> yes + */ +int read_yesno(const char *input_str, int def) +{ + char input[MAX_INPUT_SIZE]; + + restart: + fputs(input_str, stderr); + if (fgets(input, sizeof(input), stdin) == NULL) + return def; + + if (IS_NEWLINE(input)) + return def; + + if (input[0] == 'y' || input[0] == 'Y') + return 1; + else if (input[0] == 'n' || input[0] == 'N') + return 0; + else + goto restart; +} + + +/* Wrapper functions for non-interactive mode. + */ +const char *get_pass(void) +{ + if (batch && !ask_pass) + return cfg.password; + else + return getpass("Enter password: "); +} + +const char *get_confirmed_pass(bool empty_ok) +{ + if (batch && !ask_pass) + return cfg.password; + else { + const char *pass = NULL; + char *copy = NULL; + + do { + if (pass) + fprintf(stderr, + "Password mismatch, try again.\n"); + + free(copy); + + pass = getpass("Enter password: "); + copy = strdup(pass); + CHECK_MALLOC(copy); + pass = getpass("Confirm password: "); + } + while (strcmp(pass, copy) != 0 + && !(empty_ok && *pass == '\0')); + + free(copy); + + return pass; + } +} + +const char *get_challenge_pass(void) +{ + if (batch && !ask_pass) + return cfg.challenge_password; + else + return getpass("Enter a challenge password: "); +} + +void get_crl_dist_point_set(gnutls_x509_crt_t crt) +{ + int ret = 0, i; + + if (batch) { + if (!cfg.crl_dist_points) + return; + + for (i = 0; cfg.crl_dist_points[i] != NULL; i++) { + ret = + gnutls_x509_crt_set_crl_dist_points + (crt, GNUTLS_SAN_URI, cfg.crl_dist_points[i], + 0); + if (ret < 0) + break; + } + } else { + const char *p; + unsigned int counter = 0; + + do { + if (counter == 0) { + p = read_str + ("Enter the URI of the CRL distribution point: "); + } else { + p = read_str + ("Enter an additional URI of the CRL distribution point: "); + } + if (!p) + return; + + ret = gnutls_x509_crt_set_crl_dist_points + (crt, GNUTLS_SAN_URI, p, 0); + if (ret < 0) + break; + + counter++; + } + while (p); + } + + if (ret < 0) { + fprintf(stderr, "gnutls_x509_crt_set_crl_dist_points: %s\n", + gnutls_strerror(ret)); + exit(1); + } +} + +void get_country_crt_set(gnutls_x509_crt_t crt) +{ + int ret; + + if (batch) { + if (!cfg.country) + return; + ret = + gnutls_x509_crt_set_dn_by_oid(crt, + GNUTLS_OID_X520_COUNTRY_NAME, + 0, cfg.country, + strlen(cfg.country)); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + read_crt_set(crt, "Country name (2 chars): ", + GNUTLS_OID_X520_COUNTRY_NAME); + } + +} + +void get_organization_crt_set(gnutls_x509_crt_t crt) +{ + int ret; + unsigned i; + + if (batch) { + if (!cfg.organization) + return; + + for (i = 0; cfg.organization[i] != NULL; i++) { + ret = + gnutls_x509_crt_set_dn_by_oid(crt, + GNUTLS_OID_X520_ORGANIZATION_NAME, + 0, cfg.organization[i], + strlen(cfg.organization[i])); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } + } else { + read_crt_set(crt, "Organization name: ", + GNUTLS_OID_X520_ORGANIZATION_NAME); + } + +} + +void get_unit_crt_set(gnutls_x509_crt_t crt) +{ + int ret; + unsigned i; + + if (batch) { + if (!cfg.unit) + return; + + for (i = 0; cfg.unit[i] != NULL; i++) { + ret = + gnutls_x509_crt_set_dn_by_oid(crt, + GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, + 0, cfg.unit[i], + strlen(cfg.unit[i])); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } + } else { + read_crt_set(crt, "Organizational unit name: ", + GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME); + } + +} + +void get_state_crt_set(gnutls_x509_crt_t crt) +{ + int ret; + + if (batch) { + if (!cfg.state) + return; + ret = + gnutls_x509_crt_set_dn_by_oid(crt, + GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, + 0, cfg.state, + strlen(cfg.state)); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + read_crt_set(crt, "State or province name: ", + GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME); + } + +} + +void get_locality_crt_set(gnutls_x509_crt_t crt) +{ + int ret; + + if (batch) { + if (!cfg.locality) + return; + ret = + gnutls_x509_crt_set_dn_by_oid(crt, + GNUTLS_OID_X520_LOCALITY_NAME, + 0, cfg.locality, + strlen(cfg.locality)); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + read_crt_set(crt, "Locality name: ", + GNUTLS_OID_X520_LOCALITY_NAME); + } + +} + +void get_cn_crt_set(gnutls_x509_crt_t crt) +{ + int ret; + + if (batch) { + if (!cfg.cn) + return; + ret = + gnutls_x509_crt_set_dn_by_oid(crt, + GNUTLS_OID_X520_COMMON_NAME, + 0, cfg.cn, + strlen(cfg.cn)); + if (ret < 0) { + fprintf(stderr, "set_dn_by_oid: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + read_crt_set(crt, "Common name: ", + GNUTLS_OID_X520_COMMON_NAME); + } + +} + +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 crt_constraints_set(gnutls_x509_crt_t crt) +{ + int ret; + unsigned i; + gnutls_x509_name_constraints_t nc; + gnutls_datum_t name; + + if (batch) { + if (cfg.permitted_nc_dns == NULL && cfg.permitted_nc_email == NULL && + cfg.excluded_nc_dns == NULL && cfg.excluded_nc_email == NULL && + cfg.permitted_nc_ip == NULL && cfg.excluded_nc_ip == NULL) + return; /* nothing to do */ + + ret = gnutls_x509_name_constraints_init(&nc); + if (ret < 0) { + fprintf(stderr, "nc_init: %s\n", gnutls_strerror(ret)); + exit(1); + } + + if (cfg.permitted_nc_ip) { + for (i = 0; cfg.permitted_nc_ip[i] != NULL; i++) { + ret = gnutls_x509_cidr_to_rfc5280(cfg.permitted_nc_ip[i], &name); + if (ret < 0) { + fprintf(stderr, "error parsing IP constraint: %s\n", gnutls_strerror(ret)); + exit(1); + } + ret = gnutls_x509_name_constraints_add_permitted(nc, GNUTLS_SAN_IPADDRESS, &name); + if (ret < 0) { + fprintf(stderr, "error adding constraint: %s\n", gnutls_strerror(ret)); + exit(1); + } + free(name.data); + } + } + + if (cfg.excluded_nc_ip) { + for (i = 0; cfg.excluded_nc_ip[i] != NULL; i++) { + ret = gnutls_x509_cidr_to_rfc5280(cfg.excluded_nc_ip[i], &name); + if (ret < 0) { + fprintf(stderr, "error parsing IP constraint: %s\n", gnutls_strerror(ret)); + exit(1); + } + ret = gnutls_x509_name_constraints_add_excluded(nc, GNUTLS_SAN_IPADDRESS, &name); + if (ret < 0) { + fprintf(stderr, "error adding constraint: %s\n", gnutls_strerror(ret)); + exit(1); + } + free(name.data); + } + } + + if (cfg.permitted_nc_dns) { + + for (i = 0; cfg.permitted_nc_dns[i] != NULL; i++) { + + name.data = (void*)cfg.permitted_nc_dns[i]; + name.size = strlen((char*)name.data); + ret = gnutls_x509_name_constraints_add_permitted(nc, GNUTLS_SAN_DNSNAME, &name); + if (ret < 0) { + fprintf(stderr, "error adding constraint: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + } + + + if (cfg.excluded_nc_dns) { + for (i = 0; cfg.excluded_nc_dns[i] != NULL; i++) { + name.data = (void*)cfg.excluded_nc_dns[i]; + name.size = strlen((char*)name.data); + ret = gnutls_x509_name_constraints_add_excluded(nc, GNUTLS_SAN_DNSNAME, &name); + if (ret < 0) { + fprintf(stderr, "error adding constraint: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + } + + if (cfg.permitted_nc_email) { + for (i = 0; cfg.permitted_nc_email[i] != NULL; i++) { + name.data = (void*)cfg.permitted_nc_email[i]; + name.size = strlen((char*)name.data); + ret = gnutls_x509_name_constraints_add_permitted(nc, GNUTLS_SAN_RFC822NAME, &name); + if (ret < 0) { + fprintf(stderr, "error adding constraint: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + } + + if (cfg.excluded_nc_email) { + for (i = 0; cfg.excluded_nc_email[i] != NULL; i++) { + name.data = (void*)cfg.excluded_nc_email[i]; + name.size = strlen((char*)name.data); + ret = gnutls_x509_name_constraints_add_excluded(nc, GNUTLS_SAN_RFC822NAME, &name); + if (ret < 0) { + fprintf(stderr, "error adding constraint: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + } + + ret = gnutls_x509_crt_set_name_constraints(crt, nc, 1); + if (ret < 0) { + fprintf(stderr, "error setting constraints: %s\n", gnutls_strerror(ret)); + exit(1); + } + + gnutls_x509_name_constraints_deinit(nc); + } +} + +void crt_unique_ids_set(gnutls_x509_crt_t crt) +{ + int ret; + + if (batch) { + if (cfg.subject_unique_id == NULL && cfg.issuer_unique_id == NULL) + return; /* nothing to do */ + + if (cfg.subject_unique_id) { + ret = gnutls_x509_crt_set_subject_unique_id(crt, cfg.subject_unique_id, cfg.subject_unique_id_size); + if (ret < 0) { + fprintf(stderr, "error setting subject unique ID: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + + if (cfg.issuer_unique_id) { + ret = gnutls_x509_crt_set_issuer_unique_id(crt, cfg.issuer_unique_id, cfg.issuer_unique_id_size); + if (ret < 0) { + fprintf(stderr, "error setting issuer unique ID: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + } +} + +void get_uid_crt_set(gnutls_x509_crt_t crt) +{ + int ret; + + if (batch) { + if (!cfg.uid) + return; + ret = + gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_LDAP_UID, + 0, cfg.uid, + strlen(cfg.uid)); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + read_crt_set(crt, "UID: ", GNUTLS_OID_LDAP_UID); + } + +} + +void get_oid_crt_set(gnutls_x509_crt_t crt) +{ + int ret, i; + + if (batch) { + if (!cfg.dn_oid) + return; + for (i = 0; cfg.dn_oid[i] != NULL; i += 2) { + if (cfg.dn_oid[i + 1] == NULL) { + fprintf(stderr, + "dn_oid: %s does not have an argument.\n", + cfg.dn_oid[i]); + exit(1); + } + ret = + gnutls_x509_crt_set_dn_by_oid(crt, + cfg.dn_oid[i], 0, + cfg.dn_oid[i + + 1], + strlen(cfg. + dn_oid[i + + 1])); + + if (ret < 0) { + fprintf(stderr, "set_dn_oid: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } + } +} + +#define ACTION_NONE 0 +#define ENCODE_OCTET_STRING 1 +static unsigned char *decode_ext_string(char *str, unsigned int *ret_size) +{ + char *p, *p2; + unsigned char *tmp; + unsigned char *raw; + unsigned int raw_size; + unsigned action = ACTION_NONE; + unsigned char tag[ASN1_MAX_TL_SIZE]; + unsigned int tag_len; + int ret, res; + + p = strchr(str, '('); + if (p != 0) { + if (strncmp(str, "octet_string", 12) == 0) { + action = ENCODE_OCTET_STRING; + } else { + fprintf(stderr, "cannot parse: %s\n", str); + exit(1); + } + p++; + p2 = strchr(p, ')'); + if (p2 == NULL) { + fprintf(stderr, "there is no terminating parenthesis in: %s\n", str); + exit(1); + } + *p2 = 0; + } else { + p = str; + } + + if (strncmp(p, "0x", 2) == 0) + p+=2; + HEX_DECODE(p, raw, raw_size); + + switch(action) { + case ENCODE_OCTET_STRING: + tag_len = sizeof(tag); + res = asn1_encode_simple_der(ASN1_ETYPE_OCTET_STRING, raw, raw_size, tag, &tag_len); + if (res != ASN1_SUCCESS) { + fprintf(stderr, "error in DER encoding: %s\n", asn1_strerror(res)); + exit(1); + } + tmp = gnutls_malloc(raw_size+tag_len); + if (tmp == NULL) { + fprintf(stderr, "error in allocation\n"); + exit(1); + } + memcpy(tmp, tag, tag_len); + memcpy(tmp+tag_len, raw, raw_size); + gnutls_free(raw); + raw = tmp; + raw_size += tag_len; + break; + } + + *ret_size = raw_size; + return raw; +} + +void get_extensions_crt_set(int type, void *crt) +{ + int ret, i; + unsigned char *raw = NULL; + unsigned raw_size; + + if (batch) { + if (!cfg.extensions) + goto check_critical; + for (i = 0; cfg.extensions[i] != NULL; i += 2) { + if (cfg.extensions[i + 1] == NULL) { + fprintf(stderr, + "extensions: %s does not have an argument.\n", + cfg.extensions[i]); + exit(1); + } + + /* convert hex to bin */ + raw = decode_ext_string(cfg.extensions[i+1], &raw_size); + + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_extension_by_oid(crt, + cfg.extensions[i], + raw, raw_size, 0); + else + ret = + gnutls_x509_crq_set_extension_by_oid(crt, + cfg.extensions[i], + raw, raw_size, 0); + + gnutls_free(raw); + if (ret < 0) { + fprintf(stderr, "set_extensions: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } + + check_critical: + if (!cfg.crit_extensions) + return; + for (i = 0; cfg.crit_extensions[i] != NULL; i += 2) { + if (cfg.crit_extensions[i + 1] == NULL) { + fprintf(stderr, + "extensions: %s does not have an argument.\n", + cfg.crit_extensions[i]); + exit(1); + } + /* convert hex to bin */ + raw = decode_ext_string(cfg.crit_extensions[i+1], &raw_size); + + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_extension_by_oid(crt, + cfg.crit_extensions[i], + raw, raw_size, 1); + else + ret = + gnutls_x509_crq_set_extension_by_oid(crt, + cfg.crit_extensions[i], + raw, raw_size, 1); + + gnutls_free(raw); + + if (ret < 0) { + fprintf(stderr, "set_extensions: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } + } +} + +void get_key_purpose_set(int type, void *crt) +{ + int ret, i; + + if (batch) { + if (!cfg.key_purpose_oids) + return; + for (i = 0; cfg.key_purpose_oids[i] != NULL; i++) { + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_key_purpose_oid + (crt, cfg.key_purpose_oids[i], 0); + else + ret = + gnutls_x509_crq_set_key_purpose_oid + (crt, cfg.key_purpose_oids[i], 0); + + if (ret < 0) { + fprintf(stderr, + "set_key_purpose_oid (%s): %s\n", + cfg.key_purpose_oids[i], + gnutls_strerror(ret)); + exit(1); + } + } + } +} + +void get_ocsp_issuer_set(gnutls_x509_crt_t crt) +{ + int ret, i; + gnutls_datum_t uri; + + if (batch) { + if (!cfg.ocsp_uris) + return; + for (i = 0; cfg.ocsp_uris[i] != NULL; i++) { + uri.data = (void*)cfg.ocsp_uris[i]; + uri.size = strlen(cfg.ocsp_uris[i]); + ret = + gnutls_x509_crt_set_authority_info_access(crt, + GNUTLS_IA_OCSP_URI, + &uri); + if (ret < 0) { + fprintf(stderr, "set OCSP URI (%s): %s\n", + cfg.ocsp_uris[i], + gnutls_strerror(ret)); + exit(1); + } + } + } +} + +void get_ca_issuers_set(gnutls_x509_crt_t crt) +{ + int ret, i; + gnutls_datum_t uri; + + if (batch) { + if (!cfg.ca_issuers_uris) + return; + for (i = 0; cfg.ca_issuers_uris[i] != NULL; i++) { + uri.data = (void*)cfg.ca_issuers_uris[i]; + uri.size = strlen(cfg.ca_issuers_uris[i]); + ret = + gnutls_x509_crt_set_authority_info_access(crt, + GNUTLS_IA_CAISSUERS_URI, + &uri); + if (ret < 0) { + fprintf(stderr, + "set CA ISSUERS URI (%s): %s\n", + cfg.ca_issuers_uris[i], + gnutls_strerror(ret)); + exit(1); + } + } + } +} + + +void get_pkcs9_email_crt_set(gnutls_x509_crt_t crt) +{ + int ret; + + if (batch) { + if (!cfg.pkcs9_email) + return; + ret = + gnutls_x509_crt_set_dn_by_oid(crt, + GNUTLS_OID_PKCS9_EMAIL, + 0, cfg.pkcs9_email, + strlen(cfg.pkcs9_email)); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + read_crt_set(crt, "E-mail: ", GNUTLS_OID_PKCS9_EMAIL); + } + +} + + +static +int default_crl_number(unsigned char* serial, size_t *size) +{ + struct timespec ts; + time_t tv_sec_tmp; + int i; + + /* default format: + * | 5 b | 4 b | 11b + * | secs | nsecs | rnd | + */ + gettime(&ts); + + if (*size < 20) { + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + tv_sec_tmp = ts.tv_sec; + for (i = 4; i >= 0; i--) { + serial[i] = tv_sec_tmp & 0xff; + tv_sec_tmp = tv_sec_tmp >> 8; + } + serial[5] = (ts.tv_nsec >> 24) & 0xff; + serial[6] = (ts.tv_nsec >> 16) & 0xff; + serial[7] = (ts.tv_nsec >> 8) & 0xff; + serial[8] = (ts.tv_nsec) & 0xff; + // crl number must be positive and max 20 octets + // so we must zero the most significant bit (with MSB set, the DER encoding + // would be 21 octets long). See RFC 5280, section 5.2.3. + serial[0] &= 0x7F; + *size = 20; + return gnutls_rnd(GNUTLS_RND_NONCE, &serial[9], 11); +} + +/** + * read_serial_value: + * @serial: pointer to buffer with serial number + * @size: pointer to actual size of data in buffer + * @max_size: capacity of the buffer + * @label: user-facing description of the field we are reading value for + * @rfc_section: rfc section number that defines the field + * + * This function will read a serial number from the user. It takes a buffer + * that contains a default value that will be displayed to the user and + * maximum size of the buffer that it can fill. When the function + * returns, either the buffer is not modified to use the default value + * or its contents are changed to reflect the user-entered value. + **/ +static +void read_serial_value(unsigned char *serial, size_t *size, size_t max_size, + const char *label, const char *rfc_section) +{ + static char input[MAX_INPUT_SIZE]; + int ret; + size_t input_len; + gnutls_datum_t decoded; + gnutls_datum_t serial_datum; + gnutls_datum_t encoded_default; + + serial_datum.data = serial; + serial_datum.size = *size; + + ret = gnutls_hex_encode2(&serial_datum, &encoded_default); + if (ret < 0) { + fprintf(stderr, "error encoding default to hex: %d\n", ret); + exit(1); + } + + while (true) { + fprintf(stderr, + "Enter the %s in decimal (123) or hex (0xabcd)\n" + "(default is 0x%s)\n" + "value: ", + label, encoded_default.data); + + if (fgets(input, sizeof(input), stdin) == NULL) + break; + + input_len = strip_nl(input, strlen(input)); + + if (input_len == 0) + break; + + ret = serial_decode(input, &decoded); + if (ret < 0) { + fprintf(stderr, "error parsing %s: %s\n", label, input); + continue; + } + + if ((decoded.size == SERIAL_MAX_BYTES && decoded.data[0] & 0x80) || + decoded.size > SERIAL_MAX_BYTES) { + fprintf(stderr, "%s would be encoded in more than 20 bytes," + "see RFC 5280, section %s\n", label, rfc_section); + gnutls_free(decoded.data); + continue; + } + + if (decoded.size > max_size) { + fprintf(stderr, "maximum %zu octets allowed for %s\n", + max_size, label); + gnutls_free(decoded.data); + continue; + } + + memcpy(serial, decoded.data, decoded.size); + *size = decoded.size; + gnutls_free(decoded.data); + break; + } + + gnutls_free(encoded_default.data); +} + +static +void get_serial_value(unsigned char *serial, size_t *size, + const unsigned char *config, size_t config_size, + int (create_default)(unsigned char *, size_t *), + const char *label, const char *rfc_section) +{ + size_t max_size = *size; + int ret; + + if (batch && config != NULL) { + if (config_size > max_size) { + fprintf(stderr, "maximum %zu octets allowed for %s!\n", + max_size, label); + exit(1); + } + memcpy(serial, config, config_size); + *size = config_size; + } else { + ret = create_default(serial, size); + if (ret < 0) { + fprintf(stderr, "error generating default %s: %s\n", + label, gnutls_strerror(ret)); + exit(1); + } + } + + if (!batch) + read_serial_value(serial, size, max_size, label, rfc_section); + + if ((*size == SERIAL_MAX_BYTES && serial[0] & 0x80) || *size > SERIAL_MAX_BYTES) { + fprintf(stderr, "%s would be encoded in more than 20 bytes," + "see RFC 5280, section %s\n", label, rfc_section); + exit(1); + } +} + +static +int default_serial(unsigned char *serial, size_t *size) +{ + int ret; + + if (*size < SERIAL_MAX_BYTES) + return GNUTLS_E_SHORT_MEMORY_BUFFER; + + ret = gnutls_rnd(GNUTLS_RND_NONCE, serial, SERIAL_MAX_BYTES); + if (ret < 0) + return ret; + + // serial must be positive and max 20 octets + // so we must zero the most significant bit (with MSB set, the DER encoding + // would be 21 octets long). See RFC 5280, section 4.1.2.2. + serial[0] &= 0x7F; + *size = SERIAL_MAX_BYTES; + + return 0; +} + +void get_serial(unsigned char *serial, size_t *size) +{ + get_serial_value(serial, size, cfg.serial, cfg.serial_size, + default_serial, "certificate's serial number", "4.1.2.2"); +} + +static +time_t get_date(const char* date) +{ + struct timespec r; + + if (date==NULL || parse_datetime(&r, date, NULL) == 0) { + PRINT_TIME_T_ERROR; + fprintf(stderr, "Cannot parse date: %s\n", date); + exit(1); + } + + return r.tv_sec; +} + +time_t get_activation_date(void) +{ + + if (batch && cfg.activation_date != NULL) { + return get_date(cfg.activation_date); + } + + return time(NULL); +} + +time_t get_crl_revocation_date(void) +{ + + if (batch && cfg.revocation_date != NULL) { + return get_date(cfg.revocation_date); + } + + return time(NULL); +} + +time_t get_crl_this_update_date(void) +{ + + if (batch && cfg.this_update_date != NULL) { + return get_date(cfg.this_update_date); + } + + return time(NULL); +} + +static +time_t days_to_secs(int days) +{ +time_t secs = days; +time_t now = time(NULL); + + if (secs != (time_t)-1) { + if (INT_MULTIPLY_OVERFLOW(secs, 24*60*60)) { + goto overflow; + } else { + secs *= 24*60*60; + } + } + + if (secs != (time_t)-1) { + if (INT_ADD_OVERFLOW(secs, now)) { + goto overflow; + } else { + secs += now; + } + } + + return secs; + overflow: + PRINT_TIME_T_ERROR; + fprintf(stderr, "Overflow while parsing days\n"); + exit(1); +} + +static +time_t get_int_date(const char *txt_val, int int_val, const char *msg) +{ + if (batch) { + if (txt_val == NULL) { + time_t secs; + + if (int_val == 0 || int_val < -2) + secs = days_to_secs(365); + else { + secs = days_to_secs(int_val); + } + + return secs; + } else + return get_date(txt_val); + } else { + int days; + + do { + days = + read_int(msg); + } + while (days == 0); + return days_to_secs(days); + } +} + +time_t get_expiration_date(void) +{ + return get_int_date(cfg.expiration_date, cfg.expiration_days, "The certificate will expire in (days): "); +} + +int get_ca_status(void) +{ + if (batch) { + return cfg.ca; + } else { + return + read_yesno + ("Does the certificate belong to an authority? (y/N): ", + 0); + } +} + +int get_crq_extensions_status(void) +{ + if (batch) { + return cfg.honor_crq_extensions; + } else { + return + read_yesno + ("Do you want to honour all the extensions from the request? (y/N): ", + 0); + } +} + +void get_crl_number(unsigned char* serial, size_t * size) +{ + get_serial_value(serial, size, cfg.crl_number, cfg.crl_number_size, + default_crl_number, "CRL's serial number", "5.2.3"); +} + +int get_path_len(void) +{ + if (batch) { + return cfg.path_len; + } else { + return read_int_with_default + ("Path length constraint (decimal, %d for no constraint): ", + -1); + } +} + +const char *get_pkcs12_key_name(void) +{ + const char *name; + + if (batch) { + if (!cfg.pkcs12_key_name) + return "Anonymous"; + return cfg.pkcs12_key_name; + } else { + do { + name = read_str("Enter a name for the key: "); + } + while (name == NULL); + } + return name; +} + +int get_tls_client_status(void) +{ + if (batch) { + return cfg.tls_www_client; + } else { + return + read_yesno + ("Is this a TLS web client certificate? (y/N): ", 0); + } +} + +int get_tls_server_status(void) +{ + if (batch) { + return cfg.tls_www_server; + } else { + return + read_yesno + ("Is this a TLS web server certificate? (y/N): ", 0); + } +} + +/* convert a printable IP to binary */ +static int string_to_ip(unsigned char *ip, const char *str) +{ + int ret; + +#if HAVE_IPV6 + int len = strlen(str); + + if (strchr(str, ':') != NULL || len > 16) { /* IPv6 */ + ret = inet_pton(AF_INET6, str, ip); + if (ret <= 0) { + fprintf(stderr, "Error in IPv6 address %s\n", str); + exit(1); + } + + /* To be done */ + return 16; + } else +#endif + { /* IPv4 */ + ret = inet_pton(AF_INET, str, ip); + if (ret <= 0) { + fprintf(stderr, "Error in IPv4 address %s\n", str); + exit(1); + } + + return 4; + } + +} + +void get_ip_addr_set(int type, void *crt) +{ + int ret = 0, i; + unsigned char ip[16]; + int len; + + if (batch) { + if (!cfg.ip_addr) + return; + + for (i = 0; cfg.ip_addr[i] != NULL; i++) { + len = string_to_ip(ip, cfg.ip_addr[i]); + if (len <= 0) { + fprintf(stderr, + "Error parsing address: %s\n", + cfg.ip_addr[i]); + exit(1); + } + + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_subject_alt_name + (crt, GNUTLS_SAN_IPADDRESS, ip, len, + GNUTLS_FSAN_APPEND); + else + ret = + gnutls_x509_crq_set_subject_alt_name + (crt, GNUTLS_SAN_IPADDRESS, ip, len, + GNUTLS_FSAN_APPEND); + + if (ret < 0) + break; + } + } else { + const char *p; + + p = read_str + ("Enter the IP address of the subject of the certificate: "); + if (!p) + return; + + len = string_to_ip(ip, p); + if (len <= 0) { + fprintf(stderr, "Error parsing address: %s\n", p); + exit(1); + } + + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_subject_alt_name(crt, + GNUTLS_SAN_IPADDRESS, + ip, len, + GNUTLS_FSAN_APPEND); + else + ret = + gnutls_x509_crq_set_subject_alt_name(crt, + GNUTLS_SAN_IPADDRESS, + ip, len, + GNUTLS_FSAN_APPEND); + } + + if (ret < 0) { + fprintf(stderr, "set_subject_alt_name: %s\n", + gnutls_strerror(ret)); + exit(1); + } +} + +void get_email_set(int type, void *crt) +{ + int ret = 0, i; + + if (batch) { + if (!cfg.email) + return; + + for (i = 0; cfg.email[i] != NULL; i++) { + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_subject_alt_name + (crt, GNUTLS_SAN_RFC822NAME, + cfg.email[i], strlen(cfg.email[i]), + GNUTLS_FSAN_APPEND); + else + ret = + gnutls_x509_crq_set_subject_alt_name + (crt, GNUTLS_SAN_RFC822NAME, + cfg.email[i], strlen(cfg.email[i]), + GNUTLS_FSAN_APPEND); + + if (ret < 0) + break; + } + } else { + const char *p; + + p = read_str + ("Enter the e-mail of the subject of the certificate: "); + if (!p) + return; + + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_subject_alt_name(crt, + GNUTLS_SAN_RFC822NAME, + p, + strlen(p), + GNUTLS_FSAN_APPEND); + else + ret = + gnutls_x509_crq_set_subject_alt_name(crt, + GNUTLS_SAN_RFC822NAME, + p, + strlen(p), + GNUTLS_FSAN_APPEND); + } + + if (ret < 0) { + fprintf(stderr, "set_subject_alt_name: %s\n", + gnutls_strerror(ret)); + exit(1); + } +} + + +void get_dc_set(int type, void *crt) +{ + int ret = 0, i; + + if (batch) { + if (!cfg.dc) + return; + + for (i = 0; cfg.dc[i] != NULL; i++) { + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_dn_by_oid(crt, + GNUTLS_OID_LDAP_DC, + 0, + cfg. + dc[i], + strlen + (cfg. + dc[i])); + else + ret = + gnutls_x509_crq_set_dn_by_oid(crt, + GNUTLS_OID_LDAP_DC, + 0, + cfg. + dc[i], + strlen + (cfg. + dc[i])); + + if (ret < 0) + break; + } + } else { + const char *p; + unsigned int counter = 0; + + do { + if (counter == 0) { + p = read_str + ("Enter the subject's domain component (DC): "); + } else { + p = read_str + ("Enter an additional domain component (DC): "); + } + if (!p) + return; + + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_dn_by_oid(crt, + GNUTLS_OID_LDAP_DC, + 0, p, + strlen + (p)); + else + ret = + gnutls_x509_crq_set_dn_by_oid(crt, + GNUTLS_OID_LDAP_DC, + 0, p, + strlen + (p)); + counter++; + if (ret < 0) + break; + } + while (p != NULL); + } + + if (ret < 0) { + fprintf(stderr, "set_dn_by_oid: %s\n", + gnutls_strerror(ret)); + exit(1); + } +} + +void get_dns_name_set(int type, void *crt) +{ + int ret = 0, i; + + if (batch) { + if (!cfg.dns_name) + return; + + for (i = 0; cfg.dns_name[i] != NULL; i++) { + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_subject_alt_name + (crt, GNUTLS_SAN_DNSNAME, + cfg.dns_name[i], + strlen(cfg.dns_name[i]), + GNUTLS_FSAN_APPEND); + else + ret = + gnutls_x509_crq_set_subject_alt_name + (crt, GNUTLS_SAN_DNSNAME, + cfg.dns_name[i], + strlen(cfg.dns_name[i]), + GNUTLS_FSAN_APPEND); + + if (ret < 0) + break; + } + } else { + const char *p; + unsigned int counter = 0; + + do { + if (counter == 0) { + p = read_str("Enter a dnsName of the subject of the certificate: "); + } else { + p = read_str("Enter an additional dnsName of the subject of the certificate: "); + } + if (!p) + return; + + if (type == TYPE_CRT) + ret = gnutls_x509_crt_set_subject_alt_name + (crt, GNUTLS_SAN_DNSNAME, p, strlen(p), + GNUTLS_FSAN_APPEND); + else + ret = gnutls_x509_crq_set_subject_alt_name + (crt, GNUTLS_SAN_DNSNAME, p, strlen(p), + GNUTLS_FSAN_APPEND); + counter++; + } while (p); + } + + if (ret < 0) { + fprintf(stderr, "set_subject_alt_name: %s\n", + gnutls_strerror(ret)); + exit(1); + } +} + +static int set_krb5_principal(int type, void *crt) +{ + int ret = 0, i; + + if (batch) { + if (!cfg.krb5_principal) + return 0; + + for (i = 0; cfg.krb5_principal[i] != NULL; i ++) { + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_subject_alt_name + (crt, GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL, + cfg.krb5_principal[i], strlen(cfg.krb5_principal[i]), + GNUTLS_FSAN_APPEND); + else + ret = + gnutls_x509_crq_set_subject_alt_name + (crt, GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL, + cfg.krb5_principal[i], strlen(cfg.krb5_principal[i]), + GNUTLS_FSAN_APPEND); + + if (ret < 0) + break; + } + } + + if (ret < 0) { + fprintf(stderr, "set_subject_alt_name(GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL): %s\n", + gnutls_strerror(ret)); + exit(1); + } + + return ret; +} + +static int set_othername(int type, void *crt) +{ + int ret = 0, i; + uint8_t *binname = NULL; + unsigned binnamelen = 0; + const char *oid; + + if (batch) { + if (!cfg.other_name) + return 0; + + for (i = 0; cfg.other_name[i] != NULL; i += 2) { + oid = cfg.other_name[i]; + + if (cfg.other_name[i + 1] == NULL) { + fprintf(stderr, + "other_name: %s does not have an argument.\n", + cfg.other_name[i]); + exit(1); + } + + HEX_DECODE (cfg.other_name[i+1], binname, binnamelen); + if (binnamelen == 0) + break; + + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_subject_alt_othername + (crt, oid, + binname, binnamelen, + GNUTLS_FSAN_APPEND); + else + ret = + gnutls_x509_crq_set_subject_alt_othername + (crt, oid, + binname, binnamelen, + GNUTLS_FSAN_APPEND); + free (binname); + binname = NULL; + + if (ret < 0) + break; + } + } + + if (ret < 0) { + fprintf(stderr, "set_subject_alt_othername: %s\n", + gnutls_strerror(ret)); + exit(1); + } + + return ret; +} + +static int set_othername_utf8(int type, void *crt) +{ + int ret = 0, i; + const char *oid; + + if (batch) { + if (!cfg.other_name_utf8) + return 0; + + for (i = 0; cfg.other_name_utf8[i] != NULL; i += 2) { + oid = cfg.other_name_utf8[i]; + + if (cfg.other_name_utf8[i + 1] == NULL) { + fprintf(stderr, + "other_name_utf8: %s does not have an argument.\n", + cfg.other_name_utf8[i]); + exit(1); + } + + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_subject_alt_othername + (crt, oid, + cfg.other_name_utf8[i + 1], strlen(cfg.other_name_utf8[i + 1]), + GNUTLS_FSAN_APPEND|GNUTLS_FSAN_ENCODE_UTF8_STRING); + else + ret = + gnutls_x509_crq_set_subject_alt_othername + (crt, oid, + cfg.other_name_utf8[i + 1], strlen(cfg.other_name_utf8[i + 1]), + GNUTLS_FSAN_APPEND|GNUTLS_FSAN_ENCODE_UTF8_STRING); + + if (ret < 0) + break; + } + } + + if (ret < 0) { + fprintf(stderr, "set_subject_alt_othername: %s\n", + gnutls_strerror(ret)); + exit(1); + } + + return ret; +} + +static int set_othername_octet(int type, void *crt) +{ + int ret = 0, i; + const char *oid; + + if (batch) { + if (!cfg.other_name_octet) + return 0; + + for (i = 0; cfg.other_name_octet[i] != NULL; i += 2) { + oid = cfg.other_name_octet[i]; + + if (cfg.other_name_octet[i + 1] == NULL) { + fprintf(stderr, + "other_name_octet: %s does not have an argument.\n", + cfg.other_name_octet[i]); + exit(1); + } + + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_subject_alt_othername + (crt, oid, + cfg.other_name_octet[i + 1], strlen(cfg.other_name_octet[i + 1]), + GNUTLS_FSAN_APPEND|GNUTLS_FSAN_ENCODE_OCTET_STRING); + else + ret = + gnutls_x509_crq_set_subject_alt_othername + (crt, oid, + cfg.other_name_octet[i + 1], strlen(cfg.other_name_octet[i + 1]), + GNUTLS_FSAN_APPEND|GNUTLS_FSAN_ENCODE_OCTET_STRING); + + if (ret < 0) + break; + } + } + + if (ret < 0) { + fprintf(stderr, "set_subject_alt_othername: %s\n", + gnutls_strerror(ret)); + exit(1); + } + + return ret; +} + +static int set_xmpp_name(int type, void *crt) +{ + int ret = 0, i; + + if (batch) { + if (!cfg.xmpp_name) + return 0; + + for (i = 0; cfg.xmpp_name[i] != NULL; i ++) { + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_subject_alt_name + (crt, GNUTLS_SAN_OTHERNAME_XMPP, + cfg.xmpp_name[i], strlen(cfg.xmpp_name[i]), + GNUTLS_FSAN_APPEND); + else + ret = + gnutls_x509_crq_set_subject_alt_name + (crt, GNUTLS_SAN_OTHERNAME_XMPP, + cfg.xmpp_name[i], strlen(cfg.xmpp_name[i]), + GNUTLS_FSAN_APPEND); + + if (ret < 0) + break; + } + } + + if (ret < 0) { + fprintf(stderr, "set_subject_alt_name(XMPP): %s\n", + gnutls_strerror(ret)); + exit(1); + } + + return ret; +} + + +void get_other_name_set(int type, void *crt) +{ + set_othername(type, crt); + set_othername_octet(type, crt); + set_othername_utf8(type, crt); + set_xmpp_name(type, crt); + set_krb5_principal(type, crt); +} + +void get_policy_set(gnutls_x509_crt_t crt) +{ + int ret = 0, i; + gnutls_x509_policy_st policy; + + if (batch) { + if (cfg.skip_certs >= 0) { + ret = gnutls_x509_crt_set_inhibit_anypolicy(crt, cfg.skip_certs); + if (ret < 0) { + fprintf(stderr, "error setting inhibit anypolicy: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + + for (i = 0; cfg.policy_oid[i] != NULL; i++) { + memset(&policy, 0, sizeof(policy)); + policy.oid = cfg.policy_oid[i]; + + if (cfg.policy_txt[i] != NULL) { + policy.qualifier[policy.qualifiers].type = + GNUTLS_X509_QUALIFIER_NOTICE; + policy.qualifier[policy.qualifiers].data = + cfg.policy_txt[i]; + policy.qualifier[policy.qualifiers].size = + strlen(cfg.policy_txt[i]); + policy.qualifiers++; + } + + if (cfg.policy_url[i] != NULL) { + policy.qualifier[policy.qualifiers].type = + GNUTLS_X509_QUALIFIER_URI; + policy.qualifier[policy.qualifiers].data = + cfg.policy_url[i]; + policy.qualifier[policy.qualifiers].size = + strlen(cfg.policy_url[i]); + policy.qualifiers++; + } + + ret = gnutls_x509_crt_set_policy(crt, &policy, 0); + if (ret < 0) + break; + } + } + + if (ret < 0) { + fprintf(stderr, "set_policy: %s\n", gnutls_strerror(ret)); + exit(1); + } +} + +void get_uri_set(int type, void *crt) +{ + int ret = 0, i; + + if (batch) { + if (!cfg.uri) + return; + + for (i = 0; cfg.uri[i] != NULL; i++) { + if (type == TYPE_CRT) + ret = + gnutls_x509_crt_set_subject_alt_name + (crt, GNUTLS_SAN_URI, cfg.uri[i], + strlen(cfg.uri[i]), + GNUTLS_FSAN_APPEND); + else + ret = + gnutls_x509_crq_set_subject_alt_name + (crt, GNUTLS_SAN_URI, cfg.uri[i], + strlen(cfg.uri[i]), + GNUTLS_FSAN_APPEND); + + if (ret < 0) + break; + } + } else { + const char *p; + unsigned int counter = 0; + + do { + if (counter == 0) { + p = read_str + ("Enter a URI of the subject of the certificate: "); + } else { + p = read_str + ("Enter an additional URI of the subject of the certificate: "); + } + if (!p) + return; + + if (type == TYPE_CRT) + ret = gnutls_x509_crt_set_subject_alt_name + (crt, GNUTLS_SAN_URI, p, strlen(p), + GNUTLS_FSAN_APPEND); + else + ret = gnutls_x509_crq_set_subject_alt_name + (crt, GNUTLS_SAN_URI, p, strlen(p), + GNUTLS_FSAN_APPEND); + counter++; + if (ret < 0) + break; + } + while (p); + } + + if (ret < 0) { + fprintf(stderr, "set_subject_alt_name: %s\n", + gnutls_strerror(ret)); + exit(1); + } +} + + + +int get_sign_status(int server) +{ + const char *msg; + + if (batch) { + return cfg.signing_key; + } else { + if (server) + msg = + "Will the certificate be used for signing (DHE ciphersuites)? (Y/n): "; + else + msg = + "Will the certificate be used for signing (required for TLS)? (Y/n): "; + return read_yesno(msg, 1); + } +} + +int get_encrypt_status(int server) +{ + const char *msg; + + if (batch) { + return cfg.encryption_key; + } else { + if (server) + msg = + "Will the certificate be used for encryption (RSA ciphersuites)? (Y/n): "; + else + msg = + "Will the certificate be used for encryption (not required for TLS)? (Y/n): "; + return read_yesno(msg, 1); + } +} + +int get_cert_sign_status(void) +{ + if (batch) { + return cfg.cert_sign_key; + } else { + return + read_yesno + ("Will the certificate be used to sign other certificates? (Y/n): ", + 1); + } +} + +int get_crl_sign_status(void) +{ + if (batch) { + return cfg.crl_sign_key; + } else { + return + read_yesno + ("Will the certificate be used to sign CRLs? (y/N): ", + 0); + } +} + +int get_key_agreement_status(void) +{ + if (batch) { + return cfg.key_agreement; + } else { + /* this option is not asked in interactive mode */ + return 0; + } +} + +int get_non_repudiation_status(void) +{ + if (batch) { + return cfg.non_repudiation; + } else { + /* this option is not asked in interactive mode */ + return 0; + } +} + +int get_data_encipherment_status(void) +{ + if (batch) { + return cfg.data_encipherment; + } else { + return read_yesno("Will the certificate be used for data encryption? (y/N): ", 0); + } +} + +int get_code_sign_status(void) +{ + if (batch) { + return cfg.code_sign_key; + } else { + return + read_yesno + ("Will the certificate be used to sign code? (y/N): ", + 0); + } +} + +int get_ocsp_sign_status(void) +{ + if (batch) { + return cfg.ocsp_sign_key; + } else { + return + read_yesno + ("Will the certificate be used to sign OCSP requests? (y/N): ", + 0); + } +} + +int get_time_stamp_status(void) +{ + if (batch) { + return cfg.time_stamping_key; + } else { + return + read_yesno + ("Will the certificate be used for time stamping? (y/N): ", + 0); + } +} + +int get_email_protection_status(void) +{ + if (batch) { + return cfg.email_protection_key; + } else { + return + read_yesno + ("Will the certificate be used for email protection? (y/N): ", + 0); + } +} + +int get_ipsec_ike_status(void) +{ + if (batch) { + return cfg.ipsec_ike_key; + } else { + return + read_yesno + ("Will the certificate be used for IPsec IKE operations? (y/N): ", + 0); + } +} + +time_t get_crl_next_update(void) +{ + return get_int_date(cfg.next_update_date, cfg.crl_next_update, "The next CRL will be issued in (days): "); +} + +const char *get_proxy_policy(char **policy, size_t * policylen) +{ + const char *ret; + + if (batch) { + ret = cfg.proxy_policy_language; + if (!ret) + ret = "1.3.6.1.5.5.7.21.1"; + } else { + do { + ret = + read_str + ("Enter the OID of the proxy policy language: "); + } + while (ret == NULL); + } + + *policy = NULL; + *policylen = 0; + + if (strcmp(ret, "1.3.6.1.5.5.7.21.1") != 0 && + strcmp(ret, "1.3.6.1.5.5.7.21.2") != 0) { + fprintf(stderr, + "Reading non-standard proxy policy not supported.\n"); + } + + return ret; +} + +/* CRQ stuff. + */ +void get_country_crq_set(gnutls_x509_crq_t crq) +{ + int ret; + + if (batch) { + if (!cfg.country) + return; + ret = + gnutls_x509_crq_set_dn_by_oid(crq, + GNUTLS_OID_X520_COUNTRY_NAME, + 0, cfg.country, + strlen(cfg.country)); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + read_crq_set(crq, "Country name (2 chars): ", + GNUTLS_OID_X520_COUNTRY_NAME); + } + +} + +void get_organization_crq_set(gnutls_x509_crq_t crq) +{ + int ret; + unsigned i; + + if (batch) { + if (!cfg.organization) + return; + + for (i = 0; cfg.organization[i] != NULL; i++) { + ret = + gnutls_x509_crq_set_dn_by_oid(crq, + GNUTLS_OID_X520_ORGANIZATION_NAME, + 0, cfg.organization[i], + strlen(cfg. + organization[i])); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } + } else { + read_crq_set(crq, "Organization name: ", + GNUTLS_OID_X520_ORGANIZATION_NAME); + } + +} + +void get_unit_crq_set(gnutls_x509_crq_t crq) +{ + int ret; + unsigned i; + + if (batch) { + if (!cfg.unit) + return; + + for (i = 0; cfg.unit[i] != NULL; i++) { + ret = + gnutls_x509_crq_set_dn_by_oid(crq, + GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, + 0, cfg.unit[i], + strlen(cfg.unit[i])); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } + } else { + read_crq_set(crq, "Organizational unit name: ", + GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME); + } + +} + +void get_state_crq_set(gnutls_x509_crq_t crq) +{ + int ret; + + if (batch) { + if (!cfg.state) + return; + ret = + gnutls_x509_crq_set_dn_by_oid(crq, + GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, + 0, cfg.state, + strlen(cfg.state)); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + read_crq_set(crq, "State or province name: ", + GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME); + } + +} + +void get_locality_crq_set(gnutls_x509_crq_t crq) +{ + int ret; + + if (batch) { + if (!cfg.locality) + return; + ret = + gnutls_x509_crq_set_dn_by_oid(crq, + GNUTLS_OID_X520_LOCALITY_NAME, + 0, cfg.locality, + strlen(cfg.locality)); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + read_crq_set(crq, "Locality name: ", + GNUTLS_OID_X520_LOCALITY_NAME); + } + +} + +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) +{ + int ret; + + if (batch) { + if (!cfg.cn) + return; + ret = + gnutls_x509_crq_set_dn_by_oid(crq, + GNUTLS_OID_X520_COMMON_NAME, + 0, cfg.cn, + strlen(cfg.cn)); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + read_crq_set(crq, "Common name: ", + GNUTLS_OID_X520_COMMON_NAME); + } + +} + +void get_uid_crq_set(gnutls_x509_crq_t crq) +{ + int ret; + + if (batch) { + if (!cfg.uid) + return; + ret = + gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_LDAP_UID, + 0, cfg.uid, + strlen(cfg.uid)); + if (ret < 0) { + fprintf(stderr, "set_dn: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + read_crq_set(crq, "UID: ", GNUTLS_OID_LDAP_UID); + } + +} + +void get_oid_crq_set(gnutls_x509_crq_t crq) +{ + int ret, i; + + if (batch) { + if (!cfg.dn_oid) + return; + for (i = 0; cfg.dn_oid[i] != NULL; i += 2) { + if (cfg.dn_oid[i + 1] == NULL) { + fprintf(stderr, + "dn_oid: %s does not have an argument.\n", + cfg.dn_oid[i]); + exit(1); + } + ret = + gnutls_x509_crq_set_dn_by_oid(crq, + cfg.dn_oid[i], 0, + cfg.dn_oid[i + + 1], + strlen(cfg. + dn_oid[i + + 1])); + + if (ret < 0) { + fprintf(stderr, "set_dn_oid: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } + } + +} + +void get_tlsfeatures_set(int type, void *crt) +{ + int ret, i; + unsigned int feature; + + if (batch) { + if (!cfg.tls_features) + return; + + gnutls_x509_tlsfeatures_t features; + ret = gnutls_x509_tlsfeatures_init(&features); + if (ret < 0) { + fprintf(stderr, "gnutls_x509_tlsfeatures_init: %s\n", + gnutls_strerror(ret)); + exit(1); + } + + for (i = 0; cfg.tls_features[i]; ++i) { + feature = strtoul(cfg.tls_features[i], 0, 10); + ret = gnutls_x509_tlsfeatures_add(features, feature); + if (ret < 0) { + fprintf(stderr, "gnutls_x509_tlsfeatures_add: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } + + if (type == TYPE_CRT) { + ret = gnutls_x509_crt_set_tlsfeatures(crt, features); + if (ret < 0) { + fprintf(stderr, "gnutls_x509_crt_set_tlsfeatures: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } + else { + ret = gnutls_x509_crq_set_tlsfeatures(crt, features); + if (ret < 0) { + fprintf(stderr, "gnutls_x509_crq_set_tlsfeatures: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } + + gnutls_x509_tlsfeatures_deinit(features); + } +} + +void crq_extensions_set(gnutls_x509_crt_t crt, gnutls_x509_crq_t crq) +{ + int ret, i; + + if (batch) { + if (!cfg.exts_to_honor) + return; + + for (i = 0; cfg.exts_to_honor[i]; ++i) { + ret = gnutls_x509_crt_set_crq_extension_by_oid(crt, crq, cfg.exts_to_honor[i], 0); + if (ret < 0) { + fprintf(stderr, "setting extension failed: %s: %s\n", cfg.exts_to_honor[i], + gnutls_strerror(ret)); + } + } + } +} |