root/lib/common/cib_secrets.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. read_file_trimmed
  2. validate_hash
  3. pcmk__substitute_secrets

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

/* [previous][next][first][last][top][bottom][index][help] */