From 3523ad7b8b2349ed4ee301b992797902b7288028 Mon Sep 17 00:00:00 2001 From: Trevor Vaughan Date: Fri, 23 Feb 2018 16:11:35 -0500 Subject: [PATCH 22/25] Allow configuration of client SCEP algorithms * Allow users to set `scep_cipher` and `scep_digest` in their CA configuration. These settings are authoritative and will override anything from the server. This was added to support connections to systems, such as Dogtag, that do not provide a CA capabilities string and, therefore, are prone to causing incorrect ciphers to be used on the client side. * In accordance with the latest SCEP Draft RFC, the default cipher has been changed to AES-256 and the default digest has been changed to SHA-256. These were chosen as reasonable defaults for most users and systems. * To ease the determination of which configuration file controls what CA, the output of `getcert list-cas -v` was updated to print a `config-path` entry which will list the specific configuration associated with a given CA. Closes #89 --- src/getcert.c | 6 ++ src/prefs.h | 5 ++ src/scepgen-o.c | 182 ++++++++++++++++++++++++++++++++++++++++++------------ src/store-files.c | 22 +++++++ src/store-int.h | 4 ++ src/tdbus.h | 2 + src/tdbush.c | 149 +++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 331 insertions(+), 39 deletions(-) diff --git a/src/getcert.c b/src/getcert.c index 35fd0d6..724d125 100644 --- a/src/getcert.c +++ b/src/getcert.c @@ -4157,6 +4157,12 @@ list_cas(const char *argv0, int argc, const char **argv) if ((s != NULL) && (strlen(s) > 0)) { printf(_("\tpost-save command: %s\n"), s); } + if (verbose > 0) { + printf(_("\tconfig-path: %s\n"), + query_rep_s(bus, cas[i], CM_DBUS_CA_INTERFACE, + "get_config_file_path", + verbose, globals.tctx)); + } } return 0; } diff --git a/src/prefs.h b/src/prefs.h index 231aea7..349ec64 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -20,9 +20,12 @@ enum cm_prefs_cipher { cm_prefs_aes128, + cm_prefs_aes192, cm_prefs_aes256, cm_prefs_des3, cm_prefs_des, + /* This is for the selection logic */ + cm_prefs_nocipher, }; enum cm_prefs_digest { @@ -31,6 +34,8 @@ enum cm_prefs_digest { cm_prefs_sha512, cm_prefs_sha1, cm_prefs_md5, + /* This is for the selection logic */ + cm_prefs_nodigest, }; enum cm_notification_method; diff --git a/src/scepgen-o.c b/src/scepgen-o.c index d11e3de..07c2b8b 100644 --- a/src/scepgen-o.c +++ b/src/scepgen-o.c @@ -433,49 +433,155 @@ cm_scepgen_o_cooked(struct cm_store_ca *ca, struct cm_store_entry *entry, free(pem); _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - cipher = cm_prefs_des; - for (i = 0; - (ca->cm_ca_capabilities != NULL) && - (ca->cm_ca_capabilities[i] != NULL); - i++) { - capability = ca->cm_ca_capabilities[i]; - if (strcmp(capability, "DES3") == 0) { - cm_log(1, "Server supports DES3, using that.\n"); + + char* scep_cipher = ca->cm_ca_scep_cipher; + if (scep_cipher != NULL) { + /* Force the cipher to whatever is in the configuration */ + if (strcmp(scep_cipher, "AES256") == 0) { + cipher = cm_prefs_aes256; + } + else if (strcmp(scep_cipher, "AES192") == 0) { + cipher = cm_prefs_aes192; + } + else if (strcmp(scep_cipher, "AES128") == 0) { + cipher = cm_prefs_aes128; + } + else if (strcmp(scep_cipher, "DES3") == 0) { cipher = cm_prefs_des3; - break; - } - } - if (cipher == cm_prefs_des) { - cm_log(1, "Server does not support DES3, using DES.\n"); - } - pref_digest = cm_prefs_preferred_digest(); - digest = cm_prefs_md5; - for (i = 0; - (ca->cm_ca_capabilities != NULL) && - (ca->cm_ca_capabilities[i] != NULL); - i++) { - capability = ca->cm_ca_capabilities[i]; - if ((pref_digest == cm_prefs_sha1) && - (strcmp(capability, "SHA-1") == 0)) { - cm_log(1, "Server supports SHA-1, using that.\n"); - digest = cm_prefs_sha1; - break; } - if ((pref_digest == cm_prefs_sha256) && - (strcmp(capability, "SHA-256") == 0)) { - cm_log(1, "Server supports SHA-256, using that.\n"); - digest = cm_prefs_sha256; - break; + else if (strcmp(scep_cipher, "DES") == 0) { + cipher = cm_prefs_des; } - if ((pref_digest == cm_prefs_sha512) && - (strcmp(capability, "SHA-512") == 0)) { - cm_log(1, "Server supports SHA-512, using that.\n"); - digest = cm_prefs_sha512; - break; + else { + cm_log(1, "Option 'scep_cipher' must be one of AES256, AES192, AES128, DES3, or DES. Got '%s'\n", scep_cipher); + _exit(1); + } + + cm_log(1, "SCEP cipher authoritatively set to: '%s'\n", scep_cipher); + } + else { + cipher = cm_prefs_nocipher; + for (i = 0; + (ca->cm_ca_capabilities != NULL) && + (ca->cm_ca_capabilities[i] != NULL); + i++) { + capability = ca->cm_ca_capabilities[i]; + if ((strcmp(capability, "AES-256") == 0) || + (strcmp(capability, "AES256") == 0)) { + cm_log(1, "Server supports AES256, using that.\n"); + cipher = cm_prefs_aes256; + break; + } + if ((strcmp(capability, "AES-192") == 0) || + (strcmp(capability, "AES192") == 0)) { + cm_log(1, "Server supports AES192, using that.\n"); + cipher = cm_prefs_aes192; + break; + } + if ((strcmp(capability, "AES-128") == 0) || + (strcmp(capability, "AES128") == 0)) { + cm_log(1, "Server supports AES128, using that.\n"); + cipher = cm_prefs_aes128; + break; + } + if (strcmp(capability, "AES") == 0) { + cm_log(1, "Server supports AES, using AES256.\n"); + cipher = cm_prefs_aes256; + break; + } + if (strcmp(capability, "DES3") == 0) { + cm_log(1, "Server supports DES3, using that.\n"); + cipher = cm_prefs_des3; + break; + } + /* This remains for backward compatibility */ + if (strcmp(capability, "DES") == 0) { + cm_log(1, "Server supports DES, using that.\n"); + cipher = cm_prefs_des; + break; + } + } + if (cipher == cm_prefs_nocipher) { + /* Per the latest Draft RFC */ + cm_log(1, "Could not determine supported CA capabilities, using AES256.\n"); + cipher = cm_prefs_aes256; } } - if (digest == cm_prefs_md5) { - cm_log(1, "Server does not support better digests, using MD5.\n"); + + char* scep_digest = ca->cm_ca_scep_digest; + if (scep_digest != NULL) { + /* Force the digest to whatever is in the configuration */ + if (strcmp(scep_digest, "SHA512") == 0) { + digest = cm_prefs_sha512; + } + else if (strcmp(scep_digest, "SHA384") == 0) { + digest = cm_prefs_sha384; + } + else if (strcmp(scep_digest, "SHA256") == 0) { + digest = cm_prefs_sha256; + } + else if (strcmp(scep_digest, "SHA1") == 0) { + digest = cm_prefs_sha1; + } + else if (strcmp(scep_digest, "MD5") == 0) { + digest = cm_prefs_md5; + } + else { + cm_log(1, "Option 'scep_digest' must be one of AES256, AES192, AES128, DES3, or DES. Got '%s'\n", scep_digest); + _exit(1); + } + + cm_log(1, "SCEP digest authoritatively set to: '%s'\n", scep_digest); + } + else { + pref_digest = cm_prefs_preferred_digest(); + digest = cm_prefs_nodigest; + for (i = 0; + (ca->cm_ca_capabilities != NULL) && + (ca->cm_ca_capabilities[i] != NULL); + i++) { + capability = ca->cm_ca_capabilities[i]; + if ((pref_digest == cm_prefs_sha512) && + ((strcmp(capability, "SHA-512") == 0) || + (strcmp(capability, "SHA512") == 0))) { + cm_log(1, "Server supports SHA-512, using that.\n"); + digest = cm_prefs_sha512; + break; + } + if ((pref_digest == cm_prefs_sha384) && + ((strcmp(capability, "SHA-384") == 0) || + (strcmp(capability, "SHA384") == 0))) { + cm_log(1, "Server supports SHA-384, using that.\n"); + digest = cm_prefs_sha384; + break; + } + if ((pref_digest == cm_prefs_sha256) && + ((strcmp(capability, "SHA-256") == 0) || + (strcmp(capability, "SHA256") == 0))) { + cm_log(1, "Server supports SHA-256, using that.\n"); + digest = cm_prefs_sha256; + break; + } + if ((pref_digest == cm_prefs_sha1) && + ((strcmp(capability, "SHA-1") == 0) || + (strcmp(capability, "SHA1") == 0))) { + cm_log(1, "Server supports SHA-1, using that.\n"); + digest = cm_prefs_sha1; + break; + } + /* This remains for backward compatibility */ + if ((pref_digest == cm_prefs_sha1) && + (strcmp(capability, "MD5") == 0)) { + cm_log(1, "Server supports MD5, using that.\n"); + digest = cm_prefs_md5; + break; + } + } + if (digest == cm_prefs_nodigest) { + /* Per the latest Draft RFC */ + cm_log(1, "Could not determine supported CA capabilities, using SHA256.\n"); + digest = cm_prefs_sha256; + } } if (old_cert != NULL) { if (cm_pkcs7_envelope_ias(ca->cm_ca_encryption_cert, cipher, diff --git a/src/store-files.c b/src/store-files.c index 977e896..c7195c4 100644 --- a/src/store-files.c +++ b/src/store-files.c @@ -206,6 +206,8 @@ enum cm_store_file_field { cm_store_ca_field_other_cert_nssdbs, cm_store_ca_field_capabilities, + cm_store_ca_field_scep_cipher, + cm_store_ca_field_scep_digest, cm_store_ca_field_scep_ca_identifier, cm_store_ca_field_encryption_cert, cm_store_ca_field_encryption_issuer_cert, @@ -385,6 +387,8 @@ static struct cm_store_file_field_list { {cm_store_ca_field_other_cert_nssdbs, "ca_other_cert_dbs"}, {cm_store_ca_field_capabilities, "ca_capabilities"}, + {cm_store_ca_field_scep_cipher, "scep_cipher"}, + {cm_store_ca_field_scep_digest, "scep_digest"}, {cm_store_ca_field_scep_ca_identifier, "scep_ca_identifier"}, {cm_store_ca_field_encryption_cert, "ca_encryption_cert"}, {cm_store_ca_field_encryption_issuer_cert, "ca_encryption_issuer_cert"}, @@ -725,6 +729,8 @@ cm_store_entry_read(void *parent, const char *filename, FILE *fp) case cm_store_ca_field_other_root_cert_nssdbs: case cm_store_ca_field_other_cert_nssdbs: case cm_store_ca_field_capabilities: + case cm_store_ca_field_scep_cipher: + case cm_store_ca_field_scep_digest: case cm_store_ca_field_scep_ca_identifier: case cm_store_ca_field_encryption_cert: case cm_store_ca_field_encryption_issuer_cert: @@ -1523,6 +1529,14 @@ cm_store_ca_read(void *parent, const char *filename, FILE *fp) ret->cm_ca_capabilities = free_if_empty_multi(ret, p); break; + case cm_store_ca_field_scep_cipher: + ret->cm_ca_scep_cipher = + free_if_empty(p); + break; + case cm_store_ca_field_scep_digest: + ret->cm_ca_scep_digest = + free_if_empty(p); + break; case cm_store_ca_field_scep_ca_identifier: ret->cm_ca_scep_ca_identifier = free_if_empty(p); @@ -2339,6 +2353,10 @@ cm_store_ca_write(FILE *fp, struct cm_store_ca *ca) ca->cm_ca_other_cert_store_nssdbs); cm_store_file_write_strs(fp, cm_store_ca_field_capabilities, ca->cm_ca_capabilities); + cm_store_file_write_str(fp, cm_store_ca_field_scep_cipher, + ca->cm_ca_scep_cipher); + cm_store_file_write_str(fp, cm_store_ca_field_scep_digest, + ca->cm_ca_scep_digest); cm_store_file_write_str(fp, cm_store_ca_field_scep_ca_identifier, ca->cm_ca_scep_ca_identifier); cm_store_file_write_str(fp, cm_store_ca_field_encryption_cert, @@ -2861,6 +2879,10 @@ cm_store_ca_dup(void *parent, struct cm_store_ca *ca) ret->cm_ca_capabilities = cm_store_maybe_strdupv(ret, ca->cm_ca_capabilities); + ret->cm_ca_scep_cipher = + cm_store_maybe_strdup(ret, ca->cm_ca_scep_cipher); + ret->cm_ca_scep_digest = + cm_store_maybe_strdup(ret, ca->cm_ca_scep_digest); ret->cm_ca_scep_ca_identifier = cm_store_maybe_strdup(ret, ca->cm_ca_scep_ca_identifier); ret->cm_ca_encryption_cert = diff --git a/src/store-int.h b/src/store-int.h index 98b37e6..4a40406 100644 --- a/src/store-int.h +++ b/src/store-int.h @@ -349,6 +349,10 @@ struct cm_store_ca { char **cm_ca_other_cert_store_nssdbs; /* CA capabilities. Currently only ever SCEP capabilities. */ char **cm_ca_capabilities; + /* SCEP Cipher to use. Overrides CA Capabilities */ + char *cm_ca_scep_cipher; + /* SCEP Digest to use. Overrides CA Capabilities */ + char *cm_ca_scep_digest; /* An SCEP CA identifier, for use in gathering an RA (and possibly a * CA) certificate. */ char *cm_ca_scep_ca_identifier; diff --git a/src/tdbus.h b/src/tdbus.h index 7164f11..e63e783 100644 --- a/src/tdbus.h +++ b/src/tdbus.h @@ -119,6 +119,8 @@ #define CM_DBUS_PROP_ROOT_CERTS "root-certs" #define CM_DBUS_PROP_OTHER_ROOT_CERTS "root-other-certs" #define CM_DBUS_PROP_OTHER_CERTS "other-certs" +#define CM_DBUS_PROP_SCEP_CIPHER "scep-cipher" +#define CM_DBUS_PROP_SCEP_DIGEST "scep-digest" #define CM_DBUS_PROP_SCEP_CA_IDENTIFIER "scep-ca-identifier" #define CM_DBUS_PROP_SCEP_CA_CAPABILITIES "scep-ca-capabilities" #define CM_DBUS_PROP_SCEP_RA_CERT "scep-ra-cert" diff --git a/src/tdbush.c b/src/tdbush.c index 04fe57e..3ce6c40 100644 --- a/src/tdbush.c +++ b/src/tdbush.c @@ -2128,6 +2128,27 @@ ca_get_serial(DBusConnection *conn, DBusMessage *msg, } } +/* org.fedorahosted.certonger.ca.get_config_file_path */ +ca_get_config_file_path(DBusConnection *conn, DBusMessage *msg, + struct cm_client_info *ci, struct cm_context *ctx) +{ + DBusMessage *rep; + struct cm_store_ca *ca; + ca = get_ca_for_request_message(msg, ctx); + if (ca == NULL) { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + rep = dbus_message_new_method_return(msg); + if (rep != NULL) { + cm_tdbusm_set_s(rep, ca->cm_store_private); + dbus_connection_send(conn, rep, NULL); + dbus_message_unref(rep); + return DBUS_HANDLER_RESULT_HANDLED; + } else { + return send_internal_ca_error(conn, msg); + } +} + /* org.fedorahosted.certonger.ca.refresh */ static DBusHandlerResult ca_refresh(DBusConnection *conn, DBusMessage *msg, @@ -2262,6 +2283,106 @@ ca_prop_set_external_helper(struct cm_context *ctx, void *parent, } static const char * +ca_prop_get_scep_cipher(struct cm_context *ctx, void *parent, + void *record, const char *name) +{ + struct cm_store_ca *ca = record; + + if (strcmp(name, CM_DBUS_PROP_SCEP_CIPHER) == 0) { + if (ca->cm_ca_type != cm_ca_external) { + return ""; + } + if (ca->cm_ca_scep_cipher != NULL) { + return ca->cm_ca_scep_cipher; + } else { + return ""; + } + } + return NULL; +} + +static void +ca_prop_set_scep_cipher(struct cm_context *ctx, void *parent, + void *record, const char *name, + const char *new_value) +{ + const char *propname[2], *path; + struct cm_store_ca *ca = record; + enum cm_ca_phase phase; + + if (strcmp(name, CM_DBUS_PROP_SCEP_CIPHER) == 0) { + if (ca->cm_ca_type != cm_ca_external) { + return; + } + talloc_free(ca->cm_ca_scep_cipher); + ca->cm_ca_scep_cipher = new_value ? + talloc_strdup(ca, new_value) : + NULL; + for (phase = 0; phase < cm_ca_phase_invalid; phase++) { + cm_restart_ca(ctx, ca->cm_nickname, phase); + } + propname[0] = CM_DBUS_PROP_SCEP_CIPHER; + propname[1] = NULL; + path = talloc_asprintf(parent, "%s/%s", + CM_DBUS_CA_PATH, + ca->cm_busname); + cm_tdbush_property_emit_changed(ctx, path, + CM_DBUS_CA_INTERFACE, + propname); + } +} + +static const char * +ca_prop_get_scep_digest(struct cm_context *ctx, void *parent, + void *record, const char *name) +{ + struct cm_store_ca *ca = record; + + if (strcmp(name, CM_DBUS_PROP_SCEP_DIGEST) == 0) { + if (ca->cm_ca_type != cm_ca_external) { + return ""; + } + if (ca->cm_ca_scep_digest != NULL) { + return ca->cm_ca_scep_digest; + } else { + return ""; + } + } + return NULL; +} + +static void +ca_prop_set_scep_digest(struct cm_context *ctx, void *parent, + void *record, const char *name, + const char *new_value) +{ + const char *propname[2], *path; + struct cm_store_ca *ca = record; + enum cm_ca_phase phase; + + if (strcmp(name, CM_DBUS_PROP_SCEP_DIGEST) == 0) { + if (ca->cm_ca_type != cm_ca_external) { + return; + } + talloc_free(ca->cm_ca_scep_digest); + ca->cm_ca_scep_digest = new_value ? + talloc_strdup(ca, new_value) : + NULL; + for (phase = 0; phase < cm_ca_phase_invalid; phase++) { + cm_restart_ca(ctx, ca->cm_nickname, phase); + } + propname[0] = CM_DBUS_PROP_SCEP_DIGEST; + propname[1] = NULL; + path = talloc_asprintf(parent, "%s/%s", + CM_DBUS_CA_PATH, + ca->cm_busname); + cm_tdbush_property_emit_changed(ctx, path, + CM_DBUS_CA_INTERFACE, + propname); + } +} + +static const char * ca_prop_get_scep_ca_identifier(struct cm_context *ctx, void *parent, void *record, const char *name) { @@ -7232,6 +7353,14 @@ cm_tdbush_iface_ca(void) if (ret == NULL) { ret = make_interface(CM_DBUS_CA_INTERFACE, make_interface_item(cm_tdbush_interface_method, + make_method("get_config_file_path", + ca_get_config_file_path, + make_method_arg("path", + DBUS_TYPE_STRING_AS_STRING, + cm_tdbush_method_arg_out, + NULL), + NULL), + make_interface_item(cm_tdbush_interface_method, make_method("get_nickname", ca_get_nickname, make_method_arg("nickname", @@ -7483,6 +7612,24 @@ cm_tdbush_iface_ca(void) NULL, NULL, NULL, NULL, NULL, NULL), make_interface_item(cm_tdbush_interface_property, + make_property(CM_DBUS_PROP_SCEP_CIPHER, + cm_tdbush_property_string, + cm_tdbush_property_readwrite, + cm_tdbush_property_special, + 0, + ca_prop_get_scep_cipher, NULL, NULL, NULL, NULL, + ca_prop_set_scep_cipher, NULL, NULL, NULL, NULL, + NULL), + make_interface_item(cm_tdbush_interface_property, + make_property(CM_DBUS_PROP_SCEP_DIGEST, + cm_tdbush_property_string, + cm_tdbush_property_readwrite, + cm_tdbush_property_special, + 0, + ca_prop_get_scep_digest, NULL, NULL, NULL, NULL, + ca_prop_set_scep_digest, NULL, NULL, NULL, NULL, + NULL), + make_interface_item(cm_tdbush_interface_property, make_property(CM_DBUS_PROP_SCEP_CA_IDENTIFIER, cm_tdbush_property_string, cm_tdbush_property_readwrite, @@ -7527,7 +7674,7 @@ cm_tdbush_iface_ca(void) NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - NULL)))))))))))))))))))))))))))))))))))); + NULL))))))))))))))))))))))))))))))))))))))); } return ret; } -- 1.8.3.1