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