pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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
32static GString *
33dump_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
55static char *
56calculate_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
90static char *
91calculate_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
128char *
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
147char *
149{
150 /* We still need the sorting for operation digests */
151 return calculate_xml_digest_v1(input, TRUE, FALSE);
152}
153
164char *
165calculate_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
199bool
200pcmk__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
231bool
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
250char *
251crm_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
279static bool
280should_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,
292 "pcmk_external_ip",
293 NULL);
294}
295
302void
303pcmk__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}
const char * name
Definition cib.c:26
#define PCMK__NELEM(a)
Definition internal.h:48
uint32_t version
Definition remote.c:1
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int compare_version(const char *version1, const char *version2)
Definition utils.c:188
A dumping ground.
#define CRM_META
Definition crm.h:81
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition digest.c:303
char * crm_md5sum(const char *buffer)
Definition digest.c:251
char * calculate_operation_digest(xmlNode *input, const char *version)
Calculate and return digest of XML operation.
Definition digest.c:148
bool pcmk__xa_filterable(const char *name)
Definition digest.c:232
char * calculate_on_disk_digest(xmlNode *input)
Calculate and return digest of XML tree, suitable for storing on disk.
Definition digest.c:129
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
bool pcmk__verify_digest(xmlNode *input, const char *expected)
Definition digest.c:200
const char * pcmk__get_tmpdir(void)
Definition io.c:547
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:331
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_err(fmt, args...)
Definition logging.h:389
#define crm_log_xml_trace(xml, text)
Definition logging.h:410
#define crm_trace(fmt, args...)
Definition logging.h:402
#define pcmk__if_tracing(if_action, else_action)
void * md5_buffer(const char *buffer, size_t len, void *resblock)
Definition md5.c:227
#define MD5_DIGEST_SIZE
Definition md5.h:30
xmlNode * input
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
char * crm_meta_name(const char *field)
Get the environment variable equivalent of a meta-attribute name.
Definition nvpair.c:959
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_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_META_INTERVAL
Definition options.h:91
#define PCMK_META_TIMEOUT
Definition options.h:114
#define PCMK__META_ON_NODE_UUID
#define PCMK__META_ON_NODE
unsigned int timeout
Definition pcmk_fence.c:32
#define pcmk_ok
Definition results.h:69
@ pcmk__str_casei
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1050
Wrappers for and extensions to libxml2.
void free_xml(xmlNode *child)
Definition xml.c:867
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition xml.c:2072
@ pcmk__xml_fmt_filtered
Exclude certain XML attributes (for calculating digests)
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition xml.c:692
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
#define PCMK_XA_UPDATE_ORIGIN
Definition xml_names.h:432
#define PCMK_XA_CIB_LAST_WRITTEN
Definition xml_names.h:239
#define PCMK_XA_EPOCH
Definition xml_names.h:263
#define PCMK_XA_ID
Definition xml_names.h:296
#define PCMK_XA_ADMIN_EPOCH
Definition xml_names.h:227
#define PCMK_XA_CRM_FEATURE_SET
Definition xml_names.h:249
#define PCMK_XA_CRM_DEBUG_ORIGIN
Definition xml_names.h:248
#define PCMK_XA_UPDATE_USER
Definition xml_names.h:433
#define PCMK_XA_NUM_UPDATES
Definition xml_names.h:336
#define PCMK_XA_UPDATE_CLIENT
Definition xml_names.h:431
#define PCMK__XA_OP_DIGEST