From 6a2ad2b0780aa94e01aa0cefa960b55656cfd704 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Aug 24 2017 22:25:54 +0000 Subject: [PATCH 1/5] MS cert template: add D-Bus property and storage Add a D-Bus property and storage for a new template attribute, which will hold data to be included in CSRs in the MS V2 certificate template extension. The server does not validate the extension data (validation behaviour is implemented in a subsequent commit). Part of: https://pagure.io/certmonger/issue/78 --- diff --git a/doc/api.txt b/doc/api.txt index 31016be..83cf375 100644 --- a/doc/api.txt +++ b/doc/api.txt @@ -56,6 +56,7 @@ o object layout {("template-crldp"),array-of-string (CRL distribution point URIs)} {("template-ns-comment"),string (Netscape comment)} {("template-profile"),string (certificate profile)} + {("template-ms-certificate-template"),string (MS V2 template specifier; format: :[:] )} {("template-issuer"),string (requested issuer)} {("template-challenge-password"),string (password to add to CSR)} {("template-challenge-password-file"),string (password file) @@ -165,6 +166,7 @@ o object layout {("template-crldp"),array-of-string (CRL distribution point URIs)} {("template-ns-comment"),string (Netscape comment)} {("template-profile"),string (certificate profile)} + {("template-ms-certificate-template"),string (MS V2 template specifier; format: :[:] )} {("template-issuer"),string (requested issuer)} {("template-challenge-password"),string (password to add to CSR)} {("template-challenge-password-file"),string (password file) diff --git a/src/store-files.c b/src/store-files.c index dbeebce..5ccde77 100644 --- a/src/store-files.c +++ b/src/store-files.c @@ -145,6 +145,7 @@ enum cm_store_file_field { cm_store_entry_field_template_ns_comment, cm_store_entry_field_template_profile, cm_store_entry_field_template_issuer, + cm_store_entry_field_template_certificate_template, cm_store_entry_field_template_no_ocsp_check, cm_store_entry_field_template_ns_certtype, @@ -320,6 +321,7 @@ static struct cm_store_file_field_list { {cm_store_entry_field_template_profile, "template_profile"}, /* right */ {cm_store_entry_field_template_profile, "ca_profile"}, /* wrong */ {cm_store_entry_field_template_issuer, "template_issuer"}, + {cm_store_entry_field_template_certificate_template, "template_certificate_template"}, {cm_store_entry_field_template_no_ocsp_check, "template_no_ocsp_check"}, {cm_store_entry_field_template_ns_certtype, "template_ns_certtype"}, @@ -1208,6 +1210,9 @@ cm_store_entry_read(void *parent, const char *filename, FILE *fp) case cm_store_entry_field_template_profile: ret->cm_template_profile = free_if_empty(p); break; + case cm_store_entry_field_template_certificate_template: + ret->cm_template_certificate_template = free_if_empty(p); + break; case cm_store_entry_field_template_issuer: ret->cm_template_issuer = free_if_empty(p); break; @@ -1454,6 +1459,7 @@ cm_store_ca_read(void *parent, const char *filename, FILE *fp) case cm_store_entry_field_template_ocsp_location: case cm_store_entry_field_template_ns_comment: case cm_store_entry_field_template_profile: + case cm_store_entry_field_template_certificate_template: case cm_store_entry_field_template_issuer: case cm_store_entry_field_template_no_ocsp_check: case cm_store_entry_field_template_ns_certtype: @@ -2063,6 +2069,8 @@ cm_store_entry_write(FILE *fp, struct cm_store_entry *entry) entry->cm_template_no_ocsp_check ? 1 : 0); cm_store_file_write_str(fp, cm_store_entry_field_template_ns_certtype, entry->cm_template_ns_certtype); + cm_store_file_write_str(fp, cm_store_entry_field_template_certificate_template, + entry->cm_template_certificate_template); cm_store_file_write_str(fp, cm_store_entry_field_challenge_password, entry->cm_template_challenge_password); @@ -2824,6 +2832,8 @@ cm_store_entry_dup(void *parent, struct cm_store_entry *entry) ret->cm_template_profile = cm_store_maybe_strdup(ret, entry->cm_template_profile); ret->cm_template_issuer = cm_store_maybe_strdup(ret, entry->cm_template_issuer); ret->cm_template_no_ocsp_check = entry->cm_template_no_ocsp_check; + ret->cm_template_certificate_template = + cm_store_maybe_strdup(ret, entry->cm_template_certificate_template); ret->cm_template_ns_certtype = cm_store_maybe_strdup(ret, entry->cm_template_ns_certtype); diff --git a/src/store-int.h b/src/store-int.h index 2d3a353..98b37e6 100644 --- a/src/store-int.h +++ b/src/store-int.h @@ -144,6 +144,7 @@ struct cm_store_entry { char *cm_template_profile; char *cm_template_issuer; char *cm_template_ns_certtype; + char *cm_template_certificate_template; unsigned int cm_template_no_ocsp_check: 1; /* A challenge password, which may be included (in cleartext form!) in * a CSR. */ diff --git a/src/tdbus.h b/src/tdbus.h index 496f2dd..7164f11 100644 --- a/src/tdbus.h +++ b/src/tdbus.h @@ -110,6 +110,7 @@ #define CM_DBUS_PROP_TEMPLATE_PROFILE "template-profile" #define CM_DBUS_PROP_TEMPLATE_ISSUER "template-issuer" #define CM_DBUS_PROP_TEMPLATE_NS_CERTTYPE "template-ns-certtype" +#define CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE "template-ms-certificate-template" #define CM_DBUS_SIGNAL_REQUEST_CERT_SAVED "SavedCertificate" #define CM_DBUS_PROP_CA_PRESAVE_COMMAND "ca-presave-command" #define CM_DBUS_PROP_CA_PRESAVE_UID "ca-presave-uid" diff --git a/src/tdbush.c b/src/tdbush.c index 79f0301..6348cfc 100644 --- a/src/tdbush.c +++ b/src/tdbush.c @@ -1569,6 +1569,14 @@ base_add_request(DBusConnection *conn, DBusMessage *msg, param->value.s); } param = cm_tdbusm_find_dict_entry(d, + CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE, + cm_tdbusm_dict_s); + if (param != NULL) { + // TODO check validity + new_entry->cm_template_certificate_template = maybe_strdup(new_entry, + param->value.s); + } + param = cm_tdbusm_find_dict_entry(d, CM_DBUS_PROP_TEMPLATE_CHALLENGE_PASSWORD, cm_tdbusm_dict_s); if ((param != NULL) && @@ -3359,6 +3367,17 @@ request_modify(DBusConnection *conn, DBusMessage *msg, } } else if ((param->value_type == cm_tdbusm_dict_s) && + (strcasecmp(param->key, CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE) == 0)) { + talloc_free(entry->cm_template_certificate_template); + // TODO check validity + entry->cm_template_certificate_template = + maybe_strdup(entry, param->value.s); + if (n_propname + 2 < sizeof(propname) / sizeof(propname[0])) { + propname[n_propname++] = + CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE; + } + } else + if ((param->value_type == cm_tdbusm_dict_s) && (strcasecmp(param->key, CM_DBUS_PROP_TEMPLATE_CHALLENGE_PASSWORD) == 0)) { talloc_free(entry->cm_template_challenge_password); entry->cm_template_challenge_password = maybe_strdup(entry, @@ -6773,6 +6792,14 @@ cm_tdbush_iface_request(void) NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), make_interface_item(cm_tdbush_interface_property, + make_property(CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE, + cm_tdbush_property_string, + cm_tdbush_property_readwrite, + cm_tdbush_property_char_p, + offsetof(struct cm_store_entry, cm_template_certificate_template), + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL), + make_interface_item(cm_tdbush_interface_property, make_property(CM_DBUS_PROP_TEMPLATE_NS_CERTTYPE, cm_tdbush_property_string, cm_tdbush_property_readwrite, @@ -7217,7 +7244,7 @@ cm_tdbush_iface_request(void) make_interface_item(cm_tdbush_interface_signal, make_signal(CM_DBUS_SIGNAL_REQUEST_CERT_SAVED, NULL), - NULL)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))); + NULL))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))); } return ret; } diff --git a/tests/028-dbus/expected.out b/tests/028-dbus/expected.out index 93cc4d1..8a81a7f 100644 --- a/tests/028-dbus/expected.out +++ b/tests/028-dbus/expected.out @@ -330,6 +330,7 @@ OK + From 69df49010c6e610e87aa8f21a344dbfa424e550e Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Aug 24 2017 22:25:54 +0000 Subject: [PATCH 2/5] MS cert template: add template extension to CSR Add the MS V2 certificate template extension to the CSR, when the attribute is set. Failure to parse the value (as stored) merely causes the extension to be skipped. Part of: https://pagure.io/certmonger/issue/78 --- diff --git a/src/certext.c b/src/certext.c index a872443..def1948 100644 --- a/src/certext.c +++ b/src/certext.c @@ -69,7 +69,7 @@ struct kerberos_principal_name { struct ms_template { SECItem id; SECItem major; - SECItem *minor; + SECItem minor; }; /* KerberosString: RFC 4120, 5.2.1 */ @@ -180,7 +180,7 @@ cm_ms_template_template[] = { .kind = SEC_ASN1_SEQUENCE, .offset = 0, .sub = NULL, - .size = sizeof(struct kerberos_principal_name), + .size = sizeof(struct ms_template), }, { .kind = SEC_ASN1_OBJECT_ID, @@ -1601,6 +1601,76 @@ cm_certext_build_profile(struct cm_store_entry *entry, return item; } +/* Build a Microsoft certificate template extension value. */ +static SECItem * +cm_certext_build_certificate_template( + PLArenaPool *arena, + char *template_spec) +{ + struct ms_template template_data; + memset(&template_data, 0, sizeof(struct ms_template)); + + if (NULL == template_spec || *template_spec == '\0') + return NULL; + + /* strtok overwrites delimiters with null bytes; + * therefore duplicate the input string */ + char *template_spec_dup = PORT_ArenaStrdup(arena, template_spec); + if (NULL == template_spec_dup) + return NULL; + + int i = 0; + char *saveptr, *endptr; + for ( + char *part = strtok_r(template_spec_dup, ":", &saveptr); + part != NULL; + part = strtok_r(NULL, ":", &saveptr) + ) { + if (i == 0) { + // parse OID + if (SECSuccess != SEC_StringToOID(arena, &template_data.id, part, 0)) + return NULL; + } + else if (i == 1) { + // parse major version + long x = strtol(part, &endptr, 10); + if (*part == '\0' || *endptr != '\0') { + // string was empty or contained non-digits + return NULL; + } + if (SEC_ASN1EncodeInteger(arena, &template_data.major, x) + != &template_data.major) + return NULL; + } + else if (i == 2) { + // parse minor version + long x = strtol(part, &endptr, 10); + if (*part == '\0' || *endptr != '\0') { + // string was empty or contained non-digits + return NULL; + } + if (SEC_ASN1EncodeInteger(arena, &template_data.minor, x) + != &template_data.minor) + return NULL; + } + else { + // there are too many parts! + return NULL; + } + i++; + } + if (i < 2) { + // there are too few parts! (OID and major version are required) + return NULL; + } + + SECItem encoded; + if (SEC_ASN1EncodeItem(arena, &encoded, &template_data, + cm_ms_template_template) != &encoded) + return NULL; + return SECITEM_ArenaDupItem(arena, &encoded); +} + /* Build a Netscape certtype extension value. */ static SECItem * cm_certext_build_ns_certtype(struct cm_store_entry *entry, @@ -1848,6 +1918,18 @@ cm_certext_build_csr_extensions(struct cm_store_entry *entry, i++; } } + if (entry->cm_template_certificate_template != NULL) { + oid = (SECOidData *) &oid_microsoft_certificate_template; + item = cm_certext_build_certificate_template( + arena, entry->cm_template_certificate_template); + if ((item != NULL) && (oid != NULL)) { + ext[i].id = oid->oid; + ext[i].critical = der_false; + ext[i].value = *item; + exts[i] = &ext[i]; + i++; + } + } if (entry->cm_template_ns_certtype != NULL) { oid = SECOID_FindOIDByTag(SEC_OID_NS_CERT_EXT_CERT_TYPE); item = cm_certext_build_ns_certtype(entry, arena, From 065ed8638504cdd29e1b709d1bee42e831eddec8 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Aug 24 2017 22:25:54 +0000 Subject: [PATCH 3/5] MS cert template: add option to command line programs Add the --ms-template-spec command line argument for specifying the value of the V2 Certificate Template extension. Part of: https://pagure.io/certmonger/issue/78 --- diff --git a/src/getcert-rekey.1.in b/src/getcert-rekey.1.in index ba11bf7..39ba761 100644 --- a/src/getcert-rekey.1.in +++ b/src/getcert-rekey.1.in @@ -61,6 +61,12 @@ the CA should correspond to one listed by \fIgetcert list-cas\fR. Request a certificate using the named profile, template, or certtype, from the specified CA. .TP +\fB\-\-ms-template-spec\fR SPEC +Include a V2 Certificate Template extension in the signing request. +This datum includes an Object Identifier, a major version number +(positive integer) and an optional minor version number. The format +is: \fB:[:]\fR. +.TP \fB\-X\fR NAME Request a certificate using the named issuer from the specified CA. .TP diff --git a/src/getcert-request.1.in b/src/getcert-request.1.in index ed9dd6a..f71ada7 100644 --- a/src/getcert-request.1.in +++ b/src/getcert-request.1.in @@ -88,6 +88,12 @@ the CA should correspond to one listed by \fIgetcert list-cas\fR. Request a certificate using the named profile, template, or certtype, from the specified CA. .TP +\fB\-\-ms-template-spec\fR SPEC +Include a V2 Certificate Template extension in the signing request. +This datum includes an Object Identifier, a major version number +(positive integer) and an optional minor version number. The format +is: \fB:[:]\fR. +.TP \fB\-X\fR NAME Request a certificate using the named issuer from the specified CA. diff --git a/src/getcert-resubmit.1.in b/src/getcert-resubmit.1.in index ec92b6e..f9e6bb1 100644 --- a/src/getcert-resubmit.1.in +++ b/src/getcert-resubmit.1.in @@ -48,6 +48,12 @@ the CA should correspond to one listed by \fIgetcert list-cas\fR. Request a certificate using the named profile, template, or certtype, from the specified CA. .TP +\fB\-\-ms-template-spec\fR SPEC +Include a V2 Certificate Template extension in the signing request. +This datum includes an Object Identifier, a major version number +(positive integer) and an optional minor version number. The format +is: \fB:[:]\fR. +.TP \fB\-X\fR NAME Request a certificate using the named issuer from the specified CA. .TP diff --git a/src/getcert-start-tracking.1.in b/src/getcert-start-tracking.1.in index 75d3a4d..f60e4a7 100644 --- a/src/getcert-start-tracking.1.in +++ b/src/getcert-start-tracking.1.in @@ -86,6 +86,12 @@ useful in combination with \fB\-r\fR. Request a certificate using the named profile, template, or certtype, from the specified CA. .TP +\fB\-\-ms-template-spec\fR SPEC +Include a V2 Certificate Template extension in the signing request. +This datum includes an Object Identifier, a major version number +(positive integer) and an optional minor version number. The format +is: \fB:[:]\fR. +.TP \fB\-X\fR NAME Request a certificate using the named issuer from the specified CA. diff --git a/src/getcert.c b/src/getcert.c index e39a173..5ecd712 100644 --- a/src/getcert.c +++ b/src/getcert.c @@ -748,6 +748,7 @@ request(const char *argv0, int argc, const char **argv) int keysize = 0, auto_renew = 1, verbose = 0, ku = 0, kubit, c, i, j; char *ca = DEFAULT_CA, *subject = NULL, **eku = NULL, *oid, *id = NULL; char *profile = NULL, *issuer = NULL, kustring[16]; + char *ms_template_spec = NULL; char **principal = NULL, **dns = NULL, **email = NULL, **ipaddr = NULL; char *key_owner = NULL, *key_perms = NULL; char *cert_owner = NULL, *cert_perms = NULL; @@ -789,6 +790,7 @@ request(const char *argv0, int argc, const char **argv) {"ca", 'c', POPT_ARG_STRING, &ca, 0, _("use the specified CA configuration rather than the default"), HELP_TYPE_NAME}, #endif {"profile", 'T', POPT_ARG_STRING, NULL, 'T', _("ask the CA to process the request using the named profile or template"), HELP_TYPE_NAME}, + {"ms-template-spec", 0, POPT_ARG_STRING, NULL, 'Y', _("include V2 template specifier in CSR (format: OID:MAJOR-VERSION[:MINOR-VERSION])"), HELP_TYPE_NAME}, {"issuer", 'X', POPT_ARG_STRING, NULL, 'X', _("ask the CA to process the request using the named issuer"), HELP_TYPE_NAME}, {"subject-name", 'N', POPT_ARG_STRING, NULL, 'N', _("set requested subject name (default: CN=)"), HELP_TYPE_SUBJECT}, {"key-usage", 'u', POPT_ARG_STRING, NULL, 'u', _("set requested key usage value"), HELP_TYPE_KU}, @@ -919,6 +921,9 @@ request(const char *argv0, int argc, const char **argv) case 'T': profile = talloc_strdup(globals.tctx, poptarg); break; + case 'Y': + ms_template_spec = talloc_strdup(globals.tctx, poptarg); + break; case 'X': issuer = talloc_strdup(globals.tctx, poptarg); break; @@ -1362,6 +1367,13 @@ request(const char *argv0, int argc, const char **argv) params[i] = ¶m[i]; i++; } + if (ms_template_spec != NULL) { + param[i].key = CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE; + param[i].value_type = cm_tdbusm_dict_s; + param[i].value.s = ms_template_spec; + params[i] = ¶m[i]; + i++; + } if (issuer != NULL) { param[i].key = CM_DBUS_PROP_TEMPLATE_ISSUER; param[i].value_type = cm_tdbusm_dict_s; @@ -1561,6 +1573,7 @@ add_basic_request(enum cm_tdbus_type bus, char *id, char *pin, char *pinfile, char *cpass, char *cpassfile, char *ca, char *profile, char *issuer, + char *ms_template_spec, char *precommand, char *postcommand, char **anchor_dbs, char **anchor_files, int is_ca, int path_length, @@ -1735,6 +1748,13 @@ add_basic_request(enum cm_tdbus_type bus, char *id, params[i] = ¶m[i]; i++; } + if (ms_template_spec != NULL) { + param[i].key = CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE; + param[i].value_type = cm_tdbusm_dict_s; + param[i].value.s = ms_template_spec; + params[i] = ¶m[i]; + i++; + } if (issuer != NULL) { param[i].key = CM_DBUS_PROP_TEMPLATE_ISSUER; param[i].value_type = cm_tdbusm_dict_s; @@ -1825,6 +1845,7 @@ set_tracking(const char *argv0, const char *category, char *id = NULL, *new_id = NULL, *new_request; char *keyfile = NULL, *certfile = NULL, *ca = DEFAULT_CA; char *profile = NULL, *issuer = NULL; + char *ms_template_spec = NULL; char *pin = NULL, *pinfile = NULL, *cpass = NULL, *cpassfile = NULL; char *key_owner = NULL, *key_perms = NULL; char *cert_owner = NULL, *cert_perms = NULL; @@ -1866,6 +1887,7 @@ set_tracking(const char *argv0, const char *category, {"ca", 'c', POPT_ARG_STRING, &ca, 0, _("use the specified CA configuration rather than the default"), HELP_TYPE_NAME}, #endif {"profile", 'T', POPT_ARG_STRING, NULL, 'T', _("ask the CA to process the request using the named profile or template"), HELP_TYPE_NAME}, + {"ms-template-spec", 0, POPT_ARG_STRING, NULL, 'Y', _("include V2 template specifier in CSR (format: OID:MAJOR-VERSION[:MINOR-VERSION])"), HELP_TYPE_NAME}, {"issuer", 'X', POPT_ARG_STRING, NULL, 'X', _("ask the CA to process the request using the named issuer"), HELP_TYPE_NAME}, {"key-usage", 'u', POPT_ARG_STRING, NULL, 'u', _("override requested key usage value"), HELP_TYPE_KU}, {"extended-key-usage", 'U', POPT_ARG_STRING, NULL, 'U', _("override requested extended key usage OID"), HELP_TYPE_EKU}, @@ -1971,6 +1993,9 @@ set_tracking(const char *argv0, const char *category, case 'T': profile = talloc_strdup(globals.tctx, poptarg); break; + case 'Y': + ms_template_spec = talloc_strdup(globals.tctx, poptarg); + break; case 'X': issuer = talloc_strdup(globals.tctx, poptarg); break; @@ -2409,6 +2434,7 @@ set_tracking(const char *argv0, const char *category, pin, pinfile, cpass, cpassfile, ca, profile, issuer, + ms_template_spec, precommand, postcommand, anchor_dbs, anchor_files, is_ca, path_length, @@ -2485,6 +2511,7 @@ rekey_or_resubmit(const char *argv0, const char *category, int argc, char *subject = NULL, **eku = NULL, *oid = NULL; char **principal = NULL, **dns = NULL, **email = NULL, **ipaddr = NULL; char *profile = NULL, *issuer = NULL, kustring[16]; + char *ms_template_spec = NULL; char *key_owner = NULL, *key_perms = NULL; char *cert_owner = NULL, *cert_perms = NULL; char *keytype = NULL; @@ -2522,6 +2549,7 @@ rekey_or_resubmit(const char *argv0, const char *category, int argc, {"ca", 'c', POPT_ARG_STRING, &ca, 0, _("use the specified CA configuration rather than the current one"), HELP_TYPE_NAME}, #endif {"profile", 'T', POPT_ARG_STRING, NULL, 'T', _("ask the CA to process the request using the named profile or template"), HELP_TYPE_NAME}, + {"ms-template-spec", 0, POPT_ARG_STRING, NULL, 'Y', _("include V2 template specifier in CSR (format: OID:MAJOR-VERSION[:MINOR-VERSION])"), HELP_TYPE_NAME}, {"issuer", 'X', POPT_ARG_STRING, NULL, 'X', _("ask the CA to process the request using the named issuer"), HELP_TYPE_NAME}, {"subject-name", 'N', POPT_ARG_STRING, NULL, 'N', _("set requested subject name (default: CN=)"), HELP_TYPE_SUBJECT}, {"key-usage", 'u', POPT_ARG_STRING, NULL, 'u', _("set requested key usage value"), HELP_TYPE_KU}, @@ -2600,6 +2628,9 @@ rekey_or_resubmit(const char *argv0, const char *category, int argc, case 'T': profile = talloc_strdup(globals.tctx, poptarg); break; + case 'Y': + ms_template_spec = talloc_strdup(globals.tctx, poptarg); + break; case 'X': issuer = talloc_strdup(globals.tctx, poptarg); break; @@ -2975,6 +3006,13 @@ rekey_or_resubmit(const char *argv0, const char *category, int argc, params[i] = ¶m[i]; i++; } + if (ms_template_spec != NULL) { + param[i].key = CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE; + param[i].value_type = cm_tdbusm_dict_s; + param[i].value.s = ms_template_spec; + params[i] = ¶m[i]; + i++; + } if (issuer != NULL) { param[i].key = CM_DBUS_PROP_TEMPLATE_ISSUER; param[i].value_type = cm_tdbusm_dict_s; @@ -4816,6 +4854,8 @@ help(const char *twopartcmd, const char *category) N_(" -c CA use the specified CA rather than the default\n"), #endif N_(" -T PROFILE ask the CA to process the request using the named profile or template\n"), + N_(" --ms-template-spec SPEC\n"), + N_(" include V2 template specifier in CSR (format: OID:MAJOR-VERSION[:MINOR-VERSION])\n"), N_(" -X ISSUER ask the CA to process the request using the named issuer\n"), N_("* Parameters for the signing request:\n"), N_(" -N NAME set requested subject name (default: CN=)\n"), @@ -4865,6 +4905,8 @@ help(const char *twopartcmd, const char *category) N_(" -c CA use the specified CA rather than the default\n"), #endif N_(" -T PROFILE ask the CA to process the request using the named profile or template\n"), + N_(" --ms-template-spec SPEC\n"), + N_(" include V2 template specifier in CSR (format: OID:MAJOR-VERSION[:MINOR-VERSION])\n"), N_(" -X ISSUER ask the CA to process the request using the named issuer\n"), N_("* Parameters for the signing request at renewal time:\n"), N_(" -U EXTUSAGE override requested extended key usage OID\n"), @@ -4944,6 +4986,8 @@ help(const char *twopartcmd, const char *category) N_(" -c CA use the specified CA rather than the current one\n"), #endif N_(" -T PROFILE ask the CA to process the request using the named profile or template\n"), + N_(" --ms-template-spec SPEC\n"), + N_(" include V2 template specifier in CSR (format: OID:MAJOR-VERSION[:MINOR-VERSION])\n"), N_(" -X ISSUER ask the CA to process the request using the named issuer\n"), N_("* Bus options:\n"), N_(" -S connect to the certmonger service on the system bus\n"), @@ -4992,6 +5036,8 @@ help(const char *twopartcmd, const char *category) N_(" -c CA use the specified CA rather than the current one\n"), #endif N_(" -T PROFILE ask the CA to process the request using the named profile or template\n"), + N_(" --ms-template-spec SPEC\n"), + N_(" include V2 template specifier in CSR (format: OID:MAJOR-VERSION[:MINOR-VERSION])\n"), N_(" -X ISSUER ask the CA to process the request using the named issuer\n"), N_(" -G TYPE type of new key to be generated\n"), N_(" -g SIZE size of new key to be generated\n"), From 803ac63dd8d7af272af9da689468a3eef76b45bd Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Aug 24 2017 22:25:54 +0000 Subject: [PATCH 4/5] MS cert template: validate argument Update the server to validate the MS V2 certificate template option argument when adding or updating a request. Fixes: https://pagure.io/certmonger/issue/78 --- diff --git a/src/Makefile.am b/src/Makefile.am index 6621703..c871347 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -186,7 +186,7 @@ tdbusm_check_SOURCES = tdbusm-check.c tm.c tm.h tdbusm_check_LDADD = libcm.a $(CERTMONGER_LIBS) $(POPT_LIBS) serial_check_LDADD = libcm.a $(CERTMONGER_LIBS) $(LTLIBICONV) nl_check_LDADD = libcm.a $(CERTMONGER_LIBS) -submit_x_CFLAGS = $(AM_CFLAGS) -DCM_SUBMIT_X_MAIN +submit_x_CFLAGS = $(AM_CFLAGS) $(NSS_CFLAGS) -DCM_SUBMIT_X_MAIN submit_x_SOURCES = submit-x.c submit-x.h submit-u.c submit-u.h log.c log.h \ tm.c tm.h submit_x_LDADD = $(XMLRPC_LIBS) $(KRB5_LIBS) $(TALLOC_LIBS) \ @@ -205,12 +205,14 @@ pkglibexec_PROGRAMS += local-submit pkglibexec_PROGRAMS += scep-submit endif noinst_PROGRAMS += submit-h submit-d +ipa_submit_CFLAGS = $(AM_CFLAGS) $(NSS_CFLAGS) ipa_submit_SOURCES = ipa.c srvloc.c srvloc.h store.h store-gen.c \ submit-x.c submit-x.h submit-u.c submit-u.h \ submit-e.h util.c util.h log.c log.h tm.c tm.h ipa_submit_LDADD = $(XMLRPC_LIBS) $(LDAP_LIBS) $(KRB5_LIBS) $(TALLOC_LIBS) \ $(GMP_LIBS) $(IDN_LIBS) $(OPENSSL_LIBS) $(UUID_LIBS) \ $(RESOLV_LIBS) $(LTLIBICONV) $(POPT_LIBS) +certmaster_submit_CFLAGS = $(AM_CFLAGS) $(NSS_CFLAGS) certmaster_submit_SOURCES = certmaster.c submit-x.c submit-x.h \ submit-e.h submit-u.c submit-u.h util.c util.h log.c log.h \ tm.c tm.h diff --git a/src/certext.c b/src/certext.c index def1948..e5d8065 100644 --- a/src/certext.c +++ b/src/certext.c @@ -1671,6 +1671,19 @@ cm_certext_build_certificate_template( return SECITEM_ArenaDupItem(arena, &encoded); } +/* Validate a V2 template spec */ +PRBool cm_ms_template_valid(char *template_spec) { + PLArenaPool *arena = PORT_NewArena(sizeof(double)); + if (arena == NULL) + return PR_FALSE; + SECItem *result = + cm_certext_build_certificate_template(arena, template_spec); + PORT_FreeArena(arena, PR_FALSE); + // *result has been freed, but we don't read it; + // we only need to know whether the parse succeeded + return result != NULL; +} + /* Build a Netscape certtype extension value. */ static SECItem * cm_certext_build_ns_certtype(struct cm_store_entry *entry, diff --git a/src/certext.h b/src/certext.h index 530ece4..5e95835 100644 --- a/src/certext.h +++ b/src/certext.h @@ -15,6 +15,8 @@ * along with this program. If not, see . */ +#include + #ifndef cmcertext_h #define cmcertext_h @@ -25,4 +27,7 @@ void cm_certext_build_csr_extensions(struct cm_store_entry *entry, struct NSSInitContextStr *ctx, unsigned char **encoded, size_t *length); +/* Validate a V2 template spec */ +PRBool cm_ms_template_valid(char *template_spec); + #endif diff --git a/src/tdbush.c b/src/tdbush.c index 6348cfc..d5febc8 100644 --- a/src/tdbush.c +++ b/src/tdbush.c @@ -32,6 +32,7 @@ #include "log.h" #include "cm.h" +#include "certext.h" #include "prefs.h" #include "store.h" #include "store-int.h" @@ -1572,7 +1573,18 @@ base_add_request(DBusConnection *conn, DBusMessage *msg, CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE, cm_tdbusm_dict_s); if (param != NULL) { - // TODO check validity + if (param->value.s != NULL + && strlen(param->value.s) > 0 + && !cm_ms_template_valid(param->value.s)) { + cm_log(1, "Invalid V2 certificate template specifier: %s", param->value.s); + ret = send_internal_base_bad_arg_error( + conn, msg, + _("Invalid V2 certificate template specifier: %s"), + param->value.s, + CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE); + talloc_free(parent); + return ret; + } new_entry->cm_template_certificate_template = maybe_strdup(new_entry, param->value.s); } @@ -3368,8 +3380,17 @@ request_modify(DBusConnection *conn, DBusMessage *msg, } else if ((param->value_type == cm_tdbusm_dict_s) && (strcasecmp(param->key, CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE) == 0)) { + if (param->value.s != NULL + && strlen(param->value.s) > 0 + && !cm_ms_template_valid(param->value.s)) { + cm_log(1, "Invalid V2 certificate template specifier: %s", param->value.s); + return send_internal_base_bad_arg_error( + conn, msg, + _("Invalid V2 certificate template specifier: %s"), + param->value.s, + CM_DBUS_PROP_TEMPLATE_MS_CERTIFICATE_TEMPLATE); + } talloc_free(entry->cm_template_certificate_template); - // TODO check validity entry->cm_template_certificate_template = maybe_strdup(entry, param->value.s); if (n_propname + 2 < sizeof(propname) / sizeof(propname[0])) { From 1969f51eb73ffabbe592c6b4d2e4be59acca4dd2 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Aug 24 2017 22:25:54 +0000 Subject: [PATCH 5/5] MS cert template: add tests Part of: https://pagure.io/certmonger/issue/78 --- diff --git a/tests/038-ms-v2-template/expected.out b/tests/038-ms-v2-template/expected.out new file mode 100644 index 0000000..7338a5f --- /dev/null +++ b/tests/038-ms-v2-template/expected.out @@ -0,0 +1,19 @@ +[key] +OK. +[csr : bogus oid] +extension not present +[csr : bogus major version] +extension not present +[csr : missing major version] +extension not present +[csr : too many parts] +extension not present +[csr : oid, major version] + 0:d=0 hl=2 l= 8 cons: SEQUENCE + 2:d=1 hl=2 l= 3 prim: OBJECT :1.2.3.4 + 7:d=1 hl=2 l= 1 prim: INTEGER :2A +[csr : oid, major version, minor version] + 0:d=0 hl=2 l= 11 cons: SEQUENCE + 2:d=1 hl=2 l= 3 prim: OBJECT :1.2.3.4 + 7:d=1 hl=2 l= 1 prim: INTEGER :2A + 10:d=1 hl=2 l= 1 prim: INTEGER :11 diff --git a/tests/038-ms-v2-template/extract-extdata.py b/tests/038-ms-v2-template/extract-extdata.py new file mode 100755 index 0000000..cd96f99 --- /dev/null +++ b/tests/038-ms-v2-template/extract-extdata.py @@ -0,0 +1,29 @@ +#!/bin/python2 + +# Given `openssl asn1parse` output of a CSR, look for the V2 Template +# extension and output its data if found. Nonzero exit status if +# not found. + +import binascii +import re +import sys + +STATE_SEARCH, STATE_FOUND, STATE_DONE = range(3) + +state = STATE_SEARCH + +for line in sys.stdin: + if state == STATE_SEARCH and ':1.3.6.1.4.1.311.21.7' in line: + state = STATE_FOUND + continue + + # look for first OCTET STRING once we're in STATE_FOUND + # + if state == STATE_FOUND and 'OCTET STRING' in line: + result = re.search(r'\[HEX DUMP\]:(\w*)', line) + sys.stdout.write(binascii.unhexlify(result.group(1))) + state = STATE_DONE + break + +if state != STATE_DONE: + sys.exit(1) diff --git a/tests/038-ms-v2-template/run.sh b/tests/038-ms-v2-template/run.sh new file mode 100755 index 0000000..0eeb7f9 --- /dev/null +++ b/tests/038-ms-v2-template/run.sh @@ -0,0 +1,54 @@ +#!/bin/bash -e + +srcdir=$PWD +cd $tmpdir + +mkconfig() { + cat > request <<- EOF + key_storage_type=FILE + key_storage_location=$tmpdir/key + cert_storage_type=FILE + cert_storage_location=$tmpdir/cert + template_subject=CN=MS V2 Certificate Template test + EOF +} + +echo "[key]" +mkconfig +$toolsdir/keygen request + +echo "[csr : bogus oid]" +mkconfig +echo "template_certificate_template=NotAnOid:42" >> request +$toolsdir/csrgen request | openssl asn1parse \ + | $srcdir/extract-extdata.py || echo "extension not present" + +echo "[csr : bogus major version]" +mkconfig +echo "template_certificate_template=1.2.3.4:wat" >> request +$toolsdir/csrgen request | openssl asn1parse \ + | $srcdir/extract-extdata.py || echo "extension not present" + +echo "[csr : missing major version]" +mkconfig +echo "template_certificate_template=1.2.3.4" >> request +$toolsdir/csrgen request | openssl asn1parse \ + | $srcdir/extract-extdata.py || echo "extension not present" + +echo "[csr : too many parts]" +mkconfig +echo "template_certificate_template=1.2.3.4:1:1:1" >> request +$toolsdir/csrgen request | openssl asn1parse \ + | $srcdir/extract-extdata.py || echo "extension not present" + +echo "[csr : oid, major version]" +mkconfig +echo "template_certificate_template=1.2.3.4:42" >> request +$toolsdir/csrgen request | openssl asn1parse \ + | $srcdir/extract-extdata.py | openssl asn1parse -inform DER + +echo "[csr : oid, major version, minor version]" +mkconfig +echo "template_certificate_template=1.2.3.4:42:17" >> request +$toolsdir/csrgen request | openssl asn1parse \ + | $srcdir/extract-extdata.py | openssl asn1parse -inform DER diff --git a/tests/Makefile.am b/tests/Makefile.am index 3f85785..4e40743 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -125,7 +125,9 @@ CLEANFILES = \ 036-getcert/actual.out \ 036-getcert/actual.err \ 037-rekey2/actual.out \ - 037-rekey2/actual.err + 037-rekey2/actual.err \ + 038-ms-v2-template/actual.out \ + 038-ms-v2-template/actual.err EXTRA_DIST = \ run-tests.sh functions certmonger.conf tools/cachain.sh \ 001-keyiread/run.sh \ @@ -344,7 +346,10 @@ EXTRA_DIST = \ 036-getcert/expected.out \ 036-getcert/run.sh \ 037-rekey2/expected.out \ - 037-rekey2/run.sh + 037-rekey2/run.sh \ + 038-ms-v2-template/expected.out \ + 038-ms-v2-template/extract-extdata.py \ + 038-ms-v2-template/run.sh subdirs = \ 001-keyiread \ @@ -386,7 +391,8 @@ subdirs = \ 034-perms \ 035-json \ 036-getcert \ - 037-rekey2 + 037-rekey2 \ + 038-ms-v2-template if HAVE_DBM_NSSDB subdirs += \