diff --git a/SOURCES/016-CVE-2020-25654.patch b/SOURCES/016-CVE-2020-25654.patch new file mode 100644 index 0000000..24d85ff --- /dev/null +++ b/SOURCES/016-CVE-2020-25654.patch @@ -0,0 +1,513 @@ +From 3aa33bcc9c70d197b5ed0760b12d65dfab4d4da5 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Fri, 9 Oct 2020 09:56:03 -0500 +Subject: [PATCH 1/7] Log: executor: show CRM_OP_REGISTER rc in debug message + +Previously, process_lrmd_signon() would add the rc to the client reply +but not pass it back to process_lrmd_message(), which would always log "OK" in +its debug message, even if the sign-on was rejected. +--- + daemons/execd/execd_commands.c | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c +index 4d0e457..8487dd4 100644 +--- a/daemons/execd/execd_commands.c ++++ b/daemons/execd/execd_commands.c +@@ -1494,10 +1494,10 @@ free_rsc(gpointer data) + free(rsc); + } + +-static xmlNode * +-process_lrmd_signon(pcmk__client_t *client, xmlNode *request, int call_id) ++static int ++process_lrmd_signon(pcmk__client_t *client, xmlNode *request, int call_id, ++ xmlNode **reply) + { +- xmlNode *reply = NULL; + int rc = pcmk_ok; + const char *is_ipc_provider = crm_element_value(request, F_LRMD_IS_IPC_PROVIDER); + const char *protocol_version = crm_element_value(request, F_LRMD_PROTOCOL_VERSION); +@@ -1508,18 +1508,19 @@ process_lrmd_signon(pcmk__client_t *client, xmlNode *request, int call_id) + rc = -EPROTO; + } + +- reply = create_lrmd_reply(__FUNCTION__, rc, call_id); +- crm_xml_add(reply, F_LRMD_OPERATION, CRM_OP_REGISTER); +- crm_xml_add(reply, F_LRMD_CLIENTID, client->id); +- crm_xml_add(reply, F_LRMD_PROTOCOL_VERSION, LRMD_PROTOCOL_VERSION); +- + if (crm_is_true(is_ipc_provider)) { + // This is a remote connection from a cluster node's controller + #ifdef SUPPORT_REMOTE + ipc_proxy_add_provider(client); + #endif + } +- return reply; ++ ++ *reply = create_lrmd_reply(__func__, rc, call_id); ++ crm_xml_add(*reply, F_LRMD_OPERATION, CRM_OP_REGISTER); ++ crm_xml_add(*reply, F_LRMD_CLIENTID, client->id); ++ crm_xml_add(*reply, F_LRMD_PROTOCOL_VERSION, LRMD_PROTOCOL_VERSION); ++ ++ return rc; + } + + static int +@@ -1832,7 +1833,7 @@ process_lrmd_message(pcmk__client_t *client, uint32_t id, xmlNode *request) + #endif + do_reply = 1; + } else if (crm_str_eq(op, CRM_OP_REGISTER, TRUE)) { +- reply = process_lrmd_signon(client, request, call_id); ++ rc = process_lrmd_signon(client, request, call_id, &reply); + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_RSC_REG, TRUE)) { + rc = process_lrmd_rsc_register(client, id, request); +-- +1.8.3.1 + + +From d0002343faa4595e42b790119b7f3037db1130c4 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Fri, 9 Oct 2020 15:16:39 -0500 +Subject: [PATCH 2/7] Low: executor: mark controller connections to + pacemaker-remoted as privileged + +Previously, pcmk__client_privileged was only set when local clients connected +(as root or hacluster). Now, set it when pacemaker-remoted successfully +completes the TLS handshake with a remote client (i.e., the controller on a +cluster node). + +This has no effect as of this commit but will with later commits. +--- + daemons/execd/remoted_tls.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/daemons/execd/remoted_tls.c b/daemons/execd/remoted_tls.c +index 1a1f8b2..c835549 100644 +--- a/daemons/execd/remoted_tls.c ++++ b/daemons/execd/remoted_tls.c +@@ -72,6 +72,11 @@ remoted__read_handshake_data(pcmk__client_t *client) + client->remote->tls_handshake_complete = TRUE; + crm_notice("Remote client connection accepted"); + ++ /* Only a client with access to the TLS key can connect, so we can treat ++ * it as privileged. ++ */ ++ set_bit(client->flags, pcmk__client_privileged); ++ + // Alert other clients of the new connection + notify_of_new_client(client); + return 0; +-- +1.8.3.1 + + +From 3db100d775aee214fff8f54eae0076a5fcc41c56 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Thu, 15 Oct 2020 15:33:13 -0500 +Subject: [PATCH 3/7] Low: executor: return appropriate error code when no + remote support + +--- + daemons/execd/execd_commands.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c +index 8487dd4..41c8169 100644 +--- a/daemons/execd/execd_commands.c ++++ b/daemons/execd/execd_commands.c +@@ -1509,9 +1509,11 @@ process_lrmd_signon(pcmk__client_t *client, xmlNode *request, int call_id, + } + + if (crm_is_true(is_ipc_provider)) { +- // This is a remote connection from a cluster node's controller + #ifdef SUPPORT_REMOTE ++ // This is a remote connection from a cluster node's controller + ipc_proxy_add_provider(client); ++#else ++ rc = -EPROTONOSUPPORT; + #endif + } + +@@ -1830,6 +1832,8 @@ process_lrmd_message(pcmk__client_t *client, uint32_t id, xmlNode *request) + if (crm_str_eq(op, CRM_OP_IPC_FWD, TRUE)) { + #ifdef SUPPORT_REMOTE + ipc_proxy_forward_client(client, request); ++#else ++ rc = -EPROTONOSUPPORT; + #endif + do_reply = 1; + } else if (crm_str_eq(op, CRM_OP_REGISTER, TRUE)) { +-- +1.8.3.1 + + +From f273f1c16f21ff96983797ed5ceb2978dafe545a Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Thu, 15 Oct 2020 15:33:57 -0500 +Subject: [PATCH 4/7] High: executor: restrict certain IPC requests to + Pacemaker daemons + +The executor IPC API allows clients to register resources, request agent +execution, and so forth. + +If ACLs are enabled, this could allow an ACL-restricted user to bypass ACLs and +execute any code as root. (If ACLs are not enabled, users in the haclient group +have full access to the CIB, which already gives them that ability, so there is +no additional exposure in that case.) + +When ACLs are supported, this commit effectively disables the executor IPC API +for clients that aren't connecting as root or hacluster. Such clients can only +register and poke now. +--- + daemons/execd/execd_commands.c | 91 +++++++++++++++++++++++++++++++++--------- + 1 file changed, 73 insertions(+), 18 deletions(-) + +diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c +index 41c8169..207eb6a 100644 +--- a/daemons/execd/execd_commands.c ++++ b/daemons/execd/execd_commands.c +@@ -1510,8 +1510,12 @@ process_lrmd_signon(pcmk__client_t *client, xmlNode *request, int call_id, + + if (crm_is_true(is_ipc_provider)) { + #ifdef SUPPORT_REMOTE +- // This is a remote connection from a cluster node's controller +- ipc_proxy_add_provider(client); ++ if ((client->remote != NULL) && client->remote->tls_handshake_complete) { ++ // This is a remote connection from a cluster node's controller ++ ipc_proxy_add_provider(client); ++ } else { ++ rc = -EACCES; ++ } + #else + rc = -EPROTONOSUPPORT; + #endif +@@ -1826,12 +1830,26 @@ process_lrmd_message(pcmk__client_t *client, uint32_t id, xmlNode *request) + int do_notify = 0; + xmlNode *reply = NULL; + ++ bool allowed = true; ++ ++#if ENABLE_ACL ++ /* Certain IPC commands may be done only by privileged users (i.e. root or ++ * hacluster) when ACLs are enabled, because they would otherwise provide a ++ * means of bypassing ACLs. ++ */ ++ allowed = is_set(client->flags, pcmk__client_privileged); ++#endif ++ + crm_trace("Processing %s operation from %s", op, client->id); + crm_element_value_int(request, F_LRMD_CALLID, &call_id); + + if (crm_str_eq(op, CRM_OP_IPC_FWD, TRUE)) { + #ifdef SUPPORT_REMOTE +- ipc_proxy_forward_client(client, request); ++ if (allowed) { ++ ipc_proxy_forward_client(client, request); ++ } else { ++ rc = -EACCES; ++ } + #else + rc = -EPROTONOSUPPORT; + #endif +@@ -1840,38 +1858,70 @@ process_lrmd_message(pcmk__client_t *client, uint32_t id, xmlNode *request) + rc = process_lrmd_signon(client, request, call_id, &reply); + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_RSC_REG, TRUE)) { +- rc = process_lrmd_rsc_register(client, id, request); +- do_notify = 1; ++ if (allowed) { ++ rc = process_lrmd_rsc_register(client, id, request); ++ do_notify = 1; ++ } else { ++ rc = -EACCES; ++ } + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_RSC_INFO, TRUE)) { +- reply = process_lrmd_get_rsc_info(request, call_id); ++ if (allowed) { ++ reply = process_lrmd_get_rsc_info(request, call_id); ++ } else { ++ rc = -EACCES; ++ } + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_RSC_UNREG, TRUE)) { +- rc = process_lrmd_rsc_unregister(client, id, request); +- /* don't notify anyone about failed un-registers */ +- if (rc == pcmk_ok || rc == -EINPROGRESS) { +- do_notify = 1; ++ if (allowed) { ++ rc = process_lrmd_rsc_unregister(client, id, request); ++ /* don't notify anyone about failed un-registers */ ++ if (rc == pcmk_ok || rc == -EINPROGRESS) { ++ do_notify = 1; ++ } ++ } else { ++ rc = -EACCES; + } + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_RSC_EXEC, TRUE)) { +- rc = process_lrmd_rsc_exec(client, id, request); ++ if (allowed) { ++ rc = process_lrmd_rsc_exec(client, id, request); ++ } else { ++ rc = -EACCES; ++ } + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_RSC_CANCEL, TRUE)) { +- rc = process_lrmd_rsc_cancel(client, id, request); ++ if (allowed) { ++ rc = process_lrmd_rsc_cancel(client, id, request); ++ } else { ++ rc = -EACCES; ++ } + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_POKE, TRUE)) { + do_notify = 1; + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_CHECK, TRUE)) { +- xmlNode *data = get_message_xml(request, F_LRMD_CALLDATA); +- const char *timeout = crm_element_value(data, F_LRMD_WATCHDOG); +- CRM_LOG_ASSERT(data != NULL); +- pcmk__valid_sbd_timeout(timeout); ++ if (allowed) { ++ xmlNode *data = get_message_xml(request, F_LRMD_CALLDATA); ++ ++ CRM_LOG_ASSERT(data != NULL); ++ pcmk__valid_sbd_timeout(crm_element_value(data, F_LRMD_WATCHDOG)); ++ } else { ++ rc = -EACCES; ++ } + } else if (crm_str_eq(op, LRMD_OP_ALERT_EXEC, TRUE)) { +- rc = process_lrmd_alert_exec(client, id, request); ++ if (allowed) { ++ rc = process_lrmd_alert_exec(client, id, request); ++ } else { ++ rc = -EACCES; ++ } + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_GET_RECURRING, TRUE)) { +- reply = process_lrmd_get_recurring(request, call_id); ++ if (allowed) { ++ reply = process_lrmd_get_recurring(request, call_id); ++ } else { ++ rc = -EACCES; ++ } + do_reply = 1; + } else { + rc = -EOPNOTSUPP; +@@ -1879,6 +1929,11 @@ process_lrmd_message(pcmk__client_t *client, uint32_t id, xmlNode *request) + crm_err("Unknown IPC request '%s' from %s", op, client->name); + } + ++ if (rc == -EACCES) { ++ crm_warn("Rejecting IPC request '%s' from unprivileged client %s", ++ op, pcmk__client_name(client)); ++ } ++ + crm_debug("Processed %s operation from %s: rc=%d, reply=%d, notify=%d", + op, client->id, rc, do_reply, do_notify); + +-- +1.8.3.1 + + +From f13759f6971402dac3bea1aac45214a84d838728 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Fri, 9 Oct 2020 11:16:43 -0500 +Subject: [PATCH 5/7] Low: pacemakerd: check client for NULL before using it + +... to guard against bugs in client tracking +--- + daemons/pacemakerd/pacemakerd.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c +index 5ed4626..573ea5a 100644 +--- a/daemons/pacemakerd/pacemakerd.c ++++ b/daemons/pacemakerd/pacemakerd.c +@@ -553,9 +553,12 @@ pcmk_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size) + uint32_t id = 0; + uint32_t flags = 0; + const char *task = NULL; ++ xmlNode *msg = NULL; + pcmk__client_t *c = pcmk__find_client(qbc); +- xmlNode *msg = pcmk__client_data2xml(c, data, &id, &flags); + ++ CRM_CHECK(c != NULL, return 0); ++ ++ msg = pcmk__client_data2xml(c, data, &id, &flags); + pcmk__ipc_send_ack(c, id, flags, "ack"); + if (msg == NULL) { + return 0; +-- +1.8.3.1 + + +From 021081c1e28b254a0f68143fa55e517f0fcc4edb Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Fri, 9 Oct 2020 11:17:18 -0500 +Subject: [PATCH 6/7] High: pacemakerd: ignore shutdown requests from + unprivileged users + +The pacemakerd IPC API supports a shutdown request, along with a +command-line interface for using it (pacemakerd --shutdown). + +Only the haclient group has access to the IPC. Without ACLs, that group can +already shut down Pacemaker via the CIB, so there's no security implication. + +However, it might not be desired to allow ACL-restricted users to shut down +Pacemaker, so block users other than root or hacluster if ACLs are supported. +--- + daemons/pacemakerd/pacemakerd.c | 24 ++++++++++++++++++++---- + 1 file changed, 20 insertions(+), 4 deletions(-) + +diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c +index 573ea5a..2e69bd1 100644 +--- a/daemons/pacemakerd/pacemakerd.c ++++ b/daemons/pacemakerd/pacemakerd.c +@@ -566,9 +566,26 @@ pcmk_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size) + + task = crm_element_value(msg, F_CRM_TASK); + if (crm_str_eq(task, CRM_OP_QUIT, TRUE)) { +- crm_notice("Shutting down in response to IPC request %s from %s", +- crm_element_value(msg, F_CRM_REFERENCE), crm_element_value(msg, F_CRM_ORIGIN)); +- pcmk_shutdown(15); ++ bool allowed = true; ++ ++#if ENABLE_ACL ++ /* Only allow privileged users (i.e. root or hacluster) ++ * to shut down Pacemaker from the command line (or direct IPC). ++ * ++ * We only check when ACLs are enabled, because without them, any client ++ * with IPC access could shut down Pacemaker via the CIB anyway. ++ */ ++ allowed = is_set(c->flags, pcmk__client_privileged); ++#endif ++ if (allowed) { ++ crm_notice("Shutting down in response to IPC request %s from %s", ++ crm_element_value(msg, F_CRM_REFERENCE), ++ crm_element_value(msg, F_CRM_ORIGIN)); ++ pcmk_shutdown(15); ++ } else { ++ crm_warn("Ignoring shutdown request from unprivileged client %s", ++ pcmk__client_name(c)); ++ } + + } else if (crm_str_eq(task, CRM_OP_RM_NODE_CACHE, TRUE)) { + crm_trace("Ignoring IPC request to purge node " +-- +1.8.3.1 + + +From 80eb5ddfd529be02214f38669f1b177535186fbc Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Fri, 9 Oct 2020 11:55:26 -0500 +Subject: [PATCH 7/7] Fix: fencer: restrict certain IPC requests to privileged + users + +The fencer IPC API allows clients to register fence devices. + +If ACLs are enabled, this could allow an ACL-restricted user to bypass ACLs to +configure fencing. If the user is able to install executables to the standard +fencing agent locations, have arbitrary code executed as root (the standard +locations generally require root for write access, so that is unlikely to be an +issue). + +If ACLs are not enabled, users in the haclient group have full access to the +CIB, which already gives them these capabilities, so there is no additional +exposure in that case. + +This commit does not restrict unprivileged users from using other fencing API, +such as requesting actual fencing. +--- + daemons/fenced/fenced_commands.c | 41 ++++++++++++++++++++++++++++++++++++---- + 1 file changed, 37 insertions(+), 4 deletions(-) + +diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c +index 859e7b7..a8c90a6 100644 +--- a/daemons/fenced/fenced_commands.c ++++ b/daemons/fenced/fenced_commands.c +@@ -2531,6 +2531,18 @@ handle_request(pcmk__client_t *client, uint32_t id, uint32_t flags, + const char *op = crm_element_value(request, F_STONITH_OPERATION); + const char *client_id = crm_element_value(request, F_STONITH_CLIENTID); + ++ bool allowed = true; ++ ++#if ENABLE_ACL ++ /* IPC commands related to fencing configuration may be done only by ++ * privileged users (i.e. root or hacluster) when ACLs are supported, ++ * because all other users should go through the CIB to have ACLs applied. ++ */ ++ if (client != NULL) { ++ allowed = is_set(client->flags, pcmk__client_privileged); ++ } ++#endif ++ + crm_element_value_int(request, F_STONITH_CALLOPTS, &call_options); + + if (is_set(call_options, st_opt_sync_call)) { +@@ -2687,27 +2699,43 @@ handle_request(pcmk__client_t *client, uint32_t id, uint32_t flags, + } else if (crm_str_eq(op, STONITH_OP_DEVICE_ADD, TRUE)) { + const char *device_id = NULL; + +- rc = stonith_device_register(request, &device_id, FALSE); ++ if (allowed) { ++ rc = stonith_device_register(request, &device_id, FALSE); ++ } else { ++ rc = -EACCES; ++ } + do_stonith_notify_device(call_options, op, rc, device_id); + + } else if (crm_str_eq(op, STONITH_OP_DEVICE_DEL, TRUE)) { + xmlNode *dev = get_xpath_object("//" F_STONITH_DEVICE, request, LOG_ERR); + const char *device_id = crm_element_value(dev, XML_ATTR_ID); + +- rc = stonith_device_remove(device_id, FALSE); ++ if (allowed) { ++ rc = stonith_device_remove(device_id, FALSE); ++ } else { ++ rc = -EACCES; ++ } + do_stonith_notify_device(call_options, op, rc, device_id); + + } else if (crm_str_eq(op, STONITH_OP_LEVEL_ADD, TRUE)) { + char *device_id = NULL; + +- rc = stonith_level_register(request, &device_id); ++ if (allowed) { ++ rc = stonith_level_register(request, &device_id); ++ } else { ++ rc = -EACCES; ++ } + do_stonith_notify_level(call_options, op, rc, device_id); + free(device_id); + + } else if (crm_str_eq(op, STONITH_OP_LEVEL_DEL, TRUE)) { + char *device_id = NULL; + +- rc = stonith_level_remove(request, &device_id); ++ if (allowed) { ++ rc = stonith_level_remove(request, &device_id); ++ } else { ++ rc = -EACCES; ++ } + do_stonith_notify_level(call_options, op, rc, device_id); + + } else if(safe_str_eq(op, CRM_OP_RM_NODE_CACHE)) { +@@ -2727,6 +2755,11 @@ handle_request(pcmk__client_t *client, uint32_t id, uint32_t flags, + + done: + ++ if (rc == -EACCES) { ++ crm_warn("Rejecting IPC request '%s' from unprivileged client %s", ++ crm_str(op), pcmk__client_name(client)); ++ } ++ + /* Always reply unless the request is in process still. + * If in progress, a reply will happen async after the request + * processing is finished */ +-- +1.8.3.1 + diff --git a/SPECS/pacemaker.spec b/SPECS/pacemaker.spec index b33bb5f..6290d45 100644 --- a/SPECS/pacemaker.spec +++ b/SPECS/pacemaker.spec @@ -227,7 +227,7 @@ Name: pacemaker Summary: Scalable High-Availability cluster resource manager Version: %{pcmkversion} -Release: %{pcmk_release}%{?dist} +Release: %{pcmk_release}%{?dist}.1 %if %{defined _unitdir} License: GPLv2+ and LGPLv2+ %else @@ -264,6 +264,7 @@ Patch12: 012-ipc_fix.patch Patch13: 013-pacemakerd.patch Patch14: 014-sbd.patch Patch15: 015-cibsecret.patch +Patch16: 016-CVE-2020-25654.patch # downstream-only commits #Patch100: xxx.patch @@ -961,6 +962,10 @@ exit 0 %license %{nagios_name}-%{nagios_hash}/COPYING %changelog +* Mon Oct 26 2020 Ken Gaillot - 2.0.4-6.1 +- Prevent users from bypassing ACLs by using IPC directly (CVE-2020-25654) +- Resolves: rhbz1891528 + * Thu Aug 20 2020 Ken Gaillot - 2.0.4-6 - Fix cibsecret bug when node name is different from hostname - Resolves: rhbz1870873