pacemaker  2.1.4-dc6eb4362
Scalable High-Availability cluster resource manager
cib_secrets.c
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2020 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 static int is_magic_value(char *p);
27 static bool check_md5_hash(char *hash, char *value);
28 static void add_secret_params(gpointer key, gpointer value, gpointer user_data);
29 static char *read_local_file(char *local_file);
30 
31 #define MAX_VALUE_LEN 255
32 #define MAGIC "lrm://"
33 
34 static int
35 is_magic_value(char *p)
36 {
37  return !strcmp(p, MAGIC);
38 }
39 
40 static bool
41 check_md5_hash(char *hash, char *value)
42 {
43  bool rc = false;
44  char *hash2 = NULL;
45 
46  hash2 = crm_md5sum(value);
47  crm_debug("hash: %s, calculated hash: %s", hash, hash2);
48  if (pcmk__str_eq(hash, hash2, pcmk__str_casei)) {
49  rc = true;
50  }
51  free(hash2);
52  return rc;
53 }
54 
55 static char *
56 read_local_file(char *local_file)
57 {
58  FILE *fp = fopen(local_file, "r");
59  char buf[MAX_VALUE_LEN+1];
60  char *p;
61 
62  if (!fp) {
63  if (errno != ENOENT) {
64  crm_perror(LOG_ERR, "cannot open %s" , local_file);
65  }
66  return NULL;
67  }
68 
69  if (!fgets(buf, MAX_VALUE_LEN, fp)) {
70  crm_perror(LOG_ERR, "cannot read %s", local_file);
71  fclose(fp);
72  return NULL;
73  }
74  fclose(fp);
75 
76  // Strip trailing white space
77  for (p = buf + strlen(buf) - 1; (p >= buf) && isspace(*p); p--);
78  *(p+1) = '\0';
79  return strdup(buf);
80 }
81 
95 int
96 pcmk__substitute_secrets(const char *rsc_id, GHashTable *params)
97 {
98  char local_file[FILENAME_MAX+1], *start_pname;
99  char hash_file[FILENAME_MAX+1], *hash;
100  GList *secret_params = NULL, *l;
101  char *key, *pvalue, *secret_value;
102  int rc = pcmk_rc_ok;
103 
104  if (params == NULL) {
105  return pcmk_rc_ok;
106  }
107 
108  /* secret_params could be cached with the resource;
109  * there are also parameters sent with operations
110  * which cannot be cached
111  */
112  g_hash_table_foreach(params, add_secret_params, &secret_params);
113  if (secret_params == NULL) { // No secret parameters found
114  return pcmk_rc_ok;
115  }
116 
117  crm_debug("Replace secret parameters for resource %s", rsc_id);
118 
119  if (snprintf(local_file, FILENAME_MAX, LRM_CIBSECRETS_DIR "/%s/", rsc_id)
120  > FILENAME_MAX) {
121  crm_err("Can't replace secret parameters for %s: file name size exceeded",
122  rsc_id);
123  return ENAMETOOLONG;
124  }
125  start_pname = local_file + strlen(local_file);
126 
127  for (l = g_list_first(secret_params); l; l = g_list_next(l)) {
128  key = (char *)(l->data);
129  pvalue = g_hash_table_lookup(params, key);
130  if (!pvalue) { /* this cannot really happen */
131  crm_err("odd, no parameter %s for rsc %s found now", key, rsc_id);
132  continue;
133  }
134 
135  if ((strlen(key) + strlen(local_file)) >= FILENAME_MAX-2) {
136  crm_err("%s: parameter name %s too big", rsc_id, key);
137  rc = ENAMETOOLONG;
138  continue;
139  }
140 
141  strcpy(start_pname, key);
142  secret_value = read_local_file(local_file);
143  if (!secret_value) {
144  crm_err("secret for rsc %s parameter %s not found in %s",
145  rsc_id, key, LRM_CIBSECRETS_DIR);
146  rc = ENOENT;
147  continue;
148  }
149 
150  strcpy(hash_file, local_file);
151  if (strlen(hash_file) + 5 > FILENAME_MAX) {
152  crm_err("cannot build such a long name "
153  "for the sign file: %s.sign", hash_file);
154  free(secret_value);
155  rc = ENAMETOOLONG;
156  continue;
157 
158  } else {
159  strcat(hash_file, ".sign");
160  hash = read_local_file(hash_file);
161  if (hash == NULL) {
162  crm_err("md5 sum for rsc %s parameter %s "
163  "cannot be read from %s", rsc_id, key, hash_file);
164  free(secret_value);
165  rc = ENOENT;
166  continue;
167 
168  } else if (!check_md5_hash(hash, secret_value)) {
169  crm_err("md5 sum for rsc %s parameter %s "
170  "does not match", rsc_id, key);
171  free(secret_value);
172  free(hash);
173  rc = pcmk_rc_cib_corrupt;
174  continue;
175  }
176  free(hash);
177  }
178  g_hash_table_replace(params, strdup(key), secret_value);
179  }
180  g_list_free(secret_params);
181  return rc;
182 }
183 
184 static void
185 add_secret_params(gpointer key, gpointer value, gpointer user_data)
186 {
187  GList **lp = (GList **)user_data;
188 
189  if (is_magic_value((char *)value)) {
190  *lp = g_list_append(*lp, (char *)key);
191  }
192 }
#define MAGIC
Definition: cib_secrets.c:32
int pcmk__substitute_secrets(const char *rsc_id, GHashTable *params)
Definition: cib_secrets.c:96
#define LRM_CIBSECRETS_DIR
Definition: config.h:455
#define crm_debug(fmt, args...)
Definition: logging.h:363
Utility functions.
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:309
#define crm_err(fmt, args...)
Definition: logging.h:358
#define MAX_VALUE_LEN
Definition: cib_secrets.c:31
char * crm_md5sum(const char *buffer)
Definition: digest.c:271