pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
digest.c
Go to the documentation of this file.
1 /*
2  * Copyright 2015-2023 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/msg_xml.h>
20 #include <crm/common/xml.h>
21 #include "crmcommon_private.h"
22 
23 #define BEST_EFFORT_STATUS 0
24 
33 static GString *
34 dump_xml_for_digest(xmlNodePtr xml)
35 {
36  GString *buffer = g_string_sized_new(1024);
37 
38  /* for compatibility with the old result which is used for v1 digests */
39  g_string_append_c(buffer, ' ');
40  pcmk__xml2text(xml, 0, buffer, 0);
41  g_string_append_c(buffer, '\n');
42 
43  return buffer;
44 }
45 
56 static char *
57 calculate_xml_digest_v1(xmlNode *input, gboolean sort, gboolean ignored)
58 {
59  char *digest = NULL;
60  GString *buffer = NULL;
61  xmlNode *copy = NULL;
62 
63  if (sort) {
64  crm_trace("Sorting xml...");
65  copy = sorted_xml(input, NULL, TRUE);
66  crm_trace("Done");
67  input = copy;
68  }
69 
70  buffer = dump_xml_for_digest(input);
71  CRM_CHECK(buffer->len > 0, free_xml(copy);
72  g_string_free(buffer, TRUE);
73  return NULL);
74 
75  digest = crm_md5sum((const char *) buffer->str);
76  crm_log_xml_trace(input, "digest:source");
77 
78  g_string_free(buffer, TRUE);
79  free_xml(copy);
80  return digest;
81 }
82 
91 static char *
92 calculate_xml_digest_v2(const xmlNode *source, gboolean do_filter)
93 {
94  char *digest = NULL;
95  GString *buffer = g_string_sized_new(1024);
96 
97  crm_trace("Begin digest %s", do_filter?"filtered":"");
98  pcmk__xml2text(source, (do_filter? pcmk__xml_fmt_filtered : 0), buffer, 0);
99 
100  CRM_ASSERT(buffer != NULL);
101  digest = crm_md5sum((const char *) buffer->str);
102 
104  {
105  char *trace_file = crm_strdup_printf("%s/digest-%s",
106  pcmk__get_tmpdir(), digest);
107 
108  crm_trace("Saving %s.%s.%s to %s",
112  trace_file);
113  save_xml_to_file(source, "digest input", trace_file);
114  free(trace_file);
115  },
116  {}
117  );
118  g_string_free(buffer, TRUE);
119  crm_trace("End digest");
120  return digest;
121 }
122 
130 char *
132 {
133  /* Always use the v1 format for on-disk digests
134  * a) it's a compatibility nightmare
135  * b) we only use this once at startup, all other
136  * invocations are in a separate child process
137  */
138  return calculate_xml_digest_v1(input, FALSE, FALSE);
139 }
140 
149 char *
151 {
152  /* We still need the sorting for operation digests */
153  return calculate_xml_digest_v1(input, TRUE, FALSE);
154 }
155 
166 char *
167 calculate_xml_versioned_digest(xmlNode *input, gboolean sort,
168  gboolean do_filter, const char *version)
169 {
170  /*
171  * @COMPAT digests (on-disk or in diffs/patchsets) created <1.1.4;
172  * removing this affects even full-restart upgrades from old versions
173  *
174  * The sorting associated with v1 digest creation accounted for 23% of
175  * the CIB manager's CPU usage on the server. v2 drops this.
176  *
177  * The filtering accounts for an additional 2.5% and we may want to
178  * remove it in future.
179  *
180  * v2 also uses the xmlBuffer contents directly to avoid additional copying
181  */
182  if (version == NULL || compare_version("3.0.5", version) > 0) {
183  crm_trace("Using v1 digest algorithm for %s",
184  pcmk__s(version, "unknown feature set"));
185  return calculate_xml_digest_v1(input, sort, do_filter);
186  }
187  crm_trace("Using v2 digest algorithm for %s",
188  pcmk__s(version, "unknown feature set"));
189  return calculate_xml_digest_v2(input, do_filter);
190 }
191 
201 bool
202 pcmk__verify_digest(xmlNode *input, const char *expected)
203 {
204  char *calculated = NULL;
205  bool passed;
206 
207  if (input != NULL) {
208  calculated = calculate_on_disk_digest(input);
209  if (calculated == NULL) {
210  crm_perror(LOG_ERR, "Could not calculate digest for comparison");
211  return false;
212  }
213  }
214  passed = pcmk__str_eq(expected, calculated, pcmk__str_casei);
215  if (passed) {
216  crm_trace("Digest comparison passed: %s", calculated);
217  } else {
218  crm_err("Digest comparison failed: expected %s, calculated %s",
219  expected, calculated);
220  }
221  free(calculated);
222  return passed;
223 }
224 
233 bool
235 {
236  static const char *filter[] = {
242  };
243 
244  for (int i = 0; i < PCMK__NELEM(filter); i++) {
245  if (strcmp(name, filter[i]) == 0) {
246  return true;
247  }
248  }
249  return false;
250 }
251 
252 char *
253 crm_md5sum(const char *buffer)
254 {
255  int lpc = 0, len = 0;
256  char *digest = NULL;
257  unsigned char raw_digest[MD5_DIGEST_SIZE];
258 
259  if (buffer == NULL) {
260  buffer = "";
261  }
262  len = strlen(buffer);
263 
264  crm_trace("Beginning digest of %d bytes", len);
265  digest = malloc(2 * MD5_DIGEST_SIZE + 1);
266  if (digest) {
267  md5_buffer(buffer, len, raw_digest);
268  for (lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) {
269  sprintf(digest + (2 * lpc), "%02x", raw_digest[lpc]);
270  }
271  digest[(2 * MD5_DIGEST_SIZE)] = 0;
272  crm_trace("Digest %s.", digest);
273 
274  } else {
275  crm_err("Could not create digest");
276  }
277  return digest;
278 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
#define XML_ATTR_UPDATE_ORIG
Definition: msg_xml.h:163
A dumping ground.
#define XML_ATTR_UPDATE_CLIENT
Definition: msg_xml.h:164
#define pcmk__if_tracing(if_action, else_action)
#define XML_ATTR_NUMUPDATES
Definition: msg_xml.h:149
const char * name
Definition: cib.c:26
char * calculate_on_disk_digest(xmlNode *input)
Calculate and return digest of XML tree, suitable for storing on disk.
Definition: digest.c:131
Exclude certain XML attributes (for calculating digests)
Definition: xml_internal.h:134
#define XML_ATTR_UPDATE_USER
Definition: msg_xml.h:165
#define XML_ATTR_GENERATION
Definition: msg_xml.h:147
#define XML_ATTR_ORIGIN
Definition: msg_xml.h:151
void * md5_buffer(const char *buffer, size_t len, void *resblock)
Definition: md5.c:227
G_GNUC_INTERNAL void pcmk__xml2text(const xmlNode *data, uint32_t options, GString *buffer, int depth)
Definition: xml.c:1597
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
#define crm_trace(fmt, args...)
Definition: logging.h:387
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
Wrappers for and extensions to libxml2.
#define PCMK__NELEM(a)
Definition: internal.h:46
bool pcmk__xa_filterable(const char *name)
Definition: digest.c:234
void free_xml(xmlNode *child)
Definition: xml.c:783
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:167
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2456
const char * pcmk__get_tmpdir(void)
Definition: io.c:547
bool pcmk__verify_digest(xmlNode *input, const char *expected)
Definition: digest.c:202
void save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
Definition: xml.c:1718
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:323
#define crm_err(fmt, args...)
Definition: logging.h:381
#define CRM_ASSERT(expr)
Definition: results.h:42
#define XML_CIB_ATTR_WRITTEN
Definition: msg_xml.h:153
xmlNode * input
int compare_version(const char *version1, const char *version2)
Definition: utils.c:189
#define XML_ATTR_GENERATION_ADMIN
Definition: msg_xml.h:148
char * calculate_operation_digest(xmlNode *input, const char *version)
Calculate and return digest of XML operation.
Definition: digest.c:150
#define MD5_DIGEST_SIZE
Definition: md5.h:30
#define crm_log_xml_trace(xml, text)
Definition: logging.h:395
char * crm_md5sum(const char *buffer)
Definition: digest.c:253
uint32_t version
Definition: remote.c:213