This source file includes following definitions.
- pcmk__acl_mark_node_with_namespace
- pcmk__acl_annotate_permissions_recursive
- pcmk__acl_annotate_permissions
- pcmk__acl_evaled_render
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <pwd.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <stdarg.h>
18
19 #include <libxml/parser.h>
20 #include <libxml/tree.h>
21 #include <libxml/xpath.h>
22 #include <libxslt/transform.h>
23 #include <libxslt/variables.h>
24 #include <libxslt/xsltutils.h>
25
26 #include <crm/crm.h>
27 #include <crm/msg_xml.h>
28 #include <crm/common/xml.h>
29 #include <crm/common/xml_internal.h>
30 #include <crm/common/internal.h>
31
32 #include <pacemaker-internal.h>
33
34 #define ACL_NS_PREFIX "http://clusterlabs.org/ns/pacemaker/access/"
35 #define ACL_NS_Q_PREFIX "pcmk-access-"
36 #define ACL_NS_Q_WRITABLE (const xmlChar *) ACL_NS_Q_PREFIX "writable"
37 #define ACL_NS_Q_READABLE (const xmlChar *) ACL_NS_Q_PREFIX "readable"
38 #define ACL_NS_Q_DENIED (const xmlChar *) ACL_NS_Q_PREFIX "denied"
39
40 static const xmlChar *NS_WRITABLE = (const xmlChar *) ACL_NS_PREFIX "writable";
41 static const xmlChar *NS_READABLE = (const xmlChar *) ACL_NS_PREFIX "readable";
42 static const xmlChar *NS_DENIED = (const xmlChar *) ACL_NS_PREFIX "denied";
43
44
45
46
47
48
49
50
51
52
53
54
55 static void
56 pcmk__acl_mark_node_with_namespace(xmlNode *i_node, const xmlChar *ns, int *ret, xmlNs **ns_recycle_writable, xmlNs **ns_recycle_readable, xmlNs **ns_recycle_denied)
57 {
58 if (ns == NS_WRITABLE)
59 {
60 if (*ns_recycle_writable == NULL)
61 {
62 *ns_recycle_writable = xmlNewNs(xmlDocGetRootElement(i_node->doc),
63 NS_WRITABLE, ACL_NS_Q_WRITABLE);
64 }
65 xmlSetNs(i_node, *ns_recycle_writable);
66 *ret = pcmk_rc_ok;
67 }
68 else if (ns == NS_READABLE)
69 {
70 if (*ns_recycle_readable == NULL)
71 {
72 *ns_recycle_readable = xmlNewNs(xmlDocGetRootElement(i_node->doc),
73 NS_READABLE, ACL_NS_Q_READABLE);
74 }
75 xmlSetNs(i_node, *ns_recycle_readable);
76 *ret = pcmk_rc_ok;
77 }
78 else if (ns == NS_DENIED)
79 {
80 if (*ns_recycle_denied == NULL)
81 {
82 *ns_recycle_denied = xmlNewNs(xmlDocGetRootElement(i_node->doc),
83 NS_DENIED, ACL_NS_Q_DENIED);
84 };
85 xmlSetNs(i_node, *ns_recycle_denied);
86 *ret = pcmk_rc_ok;
87 }
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 static int
107 pcmk__acl_annotate_permissions_recursive(xmlNode *xml_modify)
108 {
109
110 static xmlNs *ns_recycle_writable = NULL,
111 *ns_recycle_readable = NULL,
112 *ns_recycle_denied = NULL;
113 static const xmlDoc *prev_doc = NULL;
114
115 xmlNode *i_node = NULL;
116 const xmlChar *ns;
117 int ret = EINVAL;
118
119 if (prev_doc == NULL || prev_doc != xml_modify->doc) {
120 prev_doc = xml_modify->doc;
121 ns_recycle_writable = ns_recycle_readable = ns_recycle_denied = NULL;
122 }
123
124 for (i_node = xml_modify; i_node != NULL; i_node = i_node->next) {
125 switch (i_node->type) {
126 case XML_ELEMENT_NODE:
127 pcmk__set_xml_doc_flag(i_node, pcmk__xf_tracking);
128
129 if (!pcmk__check_acl(i_node, NULL, pcmk__xf_acl_read)) {
130 ns = NS_DENIED;
131 } else if (!pcmk__check_acl(i_node, NULL, pcmk__xf_acl_write)) {
132 ns = NS_READABLE;
133 } else {
134 ns = NS_WRITABLE;
135 }
136 pcmk__acl_mark_node_with_namespace(i_node, ns, &ret, &ns_recycle_writable, &ns_recycle_readable, &ns_recycle_denied);
137
138 if (i_node->properties != NULL) {
139
140
141
142 ret |= pcmk__acl_annotate_permissions_recursive((xmlNodePtr) i_node->properties);
143 }
144 if (i_node->children != NULL) {
145 ret |= pcmk__acl_annotate_permissions_recursive(i_node->children);
146 }
147 break;
148 case XML_ATTRIBUTE_NODE:
149
150 if (!pcmk__check_acl(i_node->parent,
151 (const char *) i_node->name,
152 pcmk__xf_acl_read)) {
153 ns = NS_DENIED;
154 } else if (!pcmk__check_acl(i_node,
155 (const char *) i_node->name,
156 pcmk__xf_acl_write)) {
157 ns = NS_READABLE;
158 } else {
159 ns = NS_WRITABLE;
160 }
161 pcmk__acl_mark_node_with_namespace(i_node, ns, &ret, &ns_recycle_writable, &ns_recycle_readable, &ns_recycle_denied);
162 break;
163 case XML_COMMENT_NODE:
164
165 if (!pcmk__check_acl(i_node->parent, (const char *) i_node->name, pcmk__xf_acl_read))
166 {
167 ns = NS_DENIED;
168 }
169 else if (!pcmk__check_acl(i_node->parent, (const char *) i_node->name, pcmk__xf_acl_write))
170 {
171 ns = NS_READABLE;
172 }
173 else
174 {
175 ns = NS_WRITABLE;
176 }
177 pcmk__acl_mark_node_with_namespace(i_node, ns, &ret, &ns_recycle_writable, &ns_recycle_readable, &ns_recycle_denied);
178 break;
179 default:
180 break;
181 }
182 }
183
184 return ret;
185 }
186
187 int
188 pcmk__acl_annotate_permissions(const char *cred, const xmlDoc *cib_doc,
189 xmlDoc **acl_evaled_doc)
190 {
191 int ret, version;
192 xmlNode *target, *comment;
193 const char *validation;
194
195 CRM_CHECK(cred != NULL, return EINVAL);
196 CRM_CHECK(cib_doc != NULL, return EINVAL);
197 CRM_CHECK(acl_evaled_doc != NULL, return EINVAL);
198
199
200 if (strpbrk(cred, "<>&") != NULL) {
201 return EINVAL;
202 }
203
204 if (!pcmk_acl_required(cred)) {
205
206 return pcmk_rc_already;
207 }
208
209
210
211 validation = crm_element_value(xmlDocGetRootElement((xmlDoc *) cib_doc),
212 XML_ATTR_VALIDATION);
213 version = get_schema_version(validation);
214 if (get_schema_version(PCMK__COMPAT_ACL_2_MIN_INCL) > version) {
215 return pcmk_rc_schema_validation;
216 }
217
218 target = copy_xml(xmlDocGetRootElement((xmlDoc *) cib_doc));
219 if (target == NULL) {
220 return EINVAL;
221 }
222
223 pcmk__enable_acl(target, target, cred);
224
225 ret = pcmk__acl_annotate_permissions_recursive(target);
226
227 if (ret == pcmk_rc_ok) {
228 char* credentials = crm_strdup_printf("ACLs as evaluated for user %s", cred);
229 comment = xmlNewDocComment(target->doc, (pcmkXmlStr) credentials);
230 free(credentials);
231 if (comment == NULL) {
232 xmlFreeNode(target);
233 return EINVAL;
234 }
235 xmlAddPrevSibling(xmlDocGetRootElement(target->doc), comment);
236 *acl_evaled_doc = target->doc;
237 return pcmk_rc_ok;
238 } else {
239 xmlFreeNode(target);
240 return ret;
241 }
242 }
243
244 int
245 pcmk__acl_evaled_render(xmlDoc *annotated_doc, enum pcmk__acl_render_how how,
246 xmlChar **doc_txt_ptr)
247 {
248 xmlDoc *xslt_doc;
249 xsltStylesheet *xslt;
250 xsltTransformContext *xslt_ctxt;
251 xmlDoc *res;
252 char *sfile;
253 static const char *params_namespace[] = {
254 "accessrendercfg:c-writable", ACL_NS_Q_PREFIX "writable:",
255 "accessrendercfg:c-readable", ACL_NS_Q_PREFIX "readable:",
256 "accessrendercfg:c-denied", ACL_NS_Q_PREFIX "denied:",
257 "accessrendercfg:c-reset", "",
258 "accessrender:extra-spacing", "no",
259 "accessrender:self-reproducing-prefix", ACL_NS_Q_PREFIX,
260 NULL
261 }, *params_useansi[] = {
262
263 "accessrendercfg:c-writable", "\x1b[32m",
264 "accessrendercfg:c-readable", "\x1b[34m",
265 "accessrendercfg:c-denied", "\x1b[31m",
266 "accessrendercfg:c-reset", "\x1b[0m",
267 "accessrender:extra-spacing", "no",
268 "accessrender:self-reproducing-prefix", ACL_NS_Q_PREFIX,
269 NULL
270 }, *params_noansi[] = {
271 "accessrendercfg:c-writable", "vvv---[ WRITABLE ]---vvv",
272 "accessrendercfg:c-readable", "vvv---[ READABLE ]---vvv",
273 "accessrendercfg:c-denied", "vvv---[ ~DENIED~ ]---vvv",
274 "accessrendercfg:c-reset", "",
275 "accessrender:extra-spacing", "yes",
276 "accessrender:self-reproducing-prefix", "",
277 NULL
278 };
279 const char **params;
280 int ret;
281 xmlParserCtxtPtr parser_ctxt;
282
283
284
285
286
287
288 xmlChar *annotated_dump;
289 int dump_size;
290
291 CRM_ASSERT(how != pcmk__acl_render_none);
292
293
294 if (how == pcmk__acl_render_default) {
295 if (isatty(STDOUT_FILENO)) {
296 how = pcmk__acl_render_color;
297 } else {
298 how = pcmk__acl_render_text;
299 }
300 }
301
302 xmlDocDumpFormatMemory(annotated_doc, &annotated_dump, &dump_size, 1);
303 res = xmlReadDoc(annotated_dump, "on-the-fly-access-render", NULL,
304 XML_PARSE_NONET);
305 CRM_ASSERT(res != NULL);
306 xmlFree(annotated_dump);
307 xmlFreeDoc(annotated_doc);
308 annotated_doc = res;
309
310 sfile = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_base_xslt,
311 "access-render-2");
312 parser_ctxt = xmlNewParserCtxt();
313
314 CRM_ASSERT(sfile != NULL);
315 CRM_ASSERT(parser_ctxt != NULL);
316
317 xslt_doc = xmlCtxtReadFile(parser_ctxt, sfile, NULL, XML_PARSE_NONET);
318
319 xslt = xsltParseStylesheetDoc(xslt_doc);
320 if (xslt == NULL) {
321 crm_crit("Problem in parsing %s", sfile);
322 return EINVAL;
323 }
324 free(sfile);
325 sfile = NULL;
326 xmlFreeParserCtxt(parser_ctxt);
327
328 xslt_ctxt = xsltNewTransformContext(xslt, annotated_doc);
329 CRM_ASSERT(xslt_ctxt != NULL);
330
331 switch (how) {
332 case pcmk__acl_render_namespace:
333 params = params_namespace;
334 break;
335 case pcmk__acl_render_text:
336 params = params_noansi;
337 break;
338 default:
339
340
341
342
343 params = params_useansi;
344 break;
345 }
346
347 xsltQuoteUserParams(xslt_ctxt, params);
348
349 res = xsltApplyStylesheetUser(xslt, annotated_doc, NULL,
350 NULL, NULL, xslt_ctxt);
351
352 xmlFreeDoc(annotated_doc);
353 annotated_doc = NULL;
354 xsltFreeTransformContext(xslt_ctxt);
355 xslt_ctxt = NULL;
356
357 if (how == pcmk__acl_render_color && params != params_useansi) {
358 char **param_i = (char **) params;
359 do {
360 free(*param_i);
361 } while (*param_i++ != NULL);
362 free(params);
363 }
364
365 if (res == NULL) {
366 ret = EINVAL;
367 } else {
368 int doc_txt_len;
369 int temp = xsltSaveResultToString(doc_txt_ptr, &doc_txt_len, res, xslt);
370 xmlFreeDoc(res);
371 if (temp == 0) {
372 ret = pcmk_rc_ok;
373 } else {
374 ret = EINVAL;
375 }
376 }
377 xsltFreeStylesheet(xslt);
378 return ret;
379 }