pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
digest.c
Go to the documentation of this file.
1 /*
2  * Copyright 2015-2024 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 <stdio.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <md5.h>
17 
18 #include <crm/crm.h>
19 #include <crm/common/xml.h>
20 #include "crmcommon_private.h"
21 
22 #define BEST_EFFORT_STATUS 0
23 
32 static GString *
33 dump_xml_for_digest(xmlNodePtr xml)
34 {
35  GString *buffer = g_string_sized_new(1024);
36 
37  /* for compatibility with the old result which is used for v1 digests */
38  g_string_append_c(buffer, ' ');
39  pcmk__xml_string(xml, 0, buffer, 0);
40  g_string_append_c(buffer, '\n');
41 
42  return buffer;
43 }
44 
55 static char *
56 calculate_xml_digest_v1(xmlNode *input, gboolean sort, gboolean ignored)
57 {
58  char *digest = NULL;
59  GString *buffer = NULL;
60  xmlNode *copy = NULL;
61 
62  if (sort) {
63  crm_trace("Sorting xml...");
64  copy = sorted_xml(input, NULL, TRUE);
65  crm_trace("Done");
66  input = copy;
67  }
68 
69  buffer = dump_xml_for_digest(input);
70  CRM_CHECK(buffer->len > 0, free_xml(copy);
71  g_string_free(buffer, TRUE);
72  return NULL);
73 
74  digest = crm_md5sum((const char *) buffer->str);
75  crm_log_xml_trace(input, "digest:source");
76 
77  g_string_free(buffer, TRUE);
78  free_xml(copy);
79  return digest;
80 }
81 
90 static char *
91 calculate_xml_digest_v2(const xmlNode *source, gboolean do_filter)
92 {
93  char *digest = NULL;
94  GString *buf = g_string_sized_new(1024);
95 
96  crm_trace("Begin digest %s", do_filter?"filtered":"");
97 
98  pcmk__xml_string(source, (do_filter? pcmk__xml_fmt_filtered : 0), buf, 0);
99  digest = crm_md5sum(buf->str);
100 
102  {
103  char *trace_file = crm_strdup_printf("%s/digest-%s",
104  pcmk__get_tmpdir(), digest);
105 
106  crm_trace("Saving %s.%s.%s to %s",
110  trace_file);
111  save_xml_to_file(source, "digest input", trace_file);
112  free(trace_file);
113  },
114  {}
115  );
116  crm_trace("End digest");
117  g_string_free(buf, TRUE);
118  return digest;
119 }
120 
128 char *
130 {
131  /* Always use the v1 format for on-disk digests
132  * a) it's a compatibility nightmare
133  * b) we only use this once at startup, all other
134  * invocations are in a separate child process
135  */
136  return calculate_xml_digest_v1(input, FALSE, FALSE);
137 }
138 
147 char *
149 {
150  /* We still need the sorting for operation digests */
151  return calculate_xml_digest_v1(input, TRUE, FALSE);
152 }
153 
164 char *
165 calculate_xml_versioned_digest(xmlNode *input, gboolean sort,
166  gboolean do_filter, const char *version)
167 {
168  /*
169  * @COMPAT digests (on-disk or in diffs/patchsets) created <1.1.4;
170  * removing this affects even full-restart upgrades from old versions
171  *
172  * The sorting associated with v1 digest creation accounted for 23% of
173  * the CIB manager's CPU usage on the server. v2 drops this.
174  *
175  * The filtering accounts for an additional 2.5% and we may want to
176  * remove it in future.
177  *
178  * v2 also uses the xmlBuffer contents directly to avoid additional copying
179  */
180  if (version == NULL || compare_version("3.0.5", version) > 0) {
181  crm_trace("Using v1 digest algorithm for %s",
182  pcmk__s(version, "unknown feature set"));
183  return calculate_xml_digest_v1(input, sort, do_filter);
184  }
185  crm_trace("Using v2 digest algorithm for %s",
186  pcmk__s(version, "unknown feature set"));
187  return calculate_xml_digest_v2(input, do_filter);
188 }
189 
199 bool
200 pcmk__verify_digest(xmlNode *input, const char *expected)
201 {
202  char *calculated = NULL;
203  bool passed;
204 
205  if (input != NULL) {
206  calculated = calculate_on_disk_digest(input);
207  if (calculated == NULL) {
208  crm_perror(LOG_ERR, "Could not calculate digest for comparison");
209  return false;
210  }
211  }
212  passed = pcmk__str_eq(expected, calculated, pcmk__str_casei);
213  if (passed) {
214  crm_trace("Digest comparison passed: %s", calculated);
215  } else {
216  crm_err("Digest comparison failed: expected %s, calculated %s",
217  expected, calculated);
218  }
219  free(calculated);
220  return passed;
221 }
222 
231 bool
233 {
234  static const char *filter[] = {
240  };
241 
242  for (int i = 0; i < PCMK__NELEM(filter); i++) {
243  if (strcmp(name, filter[i]) == 0) {
244  return true;
245  }
246  }
247  return false;
248 }
249 
250 char *
251 crm_md5sum(const char *buffer)
252 {
253  int lpc = 0, len = 0;
254  char *digest = NULL;
255  unsigned char raw_digest[MD5_DIGEST_SIZE];
256 
257  if (buffer == NULL) {
258  buffer = "";
259  }
260  len = strlen(buffer);
261 
262  crm_trace("Beginning digest of %d bytes", len);
263  digest = malloc(2 * MD5_DIGEST_SIZE + 1);
264  if (digest) {
265  md5_buffer(buffer, len, raw_digest);
266  for (lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) {
267  sprintf(digest + (2 * lpc), "%02x", raw_digest[lpc]);
268  }
269  digest[(2 * MD5_DIGEST_SIZE)] = 0;
270  crm_trace("Digest %s.", digest);
271 
272  } else {
273  crm_err("Could not create digest");
274  }
275  return digest;
276 }
277 
278 // Return true if a is an attribute that should be filtered
279 static bool
280 should_filter_for_digest(xmlAttrPtr a, void *user_data)
281 {
282  if (strncmp((const char *) a->name, CRM_META "_",
283  sizeof(CRM_META " ") - 1) == 0) {
284  return true;
285  }
286  return pcmk__str_any_of((const char *) a->name,
287  PCMK_XA_ID,
292  "pcmk_external_ip",
293  NULL);
294 }
295 
302 void
303 pcmk__filter_op_for_digest(xmlNode *param_set)
304 {
305  char *key = NULL;
306  char *timeout = NULL;
307  guint interval_ms = 0;
308 
309  if (param_set == NULL) {
310  return;
311  }
312 
313  /* Timeout is useful for recurring operation digests, so grab it before
314  * removing meta-attributes
315  */
317  if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) {
318  interval_ms = 0;
319  }
320  free(key);
321  key = NULL;
322  if (interval_ms != 0) {
324  timeout = crm_element_value_copy(param_set, key);
325  }
326 
327  // Remove all CRM_meta_* attributes and certain other attributes
328  pcmk__xe_remove_matching_attrs(param_set, should_filter_for_digest, NULL);
329 
330  // Add timeout back for recurring operation digests
331  if (timeout != NULL) {
332  crm_xml_add(param_set, key, timeout);
333  }
334  free(timeout);
335  free(key);
336 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
A dumping ground.
#define pcmk__if_tracing(if_action, else_action)
#define PCMK_XA_UPDATE_ORIGIN
Definition: xml_names.h:432
const char * name
Definition: cib.c:26
void save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
Definition: xml_io.c:734
void pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer, int depth)
Definition: xml_io.c:488
char * calculate_on_disk_digest(xmlNode *input)
Calculate and return digest of XML tree, suitable for storing on disk.
Definition: digest.c:129
Exclude certain XML attributes (for calculating digests)
Definition: xml_internal.h:137
#define PCMK_XA_NUM_UPDATES
Definition: xml_names.h:336
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:301
#define PCMK_XA_CIB_LAST_WRITTEN
Definition: xml_names.h:239
#define PCMK__META_ON_NODE
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition: digest.c:303
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:539
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:674
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:446
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:692
#define PCMK__XA_OP_DIGEST
#define crm_trace(fmt, args...)
Definition: logging.h:404
#define PCMK_XA_UPDATE_USER
Definition: xml_names.h:433
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define PCMK__META_ON_NODE_UUID
#define PCMK_XA_EPOCH
Definition: xml_names.h:263
Wrappers for and extensions to libxml2.
#define PCMK__NELEM(a)
Definition: internal.h:48
#define PCMK_XA_ID
Definition: xml_names.h:296
bool pcmk__xa_filterable(const char *name)
Definition: digest.c:232
void free_xml(xmlNode *child)
Definition: xml.c:867
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1050
char * calculate_xml_versioned_digest(xmlNode *input, gboolean sort, gboolean do_filter, const char *version)
Calculate and return digest of XML tree.
Definition: digest.c:165
#define PCMK_META_TIMEOUT
Definition: options.h:114
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2072
#define PCMK_XA_CRM_FEATURE_SET
Definition: xml_names.h:249
const char * pcmk__get_tmpdir(void)
Definition: io.c:547
bool pcmk__verify_digest(xmlNode *input, const char *expected)
Definition: digest.c:200
#define PCMK_META_INTERVAL
Definition: options.h:91
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:331
#define CRM_META
Definition: crm.h:81
#define crm_err(fmt, args...)
Definition: logging.h:391
xmlNode * input
int compare_version(const char *version1, const char *version2)
Definition: utils.c:188
char * calculate_operation_digest(xmlNode *input, const char *version)
Calculate and return digest of XML operation.
Definition: digest.c:148
#define pcmk_ok
Definition: results.h:69
#define MD5_DIGEST_SIZE
Definition: md5.h:30
void * md5_buffer(const char *buffer, size_t len, void *resblock)
Definition: md5.c:227
#define crm_log_xml_trace(xml, text)
Definition: logging.h:412
char * crm_md5sum(const char *buffer)
Definition: digest.c:251
#define PCMK_XA_ADMIN_EPOCH
Definition: xml_names.h:227
#define PCMK_XA_CRM_DEBUG_ORIGIN
Definition: xml_names.h:248
unsigned int timeout
Definition: pcmk_fence.c:32
#define PCMK_XA_UPDATE_CLIENT
Definition: xml_names.h:431
uint32_t version
Definition: remote.c:213
char * crm_meta_name(const char *field)
Get the environment variable equivalent of a meta-attribute name.
Definition: nvpair.c:959