diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/certtool-cfg.c | 294 | ||||
-rw-r--r-- | src/certtool-cfg.h | 1 | ||||
-rw-r--r-- | src/certtool-common.h | 2 | ||||
-rw-r--r-- | src/certtool.c | 4 |
4 files changed, 236 insertions, 65 deletions
diff --git a/src/certtool-cfg.c b/src/certtool-cfg.c index 7f552c08c7..577bccf8b7 100644 --- a/src/certtool-cfg.c +++ b/src/certtool-cfg.c @@ -39,7 +39,6 @@ #include <intprops.h> #include <gnutls/crypto.h> #include <libtasn1.h> -#include <assert.h> /* for inet_pton */ #include <sys/types.h> @@ -127,10 +126,10 @@ static struct cfg_options available_options[] = { { .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_NUMERIC }, + { .name = "serial", .type = OPTION_STRING }, { .name = "expiration_days", .type = OPTION_NUMERIC }, { .name = "crl_next_update", .type = OPTION_NUMERIC }, - { .name = "crl_number", .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 }, @@ -197,7 +196,8 @@ typedef struct _cfg_ctx { char *revocation_date; char *this_update_date; char *next_update_date; - int64_t serial; + uint8_t *serial; + unsigned serial_size; int expiration_days; int skip_certs; /* from inhibit anypolicy */ int ca; @@ -218,7 +218,8 @@ typedef struct _cfg_ctx { int ipsec_ike_key; char **key_purpose_oids; int crl_next_update; - int64_t crl_number; + uint8_t *crl_number; + unsigned crl_number_size; int honor_crq_extensions; char *proxy_policy_language; char **exts_to_honor; @@ -233,8 +234,6 @@ void cfg_init(void) { memset(&cfg, 0, sizeof(cfg)); cfg.path_len = -1; - cfg.crl_number = -1; - cfg.serial = -1; cfg.skip_certs = -1; } @@ -306,12 +305,6 @@ void cfg_init(void) } /* READ_NUMERIC only returns a long */ -#define CHECK_LONG_OVERFLOW(x) \ - if (x == LONG_MAX) { \ - fprintf(stderr, "overflow in number\n"); \ - exit(1); \ - } - #define READ_NUMERIC(name, s_name) \ val = optionGetValue(pov, name); \ if (val != NULL) \ @@ -335,6 +328,19 @@ void cfg_init(void) 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(const tOptionValue* val) { unsigned j; @@ -518,13 +524,16 @@ int template_parse(const char *template) cfg.pkcs12_key_name = strdup(val->v.strVal); - READ_NUMERIC("serial", cfg.serial); - CHECK_LONG_OVERFLOW(cfg.serial); + val = optionGetValue(pov, "serial"); + if (val != NULL && val->valType == OPARG_TYPE_STRING) + SERIAL_DECODE(val->v.strVal, cfg.serial, cfg.serial_size); READ_NUMERIC("expiration_days", cfg.expiration_days); READ_NUMERIC("crl_next_update", cfg.crl_next_update); - READ_NUMERIC("crl_number", cfg.crl_number); - CHECK_LONG_OVERFLOW(cfg.crl_number); + + val = optionGetValue(pov, "crl_number"); + if (val != NULL && val->valType == OPARG_TYPE_STRING) + SERIAL_DECODE(val->v.strVal, cfg.crl_number, cfg.crl_number_size); READ_NUMERIC("path_len", cfg.path_len); @@ -707,6 +716,56 @@ 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: %lu)\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]; @@ -1453,75 +1512,183 @@ void get_pkcs9_email_crt_set(gnutls_x509_crt_t crt) static -void get_rand_int_value(unsigned char* serial, size_t * size, int64_t cfg_val, const char *msg) +int default_crl_number(unsigned char* serial, size_t *size) { struct timespec ts; - uint32_t default_serial[2]; + time_t tv_sec_tmp; + int i; /* default format: - * | 4 b | 4 b | 4b + * | 5 b | 4 b | 11b * | secs | nsecs | rnd | */ gettime(&ts); - if (*size < 12) { - fprintf(stderr, "error in get_serial()!\n"); + 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); +} + +/** + * strip_trailing_newlines: + * @str: zero-terminated string that will be modified in-place, must not be NULL + * + * This function will remove trailing CR or LF characters. + **/ +static void strip_trailing_newlines(char *str) +{ + char *end; + + end = str; + while (*end != '\0') end++; + end--; + while (end >= str && (*end == '\r' || *end == '\n')) { + *end = '\0'; + end--; + } +} + +/** + * 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 + * + * 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 it's 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) +{ + static char input[MAX_INPUT_SIZE]; + int ret; + 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); } - if (batch && cfg_val < 0) { - serial[0] = (ts.tv_sec >> 24) & 0xff; - serial[1] = (ts.tv_sec >> 16) & 0xff; - serial[2] = (ts.tv_sec >> 8) & 0xff; - serial[3] = (ts.tv_sec) & 0xff; - serial[4] = (ts.tv_nsec >> 24) & 0xff; - serial[5] = (ts.tv_nsec >> 16) & 0xff; - serial[6] = (ts.tv_nsec >> 8) & 0xff; - serial[7] = (ts.tv_nsec) & 0xff; - serial[0] &= 0x7F; - assert(gnutls_rnd(GNUTLS_RND_NONCE, &serial[8], 4) >= 0); - *size = 12; + fprintf(stderr, "Enter the certificate's %s in decimal " + "(123) or hex (0xabcd) (default 0x%s): ", + label, encoded_default.data); + gnutls_free(encoded_default.data); + + if (fgets(input, sizeof(input), stdin) == NULL) + return; + + strip_trailing_newlines(input); + + if (strlen(input) == 0) return; + + ret = serial_decode(input, &decoded); + if (ret < 0) { + fprintf(stderr, "error parsing %s: %s\n", label, input); + exit(1); } - if (batch) { - default_serial[0] = cfg_val >> 32; - default_serial[1] = cfg_val; + if (decoded.size > max_size) { + fprintf(stderr, "maximum %zu octets allowed for %s\n", max_size, label); + gnutls_free(decoded.data); + exit(1); + } + + + memcpy(serial, decoded.data, decoded.size); + *size = decoded.size; + gnutls_free(decoded.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 { - uint64_t default_serial_int; - char tmsg[256]; + ret = create_default(serial, size); + if (ret < 0) { + fprintf(stderr, "error generating default %s: %s\n", + label, gnutls_strerror(ret)); + exit(1); + } + } -#if SIZEOF_LONG < 8 - default_serial_int = ts.tv_sec; - snprintf(tmsg, sizeof(tmsg), "%s (default: %" PRIu64"): ", msg, default_serial_int); -#else - default_serial_int = (ts.tv_sec << 32) | ts.tv_nsec; - snprintf(tmsg, sizeof(tmsg), "%s (default: %lu): ", msg, default_serial_int); -#endif - default_serial_int = read_int_with_default(tmsg, (long)default_serial_int); + if (!batch) + read_serial_value(serial, size, max_size, label); - default_serial[0] = default_serial_int >> 32; - default_serial[1] = default_serial_int; + if ((*size == SERIAL_MAX_BYTES && serial[0] & 0x80) || *size > SERIAL_MAX_BYTES) { + // TODO allow creating larger values and convert this to warning? + fprintf(stderr, "%s would be encoded in more than 20 bytes," + "see RFC 5280, section %s\n", label, rfc_section); + exit(1); } +} - serial[0] = (default_serial[0] >> 24) & 0xff; - serial[1] = (default_serial[0] >> 16) & 0xff; - serial[2] = (default_serial[0] >> 8) & 0xff; - serial[3] = (default_serial[0]) & 0xff; - serial[4] = (default_serial[1] >> 24) & 0xff; - serial[5] = (default_serial[1] >> 16) & 0xff; - serial[6] = (default_serial[1] >> 8) & 0xff; - serial[7] = (default_serial[1]) & 0xff; - serial[0] &= 0x7F; +static +int default_serial(unsigned char *serial, size_t *size) +{ + int ret; - *size = 8; + if (*size < SERIAL_MAX_BYTES) + return GNUTLS_E_SHORT_MEMORY_BUFFER; - return; + 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) +void get_serial(unsigned char *serial, size_t *size) { - get_rand_int_value(serial, size, cfg.serial, "Enter the certificate's serial number in decimal"); + get_serial_value(serial, size, cfg.serial, cfg.serial_size, + default_serial, "serial number", "4.1.2.2"); } static @@ -1655,7 +1822,8 @@ int get_crq_extensions_status(void) void get_crl_number(unsigned char* serial, size_t * size) { - get_rand_int_value(serial, size, cfg.crl_number, "CRL Number"); + get_serial_value(serial, size, cfg.crl_number, cfg.crl_number_size, + default_crl_number, "CRL number", "5.2.3"); } int get_path_len(void) diff --git a/src/certtool-cfg.h b/src/certtool-cfg.h index e3fc56ce99..ea4a2dbc88 100644 --- a/src/certtool-cfg.h +++ b/src/certtool-cfg.h @@ -32,6 +32,7 @@ void read_crt_set(gnutls_x509_crt_t crt, const char *input_str, void read_crq_set(gnutls_x509_crq_t crq, const char *input_str, const char *oid); int64_t read_int(const char *input_str); +int serial_decode(const char *input, gnutls_datum_t *output); const char *read_str(const char *input_str); int read_yesno(const char *input_str, int def); diff --git a/src/certtool-common.h b/src/certtool-common.h index ed6de4a677..11cbc8a23d 100644 --- a/src/certtool-common.h +++ b/src/certtool-common.h @@ -29,6 +29,8 @@ #define TYPE_CRT 1 #define TYPE_CRQ 2 +#define SERIAL_MAX_BYTES 20 + void certtool_version(void); #include <gnutls/x509.h> diff --git a/src/certtool.c b/src/certtool.c index 6664d21c3a..5bac31f12c 100644 --- a/src/certtool.c +++ b/src/certtool.c @@ -397,7 +397,7 @@ generate_certificate(gnutls_privkey_t * ret_key, { size_t serial_size; - unsigned char serial[16]; + unsigned char serial[SERIAL_MAX_BYTES]; serial_size = sizeof(serial); @@ -829,7 +829,7 @@ generate_crl(gnutls_x509_crt_t ca_crt, common_info_st * cinfo) { size_t serial_size; - unsigned char serial[16]; + unsigned char serial[SERIAL_MAX_BYTES]; serial_size = sizeof(serial); |