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/>.
35 #include <libxml/xmlwriter.h>
37 #include <sys/types.h>
42 #define _(msgid) gettext(msgid)
45 #include "pwmd-error.h"
46 #include "util-misc.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",
65 const char *protected_attributes
[] = {
66 "_name", "_mtime", "_ctime", NULL
69 void log_write (const char *fmt
, ...);
70 static xmlRegexpPtr username_re
;
75 xmlMemSetup (xfree
, xmalloc
, xrealloc
, str_dup
);
80 username_re
= xmlRegexpCompile ((xmlChar
*)"[a-zA-Z#][-.a-zA-Z0-9_]*");
81 return username_re
? 0 : GPG_ERR_ENOMEM
;
89 xmlRegFreeRegexp (username_re
);
93 xml_protected_attr (const char *a
)
97 for (i
= 0; protected_attributes
[i
]; i
++)
99 if (!strcmp (a
, protected_attributes
[i
]))
100 return GPG_ERR_EPERM
;
107 xml_valid_attribute_value (const char *str
)
124 xml_valid_attribute (const char *str
)
128 int ret
= xml_valid_element ((xmlChar
*)str
);
133 wc
= str_to_wchar ((const char *)str
);
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)
151 case 0x0300 ... 0x036F:
152 case 0x203F ... 0x2040:
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;
184 xml_valid_element (xmlChar
*str
)
192 wc
= str_to_wchar ((const char *)str
);
197 for (c
= 0; c
< len
; c
++)
211 xml_valid_element_path (char **path
, int with_content
)
213 char **dup
= NULL
, **p
;
218 /* Save some memory by not duplicating the element 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 *));
234 dup
[i
] = str_dup (path
[i
]);
239 dup
= strv_dup (path
);
244 for (p
= dup
; *p
&& *(*p
); p
++)
246 if (!xml_valid_element ((xmlChar
*) * p
))
258 attr_ctime (struct client_s
*client
, xmlNodePtr n
)
260 char *buf
= str_asprintf ("%li", time (NULL
));
264 return GPG_ERR_ENOMEM
;
266 rc
= xml_add_attribute (client
, n
, "_ctime", buf
);
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
;
281 if (!acl
|| !*acl
|| !users
|| !*users
)
285 return peer_is_invoker(client
);
288 if (!peer_is_invoker(client
))
295 for (p
= users
; p
&& *p
; p
++)
300 if (*user
== '-' || *user
== '!')
308 if (strv_printf (&cmds
, "%s%s", not ? "!" : "", user
) == 0)
313 return GPG_ERR_ENOMEM
;
318 for (p
= users
; p
&& *p
; p
++)
322 // Skip command name until after user/group/TLS has been determined.
323 if (*user
== '/' || (*user
== '!' && *(user
+1) == '/')
324 || (*user
== '-' && *(user
+1) == '/'))
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
,
333 rc
= acl_check_common (client
, user
, client
->thd
->peer
->uid
,
334 client
->thd
->peer
->gid
, &allowed
);
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))
360 return allowed
? 0 : GPG_ERR_EACCES
;
364 create_acl_user (struct client_s
*client
)
367 if (client
->thd
->remote
)
368 return str_asprintf ("#%s", client
->thd
->tls
->fp
);
371 return get_username (client
->thd
->peer
->uid
);
375 create_new_element (struct client_s
*client
, int verify
, xmlNodePtr parent
,
376 const char *name
, xmlNodePtr
* result
)
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
);
392 n
= xmlNewNode (NULL
, (xmlChar
*) "element");
394 return GPG_ERR_ENOMEM
;
396 rc
= xml_add_attribute (client
, n
, "_name", name
);
398 rc
= attr_ctime (client
, n
);
400 if (!rc
&& verify
&& !root
)
401 rc
= xml_is_element_owner (client
, parent
, 0);
405 xmlNodePtr p
= xmlAddChild (parent
, n
);
406 char *user
= create_acl_user (client
);
411 if (root
|| (client
->opts
& OPT_NO_INHERIT_ACL
))
412 rc
= xml_add_attribute(client
, p
, "_acl", user
);
415 /* Inherit the parent _acl attribute and make the current user the
417 xmlChar
*a
= xml_attribute_value (parent
, (xmlChar
*)"_acl");
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
);
428 rc
= xml_add_attribute(client
, p
, "_acl", (char *)a
);
443 xml_new_root_element (struct client_s
*client
, xmlDocPtr doc
, char *name
)
445 xmlNodePtr root
= xmlDocGetRootElement (doc
);
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
);
460 xmlTextWriterPtr wr
= xmlNewTextWriterDoc (&doc
, 0);
465 if (xmlTextWriterStartDocument (wr
, NULL
, "UTF-8", "yes"))
468 if (xmlTextWriterStartDTD (wr
, (xmlChar
*) "pwmd", NULL
, NULL
) == -1)
471 if (xmlTextWriterWriteDTDElement (wr
, (xmlChar
*) "pwmd",
472 (xmlChar
*) "(element)") == -1)
475 xmlTextWriterEndDTDElement (wr
);
477 if (xmlTextWriterWriteDTDAttlist (wr
, (xmlChar
*) "element",
478 (xmlChar
*) "_name CDATA #REQUIRED") ==
482 xmlTextWriterEndDTDAttlist (wr
);
483 xmlTextWriterEndDTD (wr
);
485 if (xmlTextWriterStartElement (wr
, (xmlChar
*) "pwmd"))
488 xmlTextWriterEndElement (wr
);
489 xmlTextWriterEndDocument (wr
);
490 xmlFreeTextWriter (wr
);
494 xmlTextWriterEndDocument (wr
);
495 xmlFreeTextWriter (wr
);
503 return create_dtd ();
507 find_element_node (xmlNodePtr node
)
511 if (n
&& n
->type
== XML_ELEMENT_NODE
)
514 for (n
= node
; n
; n
= n
->next
)
516 if (n
->type
== XML_ELEMENT_NODE
)
524 xml_resolve_path (struct client_s
*client
, xmlDocPtr doc
, xmlChar
* path
,
525 char ***result
, gpg_error_t
*rc
)
528 struct xml_request_s
*req
;
530 *rc
= xml_new_request (client
, (char *)path
, XML_CMD_REALPATH
, &req
);
534 n
= xml_find_elements (client
, req
, xmlDocGetRootElement (doc
), rc
);
539 *result
= strv_dup (req
->args
);
542 *rc
= GPG_ERR_ENOMEM
;
548 xml_free_request (req
);
553 xml_find_text_node (xmlNodePtr node
)
557 if (n
&& n
->type
== XML_TEXT_NODE
)
560 for (n
= node
; n
; n
= n
->next
)
562 if (n
->type
== XML_TEXT_NODE
)
569 /* Create an element 'path' starting at 'node'. Returns the node of the final
573 xml_create_element_path (struct client_s
*client
, struct xml_request_s
*req
,
574 xmlNodePtr node
, char **path
, gpg_error_t
*rc
)
578 if (!xml_valid_element_path (path
, 0))
580 *rc
= GPG_ERR_INV_VALUE
;
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
)
589 for (p
= path
; p
&& *p
; p
++)
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
)
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
)
612 *rc
= create_new_element (client
, 0, node
, *p
, &node
);
623 /* Find the first XML_ELEMENT_NODE whose _name attribute matches 'element'. */
625 xml_find_element (struct client_s
*client
, xmlNodePtr node
, const char *element
,
632 if (!node
|| !element
)
634 *rc
= GPG_ERR_ELEMENT_NOT_FOUND
;
638 for (n
= node
; n
; n
= n
->next
)
640 if (n
->type
!= XML_ELEMENT_NODE
)
643 xmlChar
*a
= xml_attribute_value (n
, (xmlChar
*) "_name");
644 if (a
&& xmlStrEqual (a
, (xmlChar
*) element
))
647 *rc
= xml_acl_check (client
, n
);
648 return n
; // Not NULL since the node may be needed later
654 *rc
= GPG_ERR_ELEMENT_NOT_FOUND
;
659 xml_attribute_value (xmlNodePtr n
, xmlChar
* attr
)
661 xmlAttrPtr a
= xmlHasProp (n
, attr
);
666 if (!a
->children
|| !a
->children
->content
)
669 return xmlGetProp (n
, attr
);
673 xml_remove_user_attributes (struct client_s
*client
, xmlNodePtr n
)
676 xmlAttrPtr a
= n
->properties
;
680 if (!xml_reserved_attribute ((const char *)a
->name
))
682 rc
= xml_delete_attribute (client
, n
, a
->name
);
696 xml_valid_username (const char *str
)
699 return GPG_ERR_INV_VALUE
;
701 char **list
= str_split (str
, ",", 0);
705 return str
&& *str
? GPG_ERR_ENOMEM
: GPG_ERR_INV_VALUE
;
707 for (p
= list
; *p
; p
++)
711 if (*t
== '!' || *t
== '-')
714 int e
= xmlRegexpExec (username_re
, (xmlChar
*)t
);
715 if (e
!= 1 && *t
!= '/' && *t
!= '@') // client command name or group
718 return GPG_ERR_INV_USER_ID
;
727 xml_add_attribute (struct client_s
*client
, xmlNodePtr node
, const char *name
,
734 if (client
&& name
&& !strcmp (name
, "_target"))
736 rc
= xml_is_element_owner (client
, node
, 0);
742 else if (name
&& (!strcmp (name
, "_expire")
743 || !strcmp (name
, "_ctime")
744 || !strcmp (name
, "_mtime")))
749 if (!value
|| !*value
)
750 return GPG_ERR_INV_VALUE
;
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"))
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
);
778 buf
= str_asprintf ("%s,%s", user
, acl
);
781 rc
= xml_add_attribute (client
, node
, (char *) "_acl", buf
);
796 if (name
&& xmlStrEqual ((xmlChar
*) name
, (xmlChar
*) "_mtime"))
800 xml_remove_user_attributes (client
, node
);
802 buf
= str_asprintf ("%lu", time (NULL
));
803 rc
= xml_add_attribute (client
, node
, "_mtime", buf
);
809 append_path_to_list (struct client_s
*client
, struct element_list_s
*elements
,
810 const char *path
, gpg_error_t rc
)
813 struct string_s
*line
;
816 line
= string_new (NULL
);
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");
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
) ? "+" : "",
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
);
841 string_prepend (line
, path
);
843 l
= slist_append (elements
->list
, line
->str
);
847 if (!(elements
->flags
& XML_LIST_CHECK
))
853 string_free (line
, 0);
857 /* Removes the final element from the element prefix. */
859 pop_element_prefix (struct element_list_s
*elements
)
861 char *p
= strrchr (elements
->prefix
->str
, '\t');
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.
875 xml_create_path_list (struct client_s
*client
, xmlDocPtr doc
, xmlNodePtr node
,
876 struct element_list_s
*elements
, char *path
,
880 struct xml_request_s
*req
= NULL
;
881 gpg_error_t trc
= rc
;
884 target_orig
= rc
&& elements
->target
? str_dup (elements
->target
) : NULL
;
885 rc
= xml_new_request (client
, path
, XML_CMD_LIST
, &req
);
892 req
->data
= elements
;
893 n
= xml_find_elements (client
, req
,
894 node
? node
: xmlDocGetRootElement (doc
), &rc
);
897 find_element_node (n
->children
) ? XML_LIST_FLAG_CHILDREN
: 0;
901 xfree (elements
->target
);
902 elements
->target
= target_orig
;
904 elements
->flags
|= XML_LIST_HAS_TARGET
;
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
);
922 if (!rc
&& n
&& n
->children
)
924 for (n
= n
->children
; n
; n
= n
->next
)
926 xmlChar
*target
= NULL
;
927 xmlNodePtr tmp
= NULL
;
929 struct string_s
*newpath
;
931 if (n
->type
!= XML_ELEMENT_NODE
)
934 name
= xml_attribute_value (n
, (xmlChar
*) "_name");
938 newpath
= string_new (NULL
);
946 target
= xml_attribute_value (n
, (xmlChar
*) "_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;
956 if (RESUMABLE_ERROR (rc
))
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.
968 string_truncate (newpath
, 0);
969 s
= string_append_printf (newpath
, "%s", (char *)target
);
972 s
= string_append (newpath
, (char *)name
);
977 string_free (newpath
, 1);
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",
993 s
= string_append_printf (elements
->prefix
, "\t%s", name
);
998 string_free (newpath
, 1);
1003 elements
->prefix
= s
;
1005 rc
= xml_create_path_list (client
, doc
, target
? NULL
: tmp
,
1006 elements
, newpath
->str
, rc
);
1009 pop_element_prefix (elements
);
1012 string_free (newpath
, 1);
1020 xml_free_request (req
);
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;
1032 buf
= xmlBufferCreate ();
1035 return GPG_ERR_ENOMEM
;
1037 for (; value
? i
>= 0 : i
< nodes
->nodeNr
; value
? i
-- : i
++)
1039 xmlNodePtr n
= nodes
->nodeTab
[i
];
1045 if (!value
&& !attr
)
1047 if (xmlNodeDump (buf
, doc
, n
, 0, 0) == -1)
1050 return GPG_ERR_BAD_DATA
;
1058 xmlNodeSetContent (n
, value
);
1059 rc
= xml_update_element_mtime (client
, n
);
1067 rc
= xml_add_attribute (client
, n
, (char *) attr
, (char *) value
);
1069 rc
= xml_delete_attribute (client
, n
, attr
);
1081 xml_delete_attribute (struct client_s
*client
, xmlNodePtr n
,
1082 const xmlChar
*name
)
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
);
1104 return xml_update_element_mtime (client
, n
);
1108 xml_validate_import (struct client_s
*client
, xmlNodePtr node
)
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");
1126 rc
= xml_remove_user_attributes (client
, n
);
1128 rc
= xml_unlink_node (client
, n
->children
);
1134 a
= xmlGetProp (n
, (xmlChar
*) "_name");
1137 xmlChar
*t
= xmlGetNodePath (n
);
1139 log_write (_("Missing attribute '_name' at %s."), 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
,
1152 return GPG_ERR_INV_VALUE
;
1156 a
= xmlGetProp (n
, (xmlChar
*) "_ctime");
1158 attr_ctime (client
, n
);
1161 a
= xmlGetProp (n
, (xmlChar
*) "_mtime");
1163 xml_update_element_mtime (client
, n
);
1168 xmlChar
*t
= xmlGetNodePath (n
);
1169 xmlNodePtr tmp
= n
->next
;
1171 log_write (_("Warning: unknown element '%s' at %s. Removing."),
1174 (void)xml_unlink_node (client
, n
);
1175 return xml_validate_import (client
, tmp
);
1181 rc
= xml_validate_import (client
, n
->children
);
1191 xml_update_element_mtime (struct client_s
*client
, xmlNodePtr n
)
1193 return xml_add_attribute (client
, n
, NULL
, NULL
);
1197 xml_unlink_node (struct client_s
*client
, xmlNodePtr n
)
1205 rc
= xml_update_element_mtime (client
, n
->parent
);
1208 xmlFreeNodeList (n
);
1213 recurse_rename_attribute (xmlNodePtr root
, const char *old
, const char *name
)
1218 for (n
= root
; n
; n
= n
->next
)
1220 xmlAttrPtr attr
= xmlHasProp (n
, (xmlChar
*)old
);
1224 xmlChar
*a
= xml_attribute_value (n
, (xmlChar
*)old
);
1226 if (a
&& !xmlSetProp (n
, (xmlChar
*) name
, a
))
1227 rc
= GPG_ERR_BAD_DATA
;
1233 if (xmlRemoveProp (attr
) == -1)
1234 rc
= GPG_ERR_BAD_DATA
;
1241 rc
= recurse_rename_attribute (n
->children
, old
, name
);
1250 update_to_version (xmlDocPtr doc
)
1252 xmlNodePtr root
= xmlDocGetRootElement (doc
);
1253 xmlChar
*v
= xml_attribute_value (root
, (xmlChar
*)"_version");
1256 // Pwmd 3.2.0 or later. No updates needed ATM.
1263 rc
= recurse_rename_attribute (root
->children
, "target", "_target");
1265 rc
= recurse_rename_attribute (root
->children
, "expire", "_expire");
1268 rc
= recurse_rename_attribute (root
->children
, "expire_increment", "_age");
1274 xml_parse_doc (const char *xml
, size_t len
, xmlDocPtr
*result
)
1279 xmlResetLastError ();
1280 doc
= xmlReadMemory (xml
, len
, NULL
, "UTF-8", XML_PARSE_NOBLANKS
);
1281 if (!doc
&& xmlGetLastError ())
1282 return GPG_ERR_BAD_DATA
;
1286 rc
= update_to_version (doc
);
1288 rc
= GPG_ERR_ENOMEM
;
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().
1299 do_realpath (struct client_s
*client
, struct xml_request_s
*req
,
1300 xmlNodePtr node
, char **path
, const xmlChar
*target
,
1303 char **result
= NULL
;
1304 struct xml_request_s
*nreq
= NULL
;
1308 result
= str_split ((char *)target
, "\t", 0);
1311 *rc
= GPG_ERR_ENOMEM
;
1315 /* Append the original path to the target path. */
1318 char **p
= strv_catv (result
, path
);
1323 *rc
= GPG_ERR_ENOMEM
;
1330 line
= strv_join ("\t", result
);
1334 *rc
= GPG_ERR_ENOMEM
;
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
);
1348 nreq
->depth
= req
->depth
;
1350 node
= xml_find_elements (client
, nreq
, xmlDocGetRootElement (nreq
->doc
), rc
);
1352 /* Update the original client request path with the resolved one. */
1355 strv_free (req
->args
);
1356 req
->args
= nreq
->args
;
1360 xml_free_request (nreq
);
1366 node_to_element_path (xmlNodePtr node
)
1369 struct string_s
*str
= string_new ("");
1372 for (n
= node
; n
; n
= n
->parent
)
1376 for (child
= n
; child
; child
= child
->next
)
1378 if (child
->type
!= XML_ELEMENT_NODE
)
1381 xmlChar
*name
= xml_attribute_value (n
, (xmlChar
*) "_name");
1384 str
= string_prepend (str
, (char *) name
);
1386 name
= xml_attribute_value (n
, (xmlChar
*) "_target");
1388 str
= string_prepend (str
, "\t");
1390 str
= string_prepend (str
, "\t!");
1397 str
= string_erase (str
, 0, 1);
1399 string_free (str
, 0);
1404 /* Tests if 'path' references 'node' somewhere. */
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
;
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);
1418 return GPG_ERR_ENOMEM
;
1420 for (n
= n
->children
; n
&& *p
; p
++, n
= n
->next
)
1424 struct string_s
*str
= NULL
;
1426 t
= xml_find_element (client
, n
, *p
, &rc
);
1429 rc
= GPG_ERR_NO_DATA
;
1439 target
= xml_attribute_value (t
, (xmlChar
*)"_target");
1443 str
= string_new ((char *)target
);
1447 rc
= GPG_ERR_ENOMEM
;
1455 struct string_s
*tmp
;
1457 tmp
= string_append_printf (str
, "\t%s", *p
);
1460 string_free (str
, 1);
1462 return GPG_ERR_ENOMEM
;
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);
1482 * Recurse the element tree beginning at 'node' and find elements who point
1483 * back to 'dst'. Also follows target attributes.
1486 find_child_to_target (struct client_s
*client
, xmlDocPtr doc
, xmlNodePtr node
,
1487 xmlNodePtr dst
, unsigned depth
, int is_target
)
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
)
1499 if (n
->type
!= XML_ELEMENT_NODE
)
1502 if (n
== dst
&& depth
)
1503 return GPG_ERR_ELOOP
;
1505 target
= xml_attribute_value (n
, (xmlChar
*) "_target");
1509 char **result
= NULL
;
1511 tmp
= xml_resolve_path (client
, doc
, target
, &result
, &rc
);
1515 rc
= find_child_to_target (client
, doc
, tmp
, dst
, ++depth
, 1);
1518 else if (rc
== GPG_ERR_ELOOP
)
1520 gpg_error_t trc
= element_related (client
, doc
, (char *)target
,
1523 if (trc
&& trc
!= GPG_ERR_NO_DATA
)
1525 else if (trc
== GPG_ERR_NO_DATA
)
1531 if (rc
&& rc
!= GPG_ERR_ELEMENT_NOT_FOUND
)
1543 rc
= find_child_to_target (client
, doc
, n
->children
, dst
, ++depth
, 0);
1557 find_child_of_parent (xmlDocPtr doc
, xmlNodePtr src
, xmlNodePtr dst
)
1562 for (n
= src
; n
; n
= n
->next
)
1564 if (n
->type
!= XML_ELEMENT_NODE
)
1573 rc
= find_child_of_parent (doc
, n
->children
, dst
);
1580 find_parent_of_child (xmlDocPtr doc
, xmlNodePtr node
, xmlNodePtr dst
)
1586 for (n
= node
; n
; n
= n
->parent
)
1588 if (n
->type
!= XML_ELEMENT_NODE
)
1592 for (tmp
= n
->next
; tmp
; n
= n
->next
)
1594 if (n
->type
!= XML_ELEMENT_NODE
)
1598 return GPG_ERR_ELOOP
;
1603 return GPG_ERR_ELOOP
;
1610 xml_free_element_list (struct element_list_s
*elements
)
1616 int total
= slist_length (elements
->list
);
1619 for (i
= 0; i
< total
; i
++)
1621 char *tmp
= slist_nth_data (elements
->list
, i
);
1625 slist_free (elements
->list
);
1628 string_free (elements
->prefix
, 1);
1629 xfree (elements
->target
);
1634 /* This behaves like the LIST command with a specified path except it returns
1638 xml_check_recursion (struct client_s
*client
, struct xml_request_s
*req
)
1641 struct element_list_s
*elements
;
1643 char **dup
= strv_dup (req
->args
);
1646 return GPG_ERR_ENOMEM
;
1649 elements
= xcalloc (1, sizeof (struct element_list_s
));
1652 rc
= GPG_ERR_ENOMEM
;
1656 path
= strv_join ("\t", dup
);
1659 rc
= GPG_ERR_ENOMEM
;
1663 elements
->flags
|= XML_LIST_CHECK
;
1664 elements
->prefix
= string_new (NULL
);
1665 if (!elements
->prefix
)
1667 rc
= GPG_ERR_ENOMEM
;
1671 rc
= xml_create_path_list (client
, req
->doc
, xmlDocGetRootElement (req
->doc
),
1675 xml_free_element_list (elements
);
1682 xml_validate_target (struct client_s
*client
, xmlDocPtr doc
, const char *src
,
1686 xmlNodePtr src_node
;
1687 char **src_req
= NULL
;
1689 src_node
= xml_resolve_path (client
, doc
, (xmlChar
*) src
, &src_req
, &rc
);
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
);
1699 /* The destination element is a parent of the source element. */
1700 rc
= find_parent_of_child (doc
, src_node
->parent
, dst
);
1704 /* A destination child element contains a target to the source element. */
1706 rc
= find_child_to_target (client
, doc
,
1707 dst
->children
? dst
->children
: dst
, dst
, 0, 0);
1712 strv_free (src_req
);
1713 client
->flags
&= ~(FLAG_ACL_IGNORE
| FLAG_ACL_ERROR
);
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.
1723 xml_is_element_owner (struct client_s
*client
, xmlNodePtr n
, int strict
)
1725 xmlChar
*acl
= xml_attribute_value (n
, (xmlChar
*) "_acl");
1727 gpg_error_t rc
= GPG_ERR_EACCES
;
1732 return peer_is_invoker (client
);
1735 users
= str_split((char *)acl
, ",", 0);
1736 if (users
&& *users
)
1741 if (client
->thd
->remote
)
1742 user
= str_asprintf ("#%s", client
->thd
->tls
->fp
);
1744 user
= get_username (client
->thd
->peer
->uid
);
1746 user
= get_username (client
->thd
->peer
->uid
);
1750 rc
= !strcasecmp (*users
, user
) ? 0 : GPG_ERR_EACCES
;
1752 rc
= !strcmp (*users
, user
) ? 0 : GPG_ERR_EACCES
;
1755 rc
= peer_is_invoker (client
);
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.
1771 xml_find_elements (struct client_s
*client
, struct xml_request_s
*req
,
1772 xmlNodePtr node
, gpg_error_t
*rc
)
1780 if (max_recursion_depth
>= 1 && req
->depth
> max_recursion_depth
)
1783 *rc
= GPG_ERR_ELOOP
;
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
;
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?
1811 *rc
= GPG_ERR_ELEMENT_NOT_FOUND
;
1816 n
= xml_find_element (client
, n
, *p
, rc
);
1819 if (*rc
!= GPG_ERR_ELEMENT_NOT_FOUND
&& *rc
!= GPG_ERR_EACCES
)
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
))
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
);
1847 if (*rc
== GPG_ERR_ELEMENT_NOT_FOUND
)
1849 if (req
->cmd
== XML_CMD_STORE
|| req
->cmd
== XML_CMD_ATTR_TARGET
)
1852 n
= xml_create_element_path (client
, req
,
1854 && node
->parent
== xmlDocGetRootElement (req
->doc
)
1855 ? node
->parent
: last
, p
, rc
);
1857 req
->flags
|= XML_REQUEST_FLAG_CREATED
;
1864 if (req
->cmd
!= XML_CMD_LIST
&& req
->cmd
!= XML_CMD_REALPATH
)
1870 // Fall through to let LIST flags be appended.
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
)
1887 /* This is the final element in req->args. Done. */
1898 if (req
->cmd
== XML_CMD_REALPATH
)
1900 n
= do_realpath (client
, req
, n
, p
+1, target
, rc
);
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. */
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. */
1925 elements
->flags
|= XML_LIST_HAS_TARGET
;
1928 /* Prevent replacing any following target value with the
1929 * current target value. */
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
;
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
)
1953 *rc
= *rc
? *rc
: rc_orig
;
1959 /* Create a new path based on the value of the current "_target"
1961 char **nargs
= str_split ((char *)target
, "\t", 0);
1965 *rc
= GPG_ERR_INV_VALUE
;
1966 req
->data
= req
->cmd
== XML_CMD_LIST
? elements
: req
->data
;
1971 /* Append the remaining elements to the target attributes path. */
1972 char **nnargs
= strv_catv (nargs
, p
+1);
1974 char **orig
= req
->args
;
1976 tmp
= xml_find_elements (client
, req
, xmlDocGetRootElement (req
->doc
),
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
;
1991 target
= xmlStrdup ((xmlChar
*)e
->target
);
1994 if ((e
->flags
& XML_LIST_HAS_TARGET
))
1995 elements
->flags
|= XML_LIST_HAS_TARGET
;
2000 req
->data
= elements
;
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
);
2017 return *rc
? NULL
: tmp
;
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);
2034 return GPG_ERR_SYNTAX
;
2037 req
= xcalloc (1, sizeof (struct xml_request_s
));
2041 return GPG_ERR_ENOMEM
;
2046 req
->doc
= client
? client
->doc
: NULL
;
2052 xml_free_request (struct xml_request_s
*r
)
2057 strv_free (r
->args
);
2062 xml_reserved_attribute (const char *name
)
2066 for (i
= 0; reserved_attributes
[i
]; i
++)
2068 if (!strcmp (name
, reserved_attributes
[i
]))