1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
|
/*
* Copyright 2011-2025 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>
/*!
* \internal
* \brief Read file contents into a string, with trailing whitespace removed
*
* \param[in] filename Name of file to read
*
* \return File contents as a string, or \c NULL on failure or empty file
*
* \note It would be simpler to call \c pcmk__file_contents() directly without
* trimming trailing whitespace. However, this would change hashes for
* existing value files that have trailing whitespace. Similarly, if we
* trim trailing whitespace, it would make sense to trim leading
* whitespace, but this changes existing hashes.
*/
static char *
read_file_trimmed(const char *filename)
{
char *p = NULL;
char *buf = NULL;
int rc = pcmk__file_contents(filename, &buf);
if (rc != pcmk_rc_ok) {
crm_err("Failed to read %s: %s", filename, pcmk_rc_str(rc));
free(buf);
return NULL;
}
if (buf == NULL) {
crm_err("File %s is empty", filename);
return NULL;
}
// Strip trailing white space
for (p = buf + strlen(buf) - 1; (p >= buf) && isspace(*p); p--);
*(p + 1) = '\0';
return buf;
}
/*!
* \internal
* \brief Read checksum from a file and compare against calculated checksum
*
* \param[in] filename File containing stored checksum
* \param[in] secret_value String to calculate checksum from
* \param[in] rsc_id Resource ID (for logging only)
* \param[in] param Parameter name (for logging only)
*
* \return Standard Pacemaker return code
*/
static int
validate_hash(const char *filename, const char *secret_value,
const char *rsc_id, const char *param)
{
char *stored = NULL;
char *calculated = NULL;
int rc = pcmk_rc_ok;
stored = read_file_trimmed(filename);
if (stored == NULL) {
crm_err("Could not read md5 sum for resource %s parameter '%s' from "
"file '%s'",
rsc_id, param, filename);
rc = ENOENT;
goto done;
}
calculated = crm_md5sum(secret_value);
if (calculated == NULL) {
// Should be impossible
rc = EINVAL;
goto done;
}
crm_trace("Stored hash: %s, calculated hash: %s", stored, calculated);
if (!pcmk__str_eq(stored, calculated, pcmk__str_casei)) {
crm_err("Calculated md5 sum for resource %s parameter '%s' does not "
"match stored md5 sum",
rsc_id, param);
rc = pcmk_rc_cib_corrupt;
}
done:
free(stored);
free(calculated);
return rc;
}
/*!
* \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)
{
GHashTableIter iter;
char *param = NULL;
char *value = NULL;
GString *filename = NULL;
gsize dir_len = 0;
int rc = pcmk_rc_ok;
if (params == NULL) {
return pcmk_rc_ok;
}
// Some params are sent with operations, so we cannot cache secret params
g_hash_table_iter_init(&iter, params);
while (g_hash_table_iter_next(&iter, (gpointer *) ¶m,
(gpointer *) &value)) {
char *secret_value = NULL;
int hash_rc = pcmk_rc_ok;
if (!pcmk__str_eq(value, "lrm://", pcmk__str_none)) {
// Not a secret parameter
continue;
}
if (filename == NULL) {
// First secret parameter. Fill in directory path for use with all.
crm_debug("Replacing secret parameters for resource %s", rsc_id);
filename = g_string_sized_new(128);
pcmk__g_strcat(filename, PCMK__CIB_SECRETS_DIR "/", rsc_id, "/",
NULL);
dir_len = filename->len;
} else {
// Reset filename to the resource's secrets directory path
g_string_truncate(filename, dir_len);
}
// Path to file containing secret value for this parameter
g_string_append(filename, param);
secret_value = read_file_trimmed(filename->str);
if (secret_value == NULL) {
crm_err("Secret value for resource %s parameter '%s' not found in "
PCMK__CIB_SECRETS_DIR,
rsc_id, param);
rc = ENOENT;
continue;
}
// Path to file containing md5 sum for this parameter
g_string_append(filename, ".sign");
hash_rc = validate_hash(filename->str, secret_value, rsc_id, param);
if (hash_rc != pcmk_rc_ok) {
rc = hash_rc;
free(secret_value);
continue;
}
g_hash_table_iter_replace(&iter, (gpointer) secret_value);
}
if (filename != NULL) {
g_string_free(filename, TRUE);
}
return rc;
}
|