blob: 964f64439841fe3a6adaa9e760c0b5305ebaa559 [file] [log] [blame]
From c6ceceef9a8de3609c2894c30d2df701170e2022 Mon Sep 17 00:00:00 2001
From: Mattias Nissler <mnissler@chromium.org>
Date: Mon, 3 Feb 2020 12:18:28 +0100
Subject: [PATCH] CHROMIUM: compatiblity patches for OpenSSL migration
This adds adjustments to the code to help smoothen migration to
OpenSSL 1.1.1. Specifically:
* If OPENSSL_CHROMIUM_SKIP_TRUSTED_PURPOSE_CHECK is set in the
environment, don't fail if the certificate chain validation finds
an inadequate certificate purpose for a trusted certificate in a
chain. This restores previous OpenSSL behavior.
* If OPENSSL_CHROMIUM_GENERATE_METRICS is set in the environment, generate
metric reports for the condition described above. This will help
quantify the situation in the field in preparation to drop the
compatibility patches eventually.
BUG=chromium:1041803
TEST=network_8021xWiredAuthentication passes, metrics get emitted.
---
crypto/x509/x509_vfy.c | 92 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 92 insertions(+)
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -11,6 +11,11 @@
#include <time.h>
#include <errno.h>
#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include "internal/ctype.h"
#include "internal/cryptlib.h"
@@ -505,6 +508,88 @@ static STACK_OF(X509) *lookup_certs_sk(X509_STORE_CTX *ctx, X509_NAME *nm)
return sk;
}
+/*
+ * Get the process' comm string. Technically this isn't thread safe, but in
+ * practice it is exceedingly unlikely that we run into race conditions here,
+ * and the worst case is that we'll expose a partial comm string, which isn't
+ * the end of the world.
+ */
+static const char* chromium_get_proc_comm() {
+ static char comm[16] = ""; /* TASK_COMM_LEN + null terminator */
+
+ if (!*comm) {
+ char* p = NULL;
+
+ int rlen = 0;
+ BIO* file = BIO_new_file("/proc/self/comm", "r");
+ if (file) {
+ rlen = BIO_read(file, comm, sizeof(comm));
+ BIO_free(file);
+ }
+
+ if (rlen > 0) {
+ /* Last char in comm is '\n', strip it. */
+ comm[rlen - 1] = '\0';
+ for (p = comm; *p; ++p) {
+ if (isalnum(*p)) {
+ *p = tolower(*p);
+ } else {
+ *p = '_';
+ }
+ }
+ } else {
+ strncpy(comm, "UNKNOWN", sizeof(comm) - 1);
+ }
+ }
+
+ return comm;
+}
+
+/*
+ * Generate a UMA metric sample. Instead of dealing with the complications of
+ * linking to the Chromium metrics infrastructure, this just spawns
+ * metrics_client in a child process to record the sample. Note that doing so
+ * requires fork() and exec() to work; specifically, the underlying clone() and
+ * execve() syscalls must not be filtered by seccomp, so only do this if
+ * instructed by an environment variable.
+ */
+static void chromium_maybe_generate_metric_sample(const char* name, int sample,
+ int max)
+{
+ if (getenv("OPENSSL_CHROMIUM_GENERATE_METRICS") == NULL) {
+ return;
+ }
+
+ /* Prepare strings before fork() to avoid potential locking issues. */
+ char metric_name[256] = "";
+ snprintf(metric_name, sizeof(metric_name) - 1, "%s.%s", name,
+ chromium_get_proc_comm());
+ char sample_str[16] = "";
+ snprintf(sample_str, sizeof(sample_str) - 1, "%d", sample);
+ char max_str[16] = "";
+ snprintf(max_str, sizeof(max_str) - 1, "%d", max);
+
+ pid_t child_pid = fork();
+ if (child_pid < 0) {
+ return;
+ }
+
+ if (child_pid > 0) {
+ /* Reap the child to avoid leaving around zombies. */
+ waitpid(child_pid, NULL, 0);
+ return;
+ }
+
+ /* Child process, launch metrics_client. */
+ char* const argv[] = { "metrics_client", metric_name, sample_str, "0",
+ max_str, max_str, NULL };
+ char* const envp[] = { NULL };
+ execve("/usr/bin/metrics_client", argv, envp);
+
+ /* Make sure to always terminate even if execve fails. */
+ abort();
+}
+
/*
* Check EE or CA certificate purpose. For trusted certificates explicit local
* auxiliary trust can be used to override EKU-restrictions.
@@ -553,6 +638,13 @@ static int check_purpose(X509_STORE_CTX *ctx, X509 *x, int purpose, int depth,
break;
}
+ if (depth >= ctx->num_untrusted &&
+ getenv("OPENSSL_CHROMIUM_SKIP_TRUSTED_PURPOSE_CHECK") != NULL) {
+ chromium_maybe_generate_metric_sample(
+ "Platform.OpenSSL.SkipTrustedPurposeCheck", 1, 2);
+ return 1;
+ }
+
return verify_cb_cert(ctx, x, depth, X509_V_ERR_INVALID_PURPOSE);
}
--
2.24.1