diff --git a/apps/apps.c b/apps/apps.c index 30405662571ddf9573592d129f1903a5c89f2c86..834cedd5a33db8f623301cce115e0648f59de605 100644 --- a/apps/apps.c +++ b/apps/apps.c @@ -442,7 +442,7 @@ static char *app_get_pass(const char *arg, int keepbio) return OPENSSL_strdup(tpass); } -static CONF *app_load_config_(BIO *in, const char *filename) +CONF *app_load_config_bio(BIO *in, const char *filename) { long errorline = -1; CONF *conf; @@ -453,12 +453,17 @@ static CONF *app_load_config_(BIO *in, const char *filename) if (i > 0) return conf; - if (errorline <= 0) - BIO_printf(bio_err, "%s: Can't load config file \"%s\"\n", - opt_getprog(), filename); + if (errorline <= 0) { + BIO_printf(bio_err, "%s: Can't load ", opt_getprog()); + } else { + BIO_printf(bio_err, "%s: Error on line %ld of ", opt_getprog(), + errorline); + } + if (filename != NULL) + BIO_printf(bio_err, "config file \"%s\"\n", filename); else - BIO_printf(bio_err, "%s: Error on line %ld of config file \"%s\"\n", - opt_getprog(), errorline, filename); + BIO_printf(bio_err, "config input"); + NCONF_free(conf); return NULL; } @@ -472,7 +477,7 @@ CONF *app_load_config(const char *filename) if (in == NULL) return NULL; - conf = app_load_config_(in, filename); + conf = app_load_config_bio(in, filename); BIO_free(in); return conf; } @@ -486,7 +491,7 @@ CONF *app_load_config_quiet(const char *filename) if (in == NULL) return NULL; - conf = app_load_config_(in, filename); + conf = app_load_config_bio(in, filename); BIO_free(in); return conf; } diff --git a/apps/apps.h b/apps/apps.h index 321f6444da8a1e00333b80780c1103d5627b57cf..6d0d7015f886e61b1196ffac1af52bfd7f0498eb 100644 --- a/apps/apps.h +++ b/apps/apps.h @@ -52,6 +52,7 @@ BIO *dup_bio_err(int format); BIO *bio_open_owner(const char *filename, int format, int private); BIO *bio_open_default(const char *filename, char mode, int format); BIO *bio_open_default_quiet(const char *filename, char mode, int format); +CONF *app_load_config_bio(BIO *in, const char *filename); CONF *app_load_config(const char *filename); CONF *app_load_config_quiet(const char *filename); int app_load_modules(const CONF *config); diff --git a/apps/req.c b/apps/req.c index 989a6ad7cd85b08700a882263b8e3590990f8d1f..dca6038a285481a4812ba2448ace7aab275222fe 100644 --- a/apps/req.c +++ b/apps/req.c @@ -70,6 +70,7 @@ static EVP_PKEY_CTX *set_keygen_ctx(const char *gstr, int *pkey_type, long *pkeylen, char **palgnam, ENGINE *keygen_engine); static CONF *req_conf = NULL; +static CONF *addext_conf = NULL; static int batch = 0; typedef enum OPTION_choice { @@ -80,7 +81,7 @@ typedef enum OPTION_choice { OPT_PKEYOPT, OPT_SIGOPT, OPT_BATCH, OPT_NEWHDR, OPT_MODULUS, OPT_VERIFY, OPT_NODES, OPT_NOOUT, OPT_VERBOSE, OPT_UTF8, OPT_NAMEOPT, OPT_REQOPT, OPT_SUBJ, OPT_SUBJECT, OPT_TEXT, OPT_X509, - OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, OPT_EXTENSIONS, + OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, OPT_ADDEXT, OPT_EXTENSIONS, OPT_REQEXTS, OPT_PRECERT, OPT_MD, OPT_R_ENUM } OPTION_CHOICE; @@ -124,6 +125,8 @@ const OPTIONS req_options[] = { "Enable support for multivalued RDNs"}, {"days", OPT_DAYS, 'p', "Number of days cert is valid for"}, {"set_serial", OPT_SET_SERIAL, 's', "Serial number to use"}, + {"addext", OPT_ADDEXT, 's', + "Additional cert extension key=value pair (may be given more than once)"}, {"extensions", OPT_EXTENSIONS, 's', "Cert extension section (override value in config file)"}, {"reqexts", OPT_REQEXTS, 's', @@ -150,6 +153,7 @@ int req_main(int argc, char **argv) X509_REQ *req = NULL; const EVP_CIPHER *cipher = NULL; const EVP_MD *md_alg = NULL, *digest = NULL; + BIO *addext_bio = NULL; char *extensions = NULL, *infile = NULL; char *outfile = NULL, *keyfile = NULL; char *keyalgstr = NULL, *p, *prog, *passargin = NULL, *passargout = NULL; @@ -313,6 +317,14 @@ int req_main(int argc, char **argv) case OPT_MULTIVALUE_RDN: multirdn = 1; break; + case OPT_ADDEXT: + if (addext_bio == NULL) { + addext_bio = BIO_new(BIO_s_mem()); + } + if (addext_bio == NULL + || BIO_printf(addext_bio, "%s\n", opt_arg()) < 0) + goto end; + break; case OPT_EXTENSIONS: extensions = opt_arg(); break; @@ -349,6 +361,12 @@ int req_main(int argc, char **argv) if (verbose) BIO_printf(bio_err, "Using configuration from %s\n", template); req_conf = app_load_config(template); + if (addext_bio) { + if (verbose) + BIO_printf(bio_err, + "Using additional configuraton from command line\n"); + addext_conf = app_load_config_bio(addext_bio, NULL); + } if (template != default_config_file && !app_load_modules(req_conf)) goto end; @@ -401,6 +419,16 @@ int req_main(int argc, char **argv) goto end; } } + if (addext_conf != NULL) { + /* Check syntax of command line extensions */ + X509V3_CTX ctx; + X509V3_set_ctx_test(&ctx); + X509V3_set_nconf(&ctx, addext_conf); + if (!X509V3_EXT_add_nconf(addext_conf, &ctx, "default", NULL)) { + BIO_printf(bio_err, "Error Loading command line extensions\n"); + goto end; + } + } if (passin == NULL) { passin = nofree_passin = @@ -605,7 +633,8 @@ int req_main(int argc, char **argv) goto end; /* Set version to V3 */ - if (extensions != NULL && !X509_set_version(x509ss, 2)) + if ((extensions != NULL || addext_conf != NULL) + && !X509_set_version(x509ss, 2)) goto end; if (serial != NULL) { if (!X509_set_serialNumber(x509ss, serial)) @@ -643,6 +672,12 @@ int req_main(int argc, char **argv) extensions); goto end; } + if (addext_conf != NULL + && !X509V3_EXT_add_nconf(addext_conf, &ext_ctx, "default", + x509ss)) { + BIO_printf(bio_err, "Error Loading command line extensions\n"); + goto end; + } /* If a pre-cert was requested, we need to add a poison extension */ if (precert) { @@ -674,6 +709,12 @@ int req_main(int argc, char **argv) req_exts); goto end; } + if (addext_conf != NULL + && !X509V3_EXT_REQ_add_nconf(addext_conf, &ext_ctx, "default", + req)) { + BIO_printf(bio_err, "Error Loading command line extensions\n"); + goto end; + } i = do_X509_REQ_sign(req, pkey, digest, sigopts); if (!i) { ERR_print_errors(bio_err); @@ -817,6 +858,7 @@ int req_main(int argc, char **argv) ERR_print_errors(bio_err); } NCONF_free(req_conf); + BIO_free(addext_bio); BIO_free(in); BIO_free_all(out); EVP_PKEY_free(pkey); diff --git a/doc/man1/req.pod b/doc/man1/req.pod index 5ed90ada740a53b5aa80206e8271a7f91eda143f..db467bba17f0447f88b19d11f579f35a1f4518ae 100644 --- a/doc/man1/req.pod +++ b/doc/man1/req.pod @@ -37,6 +37,7 @@ B B [B<-days n>] [B<-set_serial n>] [B<-newhdr>] +[B<-addext ext>] [B<-extensions section>] [B<-reqexts section>] [B<-precert>] @@ -255,6 +256,14 @@ be a positive integer. The default is 30 days. Serial number to use when outputting a self signed certificate. This may be specified as a decimal value or a hex value if preceded by B<0x>. +=item B<-addext ext> + +Add a specific extension to the certificate (if the B<-x509> option is +present) or certificate request. The argument must have the form of +a key=value pair as it would appear in a config file. + +This option can be given multiple times. + =item B<-extensions section> =item B<-reqexts section> @@ -591,6 +600,14 @@ Sample configuration containing all field values: [ req_attributes ] challengePassword = A challenge password +Example of giving the most common attributes (subject and extensions) +on the command line: + + openssl req -new -subj "/C=GB/CN=foo" \ + -addext "subjectAltName = DNS:foo.co.uk" \ + -addext "certificatePolicies = 1.2.3.4" \ + -newkey rsa:2048 -keyout key.pem -out req.pem + =head1 NOTES