Fix a few 'gcc -fanalyzer' warnings.
[pwmd.git] / src / xml.c
blobdd4a3e96afdfaf9eb721b14b44b2e0e75a6feca6
1 /*
2 Copyright (C) 2006-2023 Ben Kibbey <bjk@luxsci.net>
4 This file is part of pwmd.
6 Pwmd is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License version 2 as
8 published by the Free Software Foundation.
10 Pwmd is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
22 #include <stdio.h>
23 #include <stdlib.h>
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 #include <errno.h>
28 #include <err.h>
29 #include <sys/stat.h>
30 #include <string.h>
31 #ifdef HAVE_FCNTL_H
32 #include <fcntl.h>
33 #endif
34 #include <ctype.h>
35 #include <libxml/xmlwriter.h>
36 #include <wctype.h>
37 #include <sys/types.h>
38 #include <pwd.h>
40 #ifndef _
41 #include "gettext.h"
42 #define _(msgid) gettext(msgid)
43 #endif
45 #include "pwmd-error.h"
46 #include "util-misc.h"
47 #include "xml.h"
48 #include "mem.h"
49 #include "rcfile.h"
50 #include "commands.h"
51 #include "acl.h"
52 #include "command-opt.h"
54 #define XML_LIST_FLAG_CHILDREN 0x0001
55 #define XML_LIST_HAS_TARGET 0x0002
56 #define XML_LIST_CHILD_TARGET 0x0004
57 #define XML_LIST_TARGET_ERROR 0x0008
58 #define XML_LIST_CHECK 0x0010
60 const char *reserved_attributes[] = {
61 "_name", "_mtime", "_ctime", "_acl", "_target",
62 NULL
65 const char *protected_attributes[] = {
66 "_name", "_mtime", "_ctime", NULL
69 void log_write (const char *fmt, ...);
70 static xmlRegexpPtr username_re;
72 gpg_error_t
73 xml_init ()
75 xmlMemSetup (xfree, xmalloc, xrealloc, str_dup);
76 xmlInitMemory ();
77 xmlInitGlobals ();
78 xmlInitParser ();
79 xmlXPathInit ();
80 username_re = xmlRegexpCompile ((xmlChar *)"[a-zA-Z#][-.a-zA-Z0-9_]*");
81 return username_re ? 0 : GPG_ERR_ENOMEM;
84 void
85 xml_deinit ()
87 xmlCleanupParser ();
88 xmlCleanupGlobals ();
89 xmlRegFreeRegexp (username_re);
92 gpg_error_t
93 xml_protected_attr (const char *a)
95 int i;
97 for (i = 0; protected_attributes[i]; i++)
99 if (!strcmp (a, protected_attributes[i]))
100 return GPG_ERR_EPERM;
103 return 0;
107 xml_valid_attribute_value (const char *str)
109 const char *p = str;
111 if (!p || !*p)
112 return 1;
114 while (*p)
116 if (*p++ == '\n')
117 return 0;
120 return 1;
124 xml_valid_attribute (const char *str)
126 wchar_t *wc;
127 size_t len, c;
128 int ret = xml_valid_element ((xmlChar *)str);
130 if (!ret)
131 return ret;
133 wc = str_to_wchar ((const char *)str);
134 if (!wc)
135 return 0;
137 len = wcslen (wc);
138 for (c = 0; c < len; c++)
140 /* Fixes cygwin compile time warning about exceeding the maximum value
141 * for the type in the switch() statement below. */
142 if (wc[c] >= 0x10000 && wc[c] <= 0xEFFFF)
143 continue;
145 switch (wc[c])
147 case '-':
148 case '.':
149 case '0' ... '9':
150 case 0xB7:
151 case 0x0300 ... 0x036F:
152 case 0x203F ... 0x2040:
153 if (!c)
155 xfree (wc);
156 return 0;
158 case ':': break;
159 case '_': break;
160 case 'A' ... 'Z': break;
161 case 'a' ... 'z': break;
162 case 0xC0 ... 0xD6: break;
163 case 0xD8 ... 0xF6: break;
164 case 0xF8 ... 0x2FF: break;
165 case 0x370 ... 0x37D: break;
166 case 0x37F ... 0x1FFF: break;
167 case 0x200C ... 0x200D: break;
168 case 0x2070 ... 0x218F: break;
169 case 0x2C00 ... 0x2FEF: break;
170 case 0x3001 ... 0xD7FF: break;
171 case 0xF900 ... 0xFDCF: break;
172 case 0xFDF0 ... 0xFFFD: break;
173 default:
174 xfree (wc);
175 return 0;
179 xfree (wc);
180 return 1;
184 xml_valid_element (xmlChar *str)
186 wchar_t *wc;
187 size_t len, c;
189 if (!str || !*str)
190 return 0;
192 wc = str_to_wchar ((const char *)str);
193 if (!wc)
194 return 0;
196 len = wcslen (wc);
197 for (c = 0; c < len; c++)
199 if (iswspace(wc[c]))
201 xfree (wc);
202 return 0;
206 xfree (wc);
207 return 1;
211 xml_valid_element_path (char **path, int with_content)
213 char **dup = NULL, **p;
215 if (!path || !*path)
216 return 0;
218 /* Save some memory by not duplicating the element content. */
219 if (with_content)
221 int i, t = strv_length (path);
223 for (i = 0; i < t - 1; i++)
225 char **tmp = xrealloc (dup, (i + 2) * sizeof (char *));
227 if (!tmp)
229 strv_free (dup);
230 return 0;
233 dup = tmp;
234 dup[i] = str_dup (path[i]);
235 dup[i + 1] = NULL;
238 else
239 dup = strv_dup (path);
241 if (!dup)
242 return 0;
244 for (p = dup; *p && *(*p); p++)
246 if (!xml_valid_element ((xmlChar *) * p))
248 strv_free (dup);
249 return 0;
253 strv_free (dup);
254 return 1;
257 static gpg_error_t
258 attr_ctime (struct client_s *client, xmlNodePtr n)
260 char *buf = str_asprintf ("%li", time (NULL));
261 gpg_error_t rc;
263 if (!buf)
264 return GPG_ERR_ENOMEM;
266 rc = xml_add_attribute (client, n, "_ctime", buf);
267 xfree (buf);
268 return rc;
271 gpg_error_t
272 xml_acl_check (struct client_s *client, xmlNodePtr n)
274 gpg_error_t rc = GPG_ERR_EACCES;
275 xmlChar *acl = xml_attribute_value (n, (xmlChar *) "_acl");
276 char **users = acl ? str_split((char *)acl, ",", 0) : NULL;
277 char **cmds = NULL;
278 char **p;
279 int allowed = 0;
281 if (!acl || !*acl || !users || !*users)
283 xmlFree (acl);
284 strv_free(users);
285 return peer_is_invoker(client);
288 if (!peer_is_invoker(client))
290 xmlFree (acl);
291 strv_free(users);
292 return 0;
295 for (p = users; p && *p; p++)
297 char *user = *p;
298 int not = 0;
300 if (*user == '-' || *user == '!')
302 not = 1;
303 user++;
306 if (*user == '/')
308 if (strv_printf (&cmds, "%s%s", not ? "!" : "", user) == 0)
310 xmlFree (acl);
311 strv_free (cmds);
312 strv_free (users);
313 return GPG_ERR_ENOMEM;
318 for (p = users; p && *p; p++)
320 char *user = *p;
322 // Skip command name until after user/group/TLS has been determined.
323 if (*user == '/' || (*user == '!' && *(user+1) == '/')
324 || (*user == '-' && *(user+1) == '/'))
325 continue;
327 #ifdef WITH_GNUTLS
328 rc = acl_check_common (client, user,
329 client->thd->remote ? 0 : client->thd->peer->uid,
330 client->thd->remote ? 0 : client->thd->peer->gid,
331 &allowed);
332 #else
333 rc = acl_check_common (client, user, client->thd->peer->uid,
334 client->thd->peer->gid, &allowed);
335 if (rc)
336 break;
337 #endif
340 if (allowed && cmds)
342 allowed = 0;
343 for (p = cmds; !rc && p && *p && !allowed; p++)
345 rc = acl_check_common (client, *p, client->thd->peer->uid,
346 client->thd->peer->gid, &allowed);
349 if (!rc && !allowed && !xml_is_element_owner (client, n, 0))
350 allowed = 1;
353 xmlFree(acl);
354 strv_free(users);
355 strv_free(cmds);
357 if (rc)
358 return rc;
360 return allowed ? 0 : GPG_ERR_EACCES;
363 static char *
364 create_acl_user (struct client_s *client)
366 #ifdef WITH_GNUTLS
367 if (client->thd->remote)
368 return str_asprintf ("#%s", client->thd->tls->fp);
369 #endif
371 return get_username (client->thd->peer->uid);
374 static gpg_error_t
375 create_new_element (struct client_s *client, int verify, xmlNodePtr parent,
376 const char *name, xmlNodePtr * result)
378 xmlNodePtr n;
379 gpg_error_t rc;
380 int root = 0;
382 // Allow any client to create a non-existing root element.
383 if (parent->parent->type != XML_DOCUMENT_NODE)
385 rc = xml_acl_check(client, parent);
386 if (rc)
387 return rc;
389 else
390 root = 1;
392 n = xmlNewNode (NULL, (xmlChar *) "element");
393 if (!n)
394 return GPG_ERR_ENOMEM;
396 rc = xml_add_attribute (client, n, "_name", name);
397 if (!rc)
398 rc = attr_ctime (client, n);
400 if (!rc && verify && !root)
401 rc = xml_is_element_owner (client, parent, 0);
403 if (!rc)
405 xmlNodePtr p = xmlAddChild (parent, n);
406 char *user = create_acl_user (client);
408 if (result)
409 *result = p;
411 if (root || (client->opts & OPT_NO_INHERIT_ACL))
412 rc = xml_add_attribute(client, p, "_acl", user);
413 else
415 /* Inherit the parent _acl attribute and make the current user the
416 owner. */
417 xmlChar *a = xml_attribute_value (parent, (xmlChar *)"_acl");
418 if (a)
420 if (xml_is_element_owner (client, parent, 1))
422 char *tmp = str_asprintf ("%s,%s", user, a);
424 rc = xml_add_attribute(client, p, "_acl", tmp);
425 xfree (tmp);
427 else
428 rc = xml_add_attribute(client, p, "_acl", (char *)a);
431 xmlFree (a);
434 xfree (user);
436 else
437 xmlFreeNode (n);
439 return rc;
442 gpg_error_t
443 xml_new_root_element (struct client_s *client, xmlDocPtr doc, char *name)
445 xmlNodePtr root = xmlDocGetRootElement (doc);
447 if (!name || !root)
448 return GPG_ERR_BAD_DATA;
450 if (!xml_valid_element ((xmlChar *) name))
451 return GPG_ERR_INV_VALUE;
453 return create_new_element (client, 0, root, name, NULL);
456 static xmlDocPtr
457 create_dtd ()
459 xmlDocPtr doc;
460 xmlTextWriterPtr wr = xmlNewTextWriterDoc (&doc, 0);
462 if (!wr)
463 return NULL;
465 if (xmlTextWriterStartDocument (wr, NULL, "UTF-8", "yes"))
466 goto fail;
468 if (xmlTextWriterStartDTD (wr, (xmlChar *) "pwmd", NULL, NULL) == -1)
469 goto fail;
471 if (xmlTextWriterWriteDTDElement (wr, (xmlChar *) "pwmd",
472 (xmlChar *) "(element)") == -1)
473 goto fail;
475 xmlTextWriterEndDTDElement (wr);
477 if (xmlTextWriterWriteDTDAttlist (wr, (xmlChar *) "element",
478 (xmlChar *) "_name CDATA #REQUIRED") ==
480 goto fail;
482 xmlTextWriterEndDTDAttlist (wr);
483 xmlTextWriterEndDTD (wr);
485 if (xmlTextWriterStartElement (wr, (xmlChar *) "pwmd"))
486 goto fail;
488 xmlTextWriterEndElement (wr);
489 xmlTextWriterEndDocument (wr);
490 xmlFreeTextWriter (wr);
491 return doc;
493 fail:
494 xmlTextWriterEndDocument (wr);
495 xmlFreeTextWriter (wr);
496 xmlFreeDoc (doc);
497 return NULL;
500 xmlDocPtr
501 xml_new_document ()
503 return create_dtd ();
506 static xmlNodePtr
507 find_element_node (xmlNodePtr node)
509 xmlNodePtr n = node;
511 if (n && n->type == XML_ELEMENT_NODE)
512 return n;
514 for (n = node; n; n = n->next)
516 if (n->type == XML_ELEMENT_NODE)
517 return n;
520 return NULL;
523 xmlNodePtr
524 xml_resolve_path (struct client_s *client, xmlDocPtr doc, xmlChar * path,
525 char ***result, gpg_error_t *rc)
527 xmlNodePtr n;
528 struct xml_request_s *req;
530 *rc = xml_new_request (client, (char *)path, XML_CMD_REALPATH, &req);
531 if (*rc)
532 return NULL;
534 n = xml_find_elements (client, req, xmlDocGetRootElement (doc), rc);
535 if (!*rc)
537 if (result)
539 *result = strv_dup (req->args);
540 if (!*result)
542 *rc = GPG_ERR_ENOMEM;
543 n = NULL;
548 xml_free_request (req);
549 return n;
552 xmlNodePtr
553 xml_find_text_node (xmlNodePtr node)
555 xmlNodePtr n = node;
557 if (n && n->type == XML_TEXT_NODE)
558 return n;
560 for (n = node; n; n = n->next)
562 if (n->type == XML_TEXT_NODE)
563 return n;
566 return NULL;
569 /* Create an element 'path' starting at 'node'. Returns the node of the final
570 * created element.
572 xmlNodePtr
573 xml_create_element_path (struct client_s *client, struct xml_request_s *req,
574 xmlNodePtr node, char **path, gpg_error_t *rc)
576 char **p;
578 if (!xml_valid_element_path (path, 0))
580 *rc = GPG_ERR_INV_VALUE;
581 return NULL;
584 /* The parent node will be an XML_ELEMENT_NODE. Otherwise we would be
585 * creating element content. */
586 if (node->type == XML_TEXT_NODE)
587 node = node->parent;
589 for (p = path; p && *p; p++)
591 xmlNodePtr n = NULL;
593 if (node != xmlDocGetRootElement (req->doc))
595 n = xml_find_element (client, node->children, *p, rc);
596 if (*rc && *rc != GPG_ERR_ELEMENT_NOT_FOUND)
597 return NULL;
599 *rc = 0;
603 * If the found element has the same parent as the current element then
604 * they are siblings and the new element needs to be created as a child
605 * of the current element (node).
607 if (n && n->parent == node->parent)
608 n = NULL;
610 if (!n)
612 *rc = create_new_element (client, 0, node, *p, &node);
613 if (*rc)
614 return NULL;
616 else
617 node = n;
620 return node;
623 /* Find the first XML_ELEMENT_NODE whose _name attribute matches 'element'. */
624 xmlNodePtr
625 xml_find_element (struct client_s *client, xmlNodePtr node, const char *element,
626 gpg_error_t *rc)
628 xmlNodePtr n;
630 *rc = 0;
632 if (!node || !element)
634 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
635 return NULL;
638 for (n = node; n; n = n->next)
640 if (n->type != XML_ELEMENT_NODE)
641 continue;
643 xmlChar *a = xml_attribute_value (n, (xmlChar *) "_name");
644 if (a && xmlStrEqual (a, (xmlChar *) element))
646 xmlFree (a);
647 *rc = xml_acl_check (client, n);
648 return n; // Not NULL since the node may be needed later
651 xmlFree (a);
654 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
655 return NULL;
658 xmlChar *
659 xml_attribute_value (xmlNodePtr n, xmlChar * attr)
661 xmlAttrPtr a = xmlHasProp (n, attr);
663 if (!a)
664 return NULL;
666 if (!a->children || !a->children->content)
667 return NULL;
669 return xmlGetProp (n, attr);
672 gpg_error_t
673 xml_remove_user_attributes (struct client_s *client, xmlNodePtr n)
675 gpg_error_t rc = 0;
676 xmlAttrPtr a = n->properties;
678 while (a)
680 if (!xml_reserved_attribute ((const char *)a->name))
682 rc = xml_delete_attribute (client, n, a->name);
683 if (rc)
684 break;
686 a = n->properties;
688 else
689 a = a->next;
692 return rc;
695 gpg_error_t
696 xml_valid_username (const char *str)
698 if (!str)
699 return GPG_ERR_INV_VALUE;
701 char **list = str_split (str, ",", 0);
702 char **p;
704 if (!list)
705 return str && *str ? GPG_ERR_ENOMEM : GPG_ERR_INV_VALUE;
707 for (p = list; *p; p++)
709 const char *t = *p;
711 if (*t == '!' || *t == '-')
712 t++;
714 int e = xmlRegexpExec (username_re, (xmlChar *)t);
715 if (e != 1 && *t != '/' && *t != '@') // client command name or group
717 strv_free (list);
718 return GPG_ERR_INV_USER_ID;
722 strv_free (list);
723 return 0;
726 gpg_error_t
727 xml_add_attribute (struct client_s *client, xmlNodePtr node, const char *name,
728 const char *value)
730 char *buf;
731 gpg_error_t rc = 0;
732 int is_target = 0;
734 if (client && name && !strcmp (name, "_target"))
736 rc = xml_is_element_owner (client, node, 0);
737 if (rc)
738 return rc;
740 is_target = 1;
742 else if (name && (!strcmp (name, "_expire")
743 || !strcmp (name, "_ctime")
744 || !strcmp (name, "_mtime")))
746 char *p;
747 time_t e;
749 if (!value || !*value)
750 return GPG_ERR_INV_VALUE;
752 errno = 0;
753 e = strtoul (value, &p, 10);
754 if (errno || *p || e == ULONG_MAX || *value == '-')
755 return GPG_ERR_INV_VALUE;
758 /* FIXME: Chained groups like "@group1,!@group2,user" wont work to find the
759 * owner of the element. */
760 if (client && name && xmlStrEqual ((xmlChar *) name, (xmlChar *) "_acl"))
761 if (*value == '@')
762 return GPG_ERR_INV_USER_ID;
764 if (name && !xmlSetProp (node, (xmlChar *) name, (xmlChar *) value))
765 return GPG_ERR_BAD_DATA;
767 if (client && name && xmlStrEqual ((xmlChar *) name, (xmlChar *) "_acl"))
769 xmlChar *acl = xml_attribute_value (node, (xmlChar *) "_acl");
771 if (!acl || !*acl || *acl == '/'
772 || ((*acl == '!' || *acl == '-') && *(acl+1) == '/'))
774 char *user = create_acl_user (client);
776 if (user)
778 buf = str_asprintf ("%s,%s", user, acl);
780 if (buf)
781 rc = xml_add_attribute (client, node, (char *) "_acl", buf);
782 else
783 rc = GPG_ERR_ENOMEM;
785 xfree (buf);
786 xfree (user);
789 xmlFree (acl);
790 return rc;
793 xmlFree (acl);
796 if (name && xmlStrEqual ((xmlChar *) name, (xmlChar *) "_mtime"))
797 return 0;
799 if (is_target)
800 xml_remove_user_attributes (client, node);
802 buf = str_asprintf ("%lu", time (NULL));
803 rc = xml_add_attribute (client, node, "_mtime", buf);
804 xfree (buf);
805 return rc;
808 static gpg_error_t
809 append_path_to_list (struct client_s *client, struct element_list_s *elements,
810 const char *path, gpg_error_t rc)
812 struct slist_s *l;
813 struct string_s *line;
815 (void)client;
816 line = string_new (NULL);
817 if (!line)
818 return GPG_ERR_ENOMEM;
820 if (rc == GPG_ERR_ELOOP)
821 string_append (line, "O");
822 else if (rc == GPG_ERR_EACCES)
823 string_append (line, "P");
824 else if (rc)
825 string_append (line, "E");
827 if (rc || elements->target)
828 string_prepend (line, " ");
830 if ((!rc || rc == GPG_ERR_ELOOP || rc == GPG_ERR_EACCES
831 || rc == GPG_ERR_ELEMENT_NOT_FOUND) && elements->target)
832 string_append_printf (line, "%sT %s",
833 (!rc && elements->flags & XML_LIST_FLAG_CHILDREN) ? "+" : "",
834 elements->target);
835 else if (!rc && (elements->flags & XML_LIST_FLAG_CHILDREN))
836 string_prepend (line, " +");
838 if (elements->prefix && elements->prefix->str)
839 string_prepend (line, elements->prefix->str);
840 else
841 string_prepend (line, path);
843 l = slist_append (elements->list, line->str);
844 if (l)
846 elements->list = l;
847 if (!(elements->flags & XML_LIST_CHECK))
848 rc = 0;
850 else
851 rc = GPG_ERR_ENOMEM;
853 string_free (line, 0);
854 return rc;
857 /* Removes the final element from the element prefix. */
858 static void
859 pop_element_prefix (struct element_list_s *elements)
861 char *p = strrchr (elements->prefix->str, '\t');
863 if (p)
864 string_truncate (elements->prefix,
865 elements->prefix->len - strlen (p));
868 #define RESUMABLE_ERROR(rc) (rc == GPG_ERR_ELOOP || rc == GPG_ERR_EACCES \
869 || rc == GPG_ERR_ELEMENT_NOT_FOUND || !rc)
872 * From the element path 'path', find sub-nodes and append them to the list.
874 gpg_error_t
875 xml_create_path_list (struct client_s *client, xmlDocPtr doc, xmlNodePtr node,
876 struct element_list_s *elements, char *path,
877 gpg_error_t rc)
879 xmlNodePtr n = NULL;
880 struct xml_request_s *req = NULL;
881 gpg_error_t trc = rc;
882 char *target_orig;
884 target_orig = rc && elements->target ? str_dup (elements->target) : NULL;
885 rc = xml_new_request (client, path, XML_CMD_LIST, &req);
886 if (rc)
888 xfree (target_orig);
889 return rc;
892 req->data = elements;
893 n = xml_find_elements (client, req,
894 node ? node : xmlDocGetRootElement (doc), &rc);
895 if (!rc)
896 elements->flags |=
897 find_element_node (n->children) ? XML_LIST_FLAG_CHILDREN : 0;
899 if (!rc && trc)
901 xfree (elements->target);
902 elements->target = target_orig;
903 elements->flags = 0;
904 elements->flags |= XML_LIST_HAS_TARGET;
906 else
907 xfree (target_orig);
909 rc = append_path_to_list (client, elements, path, trc ? trc : rc);
910 xmlFree (elements->target);
911 elements->target = NULL;
913 if (rc || trc || (elements->flags & XML_LIST_CHECK)
914 || (!elements->recurse && elements->depth) || elements->root_only)
916 xml_free_request (req);
917 return rc;
920 elements->flags = 0;
922 if (!rc && n && n->children)
924 for (n = n->children; n; n = n->next)
926 xmlChar *target = NULL;
927 xmlNodePtr tmp = NULL;
928 xmlChar *name;
929 struct string_s *newpath;
931 if (n->type != XML_ELEMENT_NODE)
932 continue;
934 name = xml_attribute_value (n, (xmlChar *) "_name");
935 if (!name)
936 continue;
938 newpath = string_new (NULL);
939 if (!newpath)
941 xmlFree (name);
942 rc = GPG_ERR_ENOMEM;
943 goto fail;
946 target = xml_attribute_value (n, (xmlChar *) "_target");
947 if (target)
949 rc = xml_validate_target (client, req->doc, (char *)target, n);
950 elements->target = (char *)target;
951 elements->flags |= rc ? XML_LIST_TARGET_ERROR : 0;
953 else
954 tmp = n;
956 if (RESUMABLE_ERROR (rc))
958 struct string_s *s;
960 /* If there is a target for this node then resolve the full path
961 * starting at the document root. Otherwise, resolve the next
962 * element starting from the child node of the current node. In
963 * either case, append the current element in the element path to
964 * the prefix of the list string.
966 if (target)
968 string_truncate (newpath, 0);
969 s = string_append_printf (newpath, "%s", (char *)target);
971 else
972 s = string_append (newpath, (char *)name);
974 if (!s)
976 xmlFree (name);
977 string_free (newpath, 1);
978 rc = GPG_ERR_ENOMEM;
979 goto fail;
982 newpath = s;
983 if (!elements->depth)
985 /* There may be a single remaining element in the prefix left
986 * over from the previous truncation (see below). It is safe
987 * to truncate it here since depth == 0. */
988 string_truncate (elements->prefix, 0);
989 s = string_append_printf (elements->prefix, "%s\t%s",
990 path, name);
992 else
993 s = string_append_printf (elements->prefix, "\t%s", name);
995 if (!s)
997 xmlFree (name);
998 string_free (newpath, 1);
999 rc = GPG_ERR_ENOMEM;
1000 goto fail;
1003 elements->prefix = s;
1004 elements->depth++;
1005 rc = xml_create_path_list (client, doc, target ? NULL : tmp,
1006 elements, newpath->str, rc);
1007 elements->depth--;
1008 if (!rc)
1009 pop_element_prefix (elements);
1012 string_free (newpath, 1);
1013 xmlFree (name);
1014 if (rc)
1015 break;
1019 fail:
1020 xml_free_request (req);
1021 return rc;
1024 gpg_error_t
1025 xml_recurse_xpath_nodeset (struct client_s *client, xmlDocPtr doc,
1026 xmlNodeSetPtr nodes, xmlChar * value,
1027 xmlBufferPtr * result, int cmd, const xmlChar * attr)
1029 int i = value ? nodes->nodeNr - 1 : 0;
1030 xmlBufferPtr buf;
1032 buf = xmlBufferCreate ();
1034 if (!buf)
1035 return GPG_ERR_ENOMEM;
1037 for (; value ? i >= 0 : i < nodes->nodeNr; value ? i-- : i++)
1039 xmlNodePtr n = nodes->nodeTab[i];
1040 gpg_error_t rc;
1042 if (!n)
1043 continue;
1045 if (!value && !attr)
1047 if (xmlNodeDump (buf, doc, n, 0, 0) == -1)
1049 *result = buf;
1050 return GPG_ERR_BAD_DATA;
1053 continue;
1056 if (!attr)
1058 xmlNodeSetContent (n, value);
1059 rc = xml_update_element_mtime (client, n);
1061 if (rc)
1062 return rc;
1064 else
1066 if (!cmd)
1067 rc = xml_add_attribute (client, n, (char *) attr, (char *) value);
1068 else
1069 rc = xml_delete_attribute (client, n, attr);
1071 if (rc)
1072 return rc;
1076 *result = buf;
1077 return 0;
1080 gpg_error_t
1081 xml_delete_attribute (struct client_s *client, xmlNodePtr n,
1082 const xmlChar *name)
1084 xmlAttrPtr a;
1085 gpg_error_t rc = 0;
1087 if ((a = xmlHasProp (n, name)) == NULL)
1088 return GPG_ERR_NOT_FOUND;
1090 if (xmlRemoveProp (a) == -1)
1091 return GPG_ERR_BAD_DATA;
1093 if (client && xmlStrEqual (name, (xmlChar *) "_acl"))
1095 char *user = create_acl_user (client);
1097 rc = xml_add_attribute (client, n, (char *) "_acl", user);
1098 xfree (user);
1100 if (rc)
1101 return rc;
1104 return xml_update_element_mtime (client, n);
1107 gpg_error_t
1108 xml_validate_import (struct client_s *client, xmlNodePtr node)
1110 gpg_error_t rc = 0;
1112 if (!node)
1113 return 0;
1115 for (xmlNodePtr n = node; n; n = n->next)
1117 if (n->type == XML_ELEMENT_NODE)
1119 if (xmlStrEqual (n->name, (xmlChar *) "element"))
1121 xmlChar *a = xmlGetProp (n, (xmlChar *) "_target");
1123 if (a)
1125 xmlFree (a);
1126 rc = xml_remove_user_attributes (client, n);
1127 if (!rc)
1128 rc = xml_unlink_node (client, n->children);
1131 if (rc)
1132 break;
1134 a = xmlGetProp (n, (xmlChar *) "_name");
1135 if (!a)
1137 xmlChar *t = xmlGetNodePath (n);
1139 log_write (_("Missing attribute '_name' at %s."), t);
1140 xmlFree (t);
1141 return GPG_ERR_INV_VALUE;
1144 if (!xml_valid_element (a))
1146 xmlChar *t = xmlGetNodePath (n);
1148 log_write (_("'%s' is not a valid element name at %s."), a,
1150 xmlFree (a);
1151 xmlFree (t);
1152 return GPG_ERR_INV_VALUE;
1155 xmlFree (a);
1156 a = xmlGetProp (n, (xmlChar *) "_ctime");
1157 if (!a)
1158 attr_ctime (client, n);
1160 xmlFree (a);
1161 a = xmlGetProp (n, (xmlChar *) "_mtime");
1162 if (!a)
1163 xml_update_element_mtime (client, n);
1164 xmlFree (a);
1166 else
1168 xmlChar *t = xmlGetNodePath (n);
1169 xmlNodePtr tmp = n->next;
1171 log_write (_("Warning: unknown element '%s' at %s. Removing."),
1172 n->name, t);
1173 xmlFree (t);
1174 (void)xml_unlink_node (client, n);
1175 return xml_validate_import (client, tmp);
1179 if (n->children)
1181 rc = xml_validate_import (client, n->children);
1182 if (rc)
1183 return rc;
1187 return rc;
1190 gpg_error_t
1191 xml_update_element_mtime (struct client_s *client, xmlNodePtr n)
1193 return xml_add_attribute (client, n, NULL, NULL);
1196 gpg_error_t
1197 xml_unlink_node (struct client_s *client, xmlNodePtr n)
1199 gpg_error_t rc = 0;
1201 if (!n)
1202 return rc;
1204 if (n->parent)
1205 rc = xml_update_element_mtime (client, n->parent);
1207 xmlUnlinkNode (n);
1208 xmlFreeNodeList (n);
1209 return rc;
1212 static gpg_error_t
1213 recurse_rename_attribute (xmlNodePtr root, const char *old, const char *name)
1215 xmlNodePtr n;
1216 gpg_error_t rc = 0;
1218 for (n = root; n; n = n->next)
1220 xmlAttrPtr attr = xmlHasProp (n, (xmlChar *)old);
1222 if (attr)
1224 xmlChar *a = xml_attribute_value (n, (xmlChar *)old);
1226 if (a && !xmlSetProp (n, (xmlChar *) name, a))
1227 rc = GPG_ERR_BAD_DATA;
1229 xmlFree (a);
1231 if (!rc)
1233 if (xmlRemoveProp (attr) == -1)
1234 rc = GPG_ERR_BAD_DATA;
1238 if (rc)
1239 break;
1241 rc = recurse_rename_attribute (n->children, old, name);
1242 if (rc)
1243 break;
1246 return rc;
1249 static gpg_error_t
1250 update_to_version (xmlDocPtr doc)
1252 xmlNodePtr root = xmlDocGetRootElement (doc);
1253 xmlChar *v = xml_attribute_value (root, (xmlChar *)"_version");
1254 gpg_error_t rc;
1256 // Pwmd 3.2.0 or later. No updates needed ATM.
1257 if (v)
1259 xmlFree (v);
1260 return 0;
1263 rc = recurse_rename_attribute (root->children, "target", "_target");
1264 if (!rc)
1265 rc = recurse_rename_attribute (root->children, "expire", "_expire");
1267 if (!rc)
1268 rc = recurse_rename_attribute (root->children, "expire_increment", "_age");
1270 return rc;
1273 gpg_error_t
1274 xml_parse_doc (const char *xml, size_t len, xmlDocPtr *result)
1276 xmlDocPtr doc;
1277 gpg_error_t rc;
1279 xmlResetLastError ();
1280 doc = xmlReadMemory (xml, len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
1281 if (!doc && xmlGetLastError ())
1282 return GPG_ERR_BAD_DATA;
1284 *result = doc;
1285 if (doc)
1286 rc = update_to_version (doc);
1287 else
1288 rc = GPG_ERR_ENOMEM;
1290 return rc;
1293 /* Resolves the 'target' attribute found in 'node' and appends 'path' to the
1294 * target. 'path' is any remaining elements in the element path after this
1295 * element 'node' as passed to xml_find_elements() or the initial command. This
1296 * function may be recursive as it calls xml_find_elements().
1298 static xmlNodePtr
1299 do_realpath (struct client_s *client, struct xml_request_s *req,
1300 xmlNodePtr node, char **path, const xmlChar *target,
1301 gpg_error_t *rc) {
1302 char *line;
1303 char **result = NULL;
1304 struct xml_request_s *nreq = NULL;
1306 *rc = 0;
1308 result = str_split ((char *)target, "\t", 0);
1309 if (!result)
1311 *rc = GPG_ERR_ENOMEM;
1312 return NULL;
1315 /* Append the original path to the target path. */
1316 if (path)
1318 char **p = strv_catv (result, path);
1320 strv_free (result);
1321 if (!p)
1323 *rc = GPG_ERR_ENOMEM;
1324 return NULL;
1327 result = p;
1330 line = strv_join ("\t", result);
1331 if (!line)
1333 strv_free (result);
1334 *rc = GPG_ERR_ENOMEM;
1335 return NULL;
1338 /* To prevent overwriting any original client request flags. We are only
1339 * interested in the path here. */
1340 *rc = xml_new_request (client, line, XML_CMD_REALPATH, &nreq);
1341 xfree (line);
1342 if (*rc)
1344 strv_free (result);
1345 return NULL;
1348 nreq->depth = req->depth;
1349 strv_free (result);
1350 node = xml_find_elements (client, nreq, xmlDocGetRootElement (nreq->doc), rc);
1352 /* Update the original client request path with the resolved one. */
1353 if (!*rc)
1355 strv_free (req->args);
1356 req->args = nreq->args;
1357 nreq->args = NULL;
1360 xml_free_request (nreq);
1361 return node;
1364 #if 0
1365 static char *
1366 node_to_element_path (xmlNodePtr node)
1368 xmlNodePtr n;
1369 struct string_s *str = string_new ("");
1370 char *result;
1372 for (n = node; n; n = n->parent)
1374 xmlNodePtr child;
1376 for (child = n; child; child = child->next)
1378 if (child->type != XML_ELEMENT_NODE)
1379 continue;
1381 xmlChar *name = xml_attribute_value (n, (xmlChar *) "_name");
1382 if (name)
1384 str = string_prepend (str, (char *) name);
1385 xmlFree (name);
1386 name = xml_attribute_value (n, (xmlChar *) "_target");
1387 if (name)
1388 str = string_prepend (str, "\t");
1389 else
1390 str = string_prepend (str, "\t!");
1391 xmlFree (name);
1393 break;
1397 str = string_erase (str, 0, 1);
1398 result = str->str;
1399 string_free (str, 0);
1400 return result;
1402 #endif
1404 /* Tests if 'path' references 'node' somewhere. */
1405 static gpg_error_t
1406 element_related (struct client_s *client, xmlDocPtr doc, const char *path,
1407 xmlNodePtr node, unsigned depth)
1409 gpg_error_t rc = GPG_ERR_NO_DATA;
1410 char **p, **orig;
1411 xmlNodePtr n = xmlDocGetRootElement (doc);
1413 if (max_recursion_depth >= 1 && depth > max_recursion_depth)
1414 return GPG_ERR_ELOOP;
1416 orig = p = str_split (path, "\t", 0);
1417 if (!p)
1418 return GPG_ERR_ENOMEM;
1420 for (n = n->children; n && *p; p++, n = n->next)
1422 xmlNodePtr t;
1423 xmlChar *target;
1424 struct string_s *str = NULL;
1426 t = xml_find_element (client, n, *p, &rc);
1427 if (rc)
1429 rc = GPG_ERR_NO_DATA;
1430 break;
1433 if (t == node)
1435 rc = 0;
1436 break;
1439 target = xml_attribute_value (t, (xmlChar *)"_target");
1440 if (!target)
1441 continue;
1443 str = string_new ((char *)target);
1444 xmlFree (target);
1445 if (!str)
1447 rc = GPG_ERR_ENOMEM;
1448 break;
1451 if (*(p+1))
1453 while (*(++p))
1455 struct string_s *tmp;
1457 tmp = string_append_printf (str, "\t%s", *p);
1458 if (!tmp)
1460 string_free (str, 1);
1461 strv_free (p);
1462 return GPG_ERR_ENOMEM;
1465 str = tmp;
1469 rc = element_related (client, doc, str->str, n->children, ++depth);
1470 if (rc == GPG_ERR_ELOOP)
1471 rc = GPG_ERR_NO_DATA;
1473 string_free(str, 1);
1474 break;
1477 strv_free (orig);
1478 return rc;
1482 * Recurse the element tree beginning at 'node' and find elements who point
1483 * back to 'dst'. Also follows target attributes.
1485 static gpg_error_t
1486 find_child_to_target (struct client_s *client, xmlDocPtr doc, xmlNodePtr node,
1487 xmlNodePtr dst, unsigned depth, int is_target)
1489 xmlNodePtr n;
1490 gpg_error_t rc = 0;
1492 if (max_recursion_depth >= 1 && depth > max_recursion_depth)
1493 return node == dst ? GPG_ERR_ELOOP : 0;
1495 for (n = node; n; n = n->next)
1497 xmlChar *target;
1499 if (n->type != XML_ELEMENT_NODE)
1500 continue;
1502 if (n == dst && depth)
1503 return GPG_ERR_ELOOP;
1505 target = xml_attribute_value (n, (xmlChar *) "_target");
1506 if (target)
1508 xmlNodePtr tmp;
1509 char **result = NULL;
1511 tmp = xml_resolve_path (client, doc, target, &result, &rc);
1512 strv_free (result);
1513 if (!rc)
1515 rc = find_child_to_target (client, doc, tmp, dst, ++depth, 1);
1516 depth--;
1518 else if (rc == GPG_ERR_ELOOP)
1520 gpg_error_t trc = element_related (client, doc, (char *)target,
1521 dst, 0);
1523 if (trc && trc != GPG_ERR_NO_DATA)
1524 rc = trc;
1525 else if (trc == GPG_ERR_NO_DATA)
1526 rc = 0;
1529 xmlFree (target);
1531 if (rc && rc != GPG_ERR_ELEMENT_NOT_FOUND)
1532 return rc;
1534 rc = 0;
1535 if (is_target)
1536 break;
1538 continue;
1541 if (n->children)
1543 rc = find_child_to_target (client, doc, n->children, dst, ++depth, 0);
1544 depth--;
1545 if (rc)
1546 return rc;
1549 if (is_target)
1550 break;
1553 return rc;
1556 static gpg_error_t
1557 find_child_of_parent (xmlDocPtr doc, xmlNodePtr src, xmlNodePtr dst)
1559 xmlNodePtr n;
1560 gpg_error_t rc = 0;
1562 for (n = src; n; n = n->next)
1564 if (n->type != XML_ELEMENT_NODE)
1565 continue;
1567 if (n == dst)
1569 rc = GPG_ERR_ELOOP;
1570 break;
1573 rc = find_child_of_parent (doc, n->children, dst);
1576 return rc;
1579 static gpg_error_t
1580 find_parent_of_child (xmlDocPtr doc, xmlNodePtr node, xmlNodePtr dst)
1582 xmlNodePtr n;
1583 gpg_error_t rc = 0;
1585 (void)doc;
1586 for (n = node; n; n = n->parent)
1588 if (n->type != XML_ELEMENT_NODE)
1590 xmlNodePtr tmp;
1592 for (tmp = n->next; tmp; n = n->next)
1594 if (n->type != XML_ELEMENT_NODE)
1595 continue;
1597 if (tmp == dst)
1598 return GPG_ERR_ELOOP;
1602 if (n == dst)
1603 return GPG_ERR_ELOOP;
1606 return rc;
1609 void
1610 xml_free_element_list (struct element_list_s *elements)
1612 if (elements)
1614 if (elements->list)
1616 int total = slist_length (elements->list);
1617 int i;
1619 for (i = 0; i < total; i++)
1621 char *tmp = slist_nth_data (elements->list, i);
1622 xfree (tmp);
1625 slist_free (elements->list);
1628 string_free (elements->prefix, 1);
1629 xfree (elements->target);
1630 xfree (elements);
1634 /* This behaves like the LIST command with a specified path except it returns
1635 * an error.
1637 gpg_error_t
1638 xml_check_recursion (struct client_s *client, struct xml_request_s *req)
1640 gpg_error_t rc;
1641 struct element_list_s *elements;
1642 char *path = NULL;
1643 char **dup = strv_dup (req->args);
1645 if (!dup)
1646 return GPG_ERR_ENOMEM;
1649 elements = xcalloc (1, sizeof (struct element_list_s));
1650 if (!elements)
1652 rc = GPG_ERR_ENOMEM;
1653 goto fail;
1656 path = strv_join ("\t", dup);
1657 if (!path)
1659 rc = GPG_ERR_ENOMEM;
1660 goto fail;
1663 elements->flags |= XML_LIST_CHECK;
1664 elements->prefix = string_new (NULL);
1665 if (!elements->prefix)
1667 rc = GPG_ERR_ENOMEM;
1668 goto fail;
1671 rc = xml_create_path_list (client, req->doc, xmlDocGetRootElement (req->doc),
1672 elements, path, 0);
1674 fail:
1675 xml_free_element_list (elements);
1676 strv_free (dup);
1677 xfree (path);
1678 return rc;
1681 gpg_error_t
1682 xml_validate_target (struct client_s *client, xmlDocPtr doc, const char *src,
1683 xmlNodePtr dst)
1685 gpg_error_t rc;
1686 xmlNodePtr src_node;
1687 char **src_req = NULL;
1689 src_node = xml_resolve_path (client, doc, (xmlChar *) src, &src_req, &rc);
1690 if (rc)
1691 goto fail;
1693 client->flags |= FLAG_ACL_IGNORE;
1694 /* A destination element is a child of the source element. */
1695 rc = find_child_of_parent (doc, src_node->children, dst);
1696 if (rc)
1697 goto fail;
1699 /* The destination element is a parent of the source element. */
1700 rc = find_parent_of_child (doc, src_node->parent, dst);
1701 if (rc)
1702 goto fail;
1704 /* A destination child element contains a target to the source element. */
1705 if (dst)
1706 rc = find_child_to_target (client, doc,
1707 dst->children ? dst->children : dst, dst, 0, 0);
1708 if (rc)
1709 goto fail;
1711 fail:
1712 strv_free (src_req);
1713 client->flags &= ~(FLAG_ACL_IGNORE | FLAG_ACL_ERROR);
1714 return rc;
1717 /* The owner of the element is the first user listed in the _acl attribute
1718 * list. xml_acl_check() should be called before calling this function. An
1719 * empty ACL is an error if the client is not an invoking_user. The 'strict'
1720 * parameter will not check if the current user is an invoking one.
1722 gpg_error_t
1723 xml_is_element_owner (struct client_s *client, xmlNodePtr n, int strict)
1725 xmlChar *acl = xml_attribute_value (n, (xmlChar *) "_acl");
1726 char **users;
1727 gpg_error_t rc = GPG_ERR_EACCES;
1729 if (!acl || !*acl)
1731 xmlFree (acl);
1732 return peer_is_invoker (client);
1735 users = str_split((char *)acl, ",", 0);
1736 if (users && *users)
1738 char *user;
1740 #ifdef WITH_GNUTLS
1741 if (client->thd->remote)
1742 user = str_asprintf ("#%s", client->thd->tls->fp);
1743 else
1744 user = get_username (client->thd->peer->uid);
1745 #else
1746 user = get_username (client->thd->peer->uid);
1747 #endif
1749 if (*user == '#')
1750 rc = !strcasecmp (*users, user) ? 0 : GPG_ERR_EACCES;
1751 else
1752 rc = !strcmp (*users, user) ? 0 : GPG_ERR_EACCES;
1754 if (rc && !strict)
1755 rc = peer_is_invoker (client);
1757 xfree (user);
1760 xmlFree (acl);
1761 strv_free (users);
1762 return rc;
1765 /* Find elements by matching element names in req->args starting at 'node'.
1766 * Returns the node of the final element in req->args on success or NULL on
1767 * failure and sets 'rc' to the error code. Some commands may need special
1768 * handling and will return the node of the previous found element.
1770 xmlNodePtr
1771 xml_find_elements (struct client_s *client, struct xml_request_s *req,
1772 xmlNodePtr node, gpg_error_t *rc)
1774 xmlNodePtr n, last;
1775 char **p;
1777 *rc = 0;
1778 req->depth++;
1780 if (max_recursion_depth >= 1 && req->depth > max_recursion_depth)
1782 req->depth--;
1783 *rc = GPG_ERR_ELOOP;
1784 return NULL;
1787 /* Beginning of a command/root element (<pwmd>). */
1788 if (node == xmlDocGetRootElement (req->doc))
1790 if (!node->children) // Empty tree
1792 if (req->cmd == XML_CMD_STORE)
1793 return xml_create_element_path (client, req, node, req->args, rc);
1795 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
1796 req->depth--;
1797 return NULL;
1800 node = node->children; // Start searching at the first child of the root
1803 for (last = n = node, p = req->args; *p; p++)
1805 xmlNodePtr tmp = NULL;
1806 struct element_list_s *elements = NULL;
1808 // FIXME should not be reached in this function?
1809 if (!*(*p))
1811 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
1812 req->depth--;
1813 return NULL;
1816 n = xml_find_element (client, n, *p, rc);
1817 if (*rc)
1819 if (*rc != GPG_ERR_ELEMENT_NOT_FOUND && *rc != GPG_ERR_EACCES)
1821 req->depth--;
1822 return NULL;
1825 if (*rc == GPG_ERR_EACCES
1826 && (req->cmd == XML_CMD_ATTR_LIST || req->cmd == XML_CMD_ATTR
1827 || req->cmd == XML_CMD_ATTR_EXPIRE))
1829 req->depth--;
1830 if (*(p+1))
1831 return NULL;
1833 *rc = 0;
1834 return n;
1837 /* Fixes ATTR LIST when an element does not exist and the parent
1838 * denies access to children. Fixes leaking information about the
1839 * current elements children. */
1840 if (*rc == GPG_ERR_ELEMENT_NOT_FOUND && last != n && last != node)
1842 gpg_error_t trc = xml_acl_check (client, last);
1843 if (trc)
1844 *rc = trc;
1847 if (*rc == GPG_ERR_ELEMENT_NOT_FOUND)
1849 if (req->cmd == XML_CMD_STORE || req->cmd == XML_CMD_ATTR_TARGET)
1851 *rc = 0;
1852 n = xml_create_element_path (client, req,
1853 *p == *req->args
1854 && node->parent == xmlDocGetRootElement (req->doc)
1855 ? node->parent : last, p, rc);
1856 if (!*rc)
1857 req->flags |= XML_REQUEST_FLAG_CREATED;
1859 req->depth--;
1860 return n;
1864 if (req->cmd != XML_CMD_LIST && req->cmd != XML_CMD_REALPATH)
1866 req->depth--;
1867 return NULL;
1870 // Fall through to let LIST flags be appended.
1873 last = n;
1875 /* Follow a "_target" attribute for each one found in each element. */
1876 xmlChar *target = xml_attribute_value (n, (xmlChar *) "_target");
1877 if (!target || !*target)
1879 xmlFree (target);
1881 if (*rc)
1883 req->depth--;
1884 return NULL;
1887 /* This is the final element in req->args. Done. */
1888 if (!*(p+1))
1890 req->depth--;
1891 return n;
1894 n = n->children;
1895 continue;
1898 if (req->cmd == XML_CMD_REALPATH)
1900 n = do_realpath (client, req, n, p+1, target, rc);
1901 xmlFree (target);
1902 req->depth--;
1903 return n;
1905 else if (req->cmd == XML_CMD_DELETE || req->cmd == XML_CMD_RENAME
1906 || req->cmd == XML_CMD_MOVE || req->cmd == XML_CMD_ATTR
1907 || req->cmd == XML_CMD_ATTR_TARGET
1908 || req->cmd == XML_CMD_ATTR_LIST)
1910 /* No more elements to resolve. Return the node with the target. */
1911 if (!*(p+1))
1913 xmlFree (target);
1914 req->depth--;
1915 return n;
1918 else if (req->cmd == XML_CMD_LIST)
1920 elements = req->data;
1921 if (elements && !(elements->flags & XML_LIST_CHILD_TARGET))
1923 /* No further elements. Use this target in the list flags. */
1924 if (!*(p+1))
1925 elements->flags |= XML_LIST_HAS_TARGET;
1926 else
1928 /* Prevent replacing any following target value with the
1929 * current target value. */
1930 req->data = NULL;
1933 else if (!elements)
1935 struct element_list_s *e = xcalloc (1, sizeof (struct element_list_s));
1937 e->target = str_dup ((char *)target);
1938 e->flags |= XML_LIST_CHILD_TARGET;
1940 if (!*(p+1))
1941 e->flags |= XML_LIST_HAS_TARGET;
1943 req->data = elements = e;
1946 gpg_error_t rc_orig = *rc;
1947 *rc = xml_validate_target (client, req->doc, (char *)target, n);
1948 /* Ignore errors for elements elsewhere in the document. */
1949 if (*rc == GPG_ERR_EACCES)
1950 *rc = 0;
1952 if (rc_orig != *rc)
1953 *rc = *rc ? *rc : rc_orig;
1955 if (*rc)
1956 goto update_target;
1959 /* Create a new path based on the value of the current "_target"
1960 * attribute. */
1961 char **nargs = str_split ((char *)target, "\t", 0);
1962 if (!nargs)
1964 xmlFree (target);
1965 *rc = GPG_ERR_INV_VALUE;
1966 req->data = req->cmd == XML_CMD_LIST ? elements : req->data;
1967 req->depth--;
1968 return NULL;
1971 /* Append the remaining elements to the target attributes path. */
1972 char **nnargs = strv_catv (nargs, p+1);
1973 strv_free (nargs);
1974 char **orig = req->args;
1975 req->args = nnargs;
1976 tmp = xml_find_elements (client, req, xmlDocGetRootElement (req->doc),
1977 rc);
1978 strv_free (nnargs);
1979 req->args = orig;
1981 update_target:
1982 if (req->cmd == XML_CMD_LIST && elements)
1984 /* A child node contained a target. Use this value rather than the
1985 * current target value as the target value for the list flag. */
1986 if (req->data && req->data != elements)
1988 struct element_list_s *e = req->data;
1990 xmlFree (target);
1991 target = xmlStrdup ((xmlChar *)e->target);
1992 xfree (e->target);
1994 if ((e->flags & XML_LIST_HAS_TARGET))
1995 elements->flags |= XML_LIST_HAS_TARGET;
1997 xfree (e);
2000 req->data = elements;
2001 if (req->data)
2003 if (!(elements->flags & XML_LIST_TARGET_ERROR))
2005 xfree (elements->target);
2006 elements->target = NULL;
2008 /* Restore the original target flag. */
2009 if (target && (elements->flags & XML_LIST_HAS_TARGET))
2010 elements->target = str_dup ((char *)target);
2015 xmlFree (target);
2016 req->depth--;
2017 return *rc ? NULL : tmp;
2020 req->depth--;
2021 return n;
2024 gpg_error_t
2025 xml_new_request (struct client_s *client, const char *line, xml_command_t cmd,
2026 struct xml_request_s **result)
2028 struct xml_request_s *req;
2029 char **pp = str_split ((char *) line, "\t", 0);
2031 if (!pp || !*pp)
2033 strv_free (pp);
2034 return GPG_ERR_SYNTAX;
2037 req = xcalloc (1, sizeof (struct xml_request_s));
2038 if (!req)
2040 strv_free (pp);
2041 return GPG_ERR_ENOMEM;
2044 req->cmd = cmd;
2045 req->args = pp;
2046 req->doc = client ? client->doc : NULL;
2047 *result = req;
2048 return 0;
2051 void
2052 xml_free_request (struct xml_request_s *r)
2054 if (!r)
2055 return;
2057 strv_free (r->args);
2058 xfree (r);
2062 xml_reserved_attribute (const char *name)
2064 int i;
2066 for (i = 0; reserved_attributes[i]; i++)
2068 if (!strcmp (name, reserved_attributes[i]))
2069 return 1;
2072 return 0;