From 3523ad7b8b2349ed4ee301b992797902b7288028 Mon Sep 17 00:00:00 2001
From: Trevor Vaughan <tvaughan@onyxpoint.com>
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