summaryrefslogtreecommitdiffstats
path: root/lib/common/cib_secrets.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common/cib_secrets.c')
-rw-r--r--lib/common/cib_secrets.c192
1 files changed, 192 insertions, 0 deletions
diff --git a/lib/common/cib_secrets.c b/lib/common/cib_secrets.c
new file mode 100644
index 0000000..2f71c98
--- /dev/null
+++ b/lib/common/cib_secrets.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2011-2020 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include <crm/common/util.h>
+
+static int is_magic_value(char *p);
+static bool check_md5_hash(char *hash, char *value);
+static void add_secret_params(gpointer key, gpointer value, gpointer user_data);
+static char *read_local_file(char *local_file);
+
+#define MAX_VALUE_LEN 255
+#define MAGIC "lrm://"
+
+static int
+is_magic_value(char *p)
+{
+ return !strcmp(p, MAGIC);
+}
+
+static bool
+check_md5_hash(char *hash, char *value)
+{
+ bool rc = false;
+ char *hash2 = NULL;
+
+ hash2 = crm_md5sum(value);
+ crm_debug("hash: %s, calculated hash: %s", hash, hash2);
+ if (pcmk__str_eq(hash, hash2, pcmk__str_casei)) {
+ rc = true;
+ }
+ free(hash2);
+ return rc;
+}
+
+static char *
+read_local_file(char *local_file)
+{
+ FILE *fp = fopen(local_file, "r");
+ char buf[MAX_VALUE_LEN+1];
+ char *p;
+
+ if (!fp) {
+ if (errno != ENOENT) {
+ crm_perror(LOG_ERR, "cannot open %s" , local_file);
+ }
+ return NULL;
+ }
+
+ if (!fgets(buf, MAX_VALUE_LEN, fp)) {
+ crm_perror(LOG_ERR, "cannot read %s", local_file);
+ fclose(fp);
+ return NULL;
+ }
+ fclose(fp);
+
+ // Strip trailing white space
+ for (p = buf + strlen(buf) - 1; (p >= buf) && isspace(*p); p--);
+ *(p+1) = '\0';
+ return strdup(buf);
+}
+
+/*!
+ * \internal
+ * \brief Read secret parameter values from file
+ *
+ * Given a table of resource parameters, if any of their values are the
+ * magic string indicating a CIB secret, replace that string with the
+ * secret read from the file appropriate to the given resource.
+ *
+ * \param[in] rsc_id Resource whose parameters are being checked
+ * \param[in,out] params Resource parameters to check
+ *
+ * \return Standard Pacemaker return code
+ */
+int
+pcmk__substitute_secrets(const char *rsc_id, GHashTable *params)
+{
+ char local_file[FILENAME_MAX+1], *start_pname;
+ char hash_file[FILENAME_MAX+1], *hash;
+ GList *secret_params = NULL, *l;
+ char *key, *pvalue, *secret_value;
+ int rc = pcmk_rc_ok;
+
+ if (params == NULL) {
+ return pcmk_rc_ok;
+ }
+
+ /* secret_params could be cached with the resource;
+ * there are also parameters sent with operations
+ * which cannot be cached
+ */
+ g_hash_table_foreach(params, add_secret_params, &secret_params);
+ if (secret_params == NULL) { // No secret parameters found
+ return pcmk_rc_ok;
+ }
+
+ crm_debug("Replace secret parameters for resource %s", rsc_id);
+
+ if (snprintf(local_file, FILENAME_MAX, LRM_CIBSECRETS_DIR "/%s/", rsc_id)
+ > FILENAME_MAX) {
+ crm_err("Can't replace secret parameters for %s: file name size exceeded",
+ rsc_id);
+ return ENAMETOOLONG;
+ }
+ start_pname = local_file + strlen(local_file);
+
+ for (l = g_list_first(secret_params); l; l = g_list_next(l)) {
+ key = (char *)(l->data);
+ pvalue = g_hash_table_lookup(params, key);
+ if (!pvalue) { /* this cannot really happen */
+ crm_err("odd, no parameter %s for rsc %s found now", key, rsc_id);
+ continue;
+ }
+
+ if ((strlen(key) + strlen(local_file)) >= FILENAME_MAX-2) {
+ crm_err("%s: parameter name %s too big", rsc_id, key);
+ rc = ENAMETOOLONG;
+ continue;
+ }
+
+ strcpy(start_pname, key);
+ secret_value = read_local_file(local_file);
+ if (!secret_value) {
+ crm_err("secret for rsc %s parameter %s not found in %s",
+ rsc_id, key, LRM_CIBSECRETS_DIR);
+ rc = ENOENT;
+ continue;
+ }
+
+ strcpy(hash_file, local_file);
+ if (strlen(hash_file) + 5 > FILENAME_MAX) {
+ crm_err("cannot build such a long name "
+ "for the sign file: %s.sign", hash_file);
+ free(secret_value);
+ rc = ENAMETOOLONG;
+ continue;
+
+ } else {
+ strcat(hash_file, ".sign");
+ hash = read_local_file(hash_file);
+ if (hash == NULL) {
+ crm_err("md5 sum for rsc %s parameter %s "
+ "cannot be read from %s", rsc_id, key, hash_file);
+ free(secret_value);
+ rc = ENOENT;
+ continue;
+
+ } else if (!check_md5_hash(hash, secret_value)) {
+ crm_err("md5 sum for rsc %s parameter %s "
+ "does not match", rsc_id, key);
+ free(secret_value);
+ free(hash);
+ rc = pcmk_rc_cib_corrupt;
+ continue;
+ }
+ free(hash);
+ }
+ g_hash_table_replace(params, strdup(key), secret_value);
+ }
+ g_list_free(secret_params);
+ return rc;
+}
+
+static void
+add_secret_params(gpointer key, gpointer value, gpointer user_data)
+{
+ GList **lp = (GList **)user_data;
+
+ if (is_magic_value((char *)value)) {
+ *lp = g_list_append(*lp, (char *)key);
+ }
+}