From a89df6fcb00fad47b94dc76339c3ff711cfe1a1d Mon Sep 17 00:00:00 2001 From: Ben Kibbey Date: Tue, 9 Sep 2014 18:50:09 -0400 Subject: [PATCH] Access control for the _acl attribute. The owner of the element is the first user listed in the ACL. Only this user and the invoking_user may modify it. --- doc/pwmd.html | 20 +++++++++------- doc/pwmd.texi | 20 +++++++++------- src/commands.c | 26 ++++++++++++++++++--- src/xml.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++--------- src/xml.h | 10 ++++---- 5 files changed, 116 insertions(+), 34 deletions(-) diff --git a/doc/pwmd.html b/doc/pwmd.html index 55436caf..b55833b6 100644 --- a/doc/pwmd.html +++ b/doc/pwmd.html @@ -160,15 +160,15 @@ Next: , Previous: Like a filesystem has an ACL to grant or limit access to directories or files for a specific user or group, pwmd can limit a local user, group or a TLS connection to a specific element path. This is done by storing -an ACL in the element attribute _acl. Its syntax is similar to the -allowed configuration parameter (see Configuration) with the +an ACL in the element attribute _acl. Its syntax is similar to the +allowed configuration parameter (see Configuration) with the exception that a TLS fingerprint hash is prefixed with a #.

-

Access is denied for all users that are not in the ACL of an element with the -exception of the invoking user (see the invoking_user and -invoking_tls configuration parameters (see Configuration)). The -connected client must be in the ACL for each element in an element -path otherwise an error is returned. As an example: +

Access is denied for all users that are not in the ACL of an element +with the exception of the invoking user (see the invoking_user and +invoking_tls configuration parameters (see Configuration)). The +connected client must be in the ACL for each element in an element path +otherwise an error is returned. As an example:

<element _name="test" _acl="username,-@wheel,root">
@@ -179,9 +179,13 @@ path otherwise an error is returned.  As an example:
 

The user username would be allowed access to the test element but not if it is a member of the wheel group. Although the root user, who may be a member of the wheel group, is allowed. No users -other than the invoking_user is allowed access to the child +other than the invoking_user is allowed access to the child element.

+

The first user listed in the ACL is considered the owner of the +element. This determines which clients may modify an _acl attribute. +The invoking_user may always modify an ACL. +


diff --git a/doc/pwmd.texi b/doc/pwmd.texi index c32efa43..b04e9774 100644 --- a/doc/pwmd.texi +++ b/doc/pwmd.texi @@ -128,15 +128,15 @@ reserved for the @code{target} attribute. @xref{Target Attribute}. Like a filesystem has an @abbr{ACL} to grant or limit access to directories or files for a specific user or group, @command{pwmd} can limit a local user, group or a TLS connection to a specific element path. This is done by storing -an ACL in the element attribute @code{_acl}. Its syntax is similar to the -@code{allowed} configuration parameter (@pxref{Configuration}) with the +an ACL in the element attribute @var{_acl}. Its syntax is similar to the +@var{allowed} configuration parameter (@pxref{Configuration}) with the exception that a TLS fingerprint hash is prefixed with a @key{#}. -Access is denied for all users that are not in the ACL of an element with the -exception of the invoking user (see the @code{invoking_user} and -@code{invoking_tls} configuration parameters (@pxref{Configuration})). The -connected client must be in the @abbr{ACL} for each element in an element -path otherwise an error is returned. As an example: +Access is denied for all users that are not in the @abbr{ACL} of an element +with the exception of the invoking user (see the @var{invoking_user} and +@var{invoking_tls} configuration parameters (@pxref{Configuration})). The +connected client must be in the @abbr{ACL} for each element in an element path +otherwise an error is returned. As an example: @example @@ -147,9 +147,13 @@ path otherwise an error is returned. As an example: The user @code{username} would be allowed access to the @code{test} element but not if it is a member of the @code{wheel} group. Although the @code{root} user, who may be a member of the @code{wheel} group, is allowed. No users -other than the @code{invoking_user} is allowed access to the @code{child} +other than the @var{invoking_user} is allowed access to the @code{child} element. +The first user listed in the @abbr{ACL} is considered the owner of the +element. This determines which clients may modify an @var{_acl} attribute. +The @var{invoking_user} may always modify an @abbr{ACL}. + @c Node, Next, Previous, Up @node Invoking, Configuration, Access Control, Top @chapter Invoking @command{pwmd} diff --git a/src/commands.c b/src/commands.c index 749abbcc..40fe76f8 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1837,7 +1837,17 @@ attribute_delete (struct client_s *client, char **req) goto fail; } - rc = delete_attribute (n, (xmlChar *) req[0]); + if (!strcmp (req[0], (char *) "_acl")) + { + rc = peer_is_self (client); + if (rc == GPG_ERR_EPERM) + rc = is_element_owner (client, n); + + if (rc) + goto fail; + } + + rc = delete_attribute (client, n, (xmlChar *) req[0]); fail: strv_free (path); @@ -2117,6 +2127,16 @@ attribute_set (struct client_s *client, char **req) goto fail; } + if (!strcmp (req[0], (char *) "_acl")) + { + rc = peer_is_self (client); + if (rc == GPG_ERR_EPERM) + rc = is_element_owner (client, n); + + if (rc) + goto fail; + } + rc = add_attribute (n, req[0], req[2]); fail: @@ -2627,7 +2647,7 @@ do_xpath (assuan_context_t ctx, char *line) goto fail; } - rc = recurse_xpath_nodeset (client->doc, xpath->result->nodesetval, + rc = recurse_xpath_nodeset (client, client->doc, xpath->result->nodesetval, (xmlChar *) xpath->req[1], &xpath->buf, 0, NULL); if (rc) @@ -2764,7 +2784,7 @@ do_xpathattr (assuan_context_t ctx, char *line) goto fail; } - rc = recurse_xpath_nodeset (client->doc, xpath->result->nodesetval, + rc = recurse_xpath_nodeset (client, client->doc, xpath->result->nodesetval, (xmlChar *) xpath->req[1], &xpath->buf, cmd, (xmlChar *) req[1]); diff --git a/src/xml.c b/src/xml.c index 01b09049..3547612b 100644 --- a/src/xml.c +++ b/src/xml.c @@ -1496,9 +1496,9 @@ fail: } gpg_error_t -recurse_xpath_nodeset (xmlDocPtr doc, xmlNodeSetPtr nodes, - xmlChar * value, xmlBufferPtr * result, int cmd, - const xmlChar * attr) +recurse_xpath_nodeset (struct client_s *client, xmlDocPtr doc, + xmlNodeSetPtr nodes, xmlChar * value, + xmlBufferPtr * result, int cmd, const xmlChar * attr) { int i = value ? nodes->nodeNr - 1 : 0; xmlBufferPtr buf; @@ -1540,7 +1540,7 @@ recurse_xpath_nodeset (xmlDocPtr doc, xmlNodeSetPtr nodes, if (!cmd) rc = add_attribute (n, (char *) attr, (char *) value); else - rc = delete_attribute (n, attr); + rc = delete_attribute (client, n, attr); if (rc) return rc; @@ -1552,7 +1552,7 @@ recurse_xpath_nodeset (xmlDocPtr doc, xmlNodeSetPtr nodes, } static gpg_error_t -convert_root_element (xmlNodePtr n) +convert_root_element (struct client_s *client, xmlNodePtr n) { xmlChar *a = xmlGetProp (n, (xmlChar *) "_name"); gpg_error_t rc; @@ -1579,7 +1579,7 @@ convert_root_element (xmlNodePtr n) if (rc) return rc; - rc = delete_attribute (n, (xmlChar *) "name"); + rc = delete_attribute (client, n, (xmlChar *) "name"); if (rc) return rc; @@ -1591,9 +1591,10 @@ convert_root_element (xmlNodePtr n) } gpg_error_t -delete_attribute (xmlNodePtr n, const xmlChar * name) +delete_attribute (struct client_s *client, xmlNodePtr n, const xmlChar * name) { xmlAttrPtr a; + gpg_error_t rc = 0; if ((a = xmlHasProp (n, name)) == NULL) return GPG_ERR_NOT_FOUND; @@ -1601,11 +1602,23 @@ delete_attribute (xmlNodePtr n, const xmlChar * name) if (xmlRemoveProp (a) == -1) return GPG_ERR_BAD_DATA; + if (client && xmlStrEqual (name, (xmlChar *) "_acl")) + { + char *user = create_acl_user (client); + + rc = add_attribute (n, (char *) "_acl", user); + xfree (user); + + if (rc) + return rc; + } + return update_element_mtime (n); } static gpg_error_t -convert_elements_recurse (xmlDocPtr doc, xmlNodePtr n, unsigned depth) +convert_elements_recurse (struct client_s *client, xmlDocPtr doc, + xmlNodePtr n, unsigned depth) { gpg_error_t rc; @@ -1657,7 +1670,7 @@ convert_elements_recurse (xmlDocPtr doc, xmlNodePtr n, unsigned depth) } else { - rc = convert_root_element (n); + rc = convert_root_element (client, n); if (rc) return rc; @@ -1666,7 +1679,7 @@ convert_elements_recurse (xmlDocPtr doc, xmlNodePtr n, unsigned depth) if (n->children) { - rc = convert_elements_recurse (doc, n, depth); + rc = convert_elements_recurse (client, doc, n, depth); if (rc) return rc; @@ -1686,7 +1699,7 @@ convert_pre_212_elements (xmlDocPtr doc) xmlNodePtr n = xmlDocGetRootElement (doc); log_write (_("Converting pre 2.12 data file...")); - return convert_elements_recurse (doc, n, 0); + return convert_elements_recurse (NULL, doc, n, 0); } gpg_error_t @@ -2113,3 +2126,42 @@ fail: strv_free (src_req); return rc; } + +/* The owner of the element is the first user listed in the _acl attribute + * list. acl_check() should be called before calling this function. An empty + * ACL is an error if the client is not invoking_user. + */ +gpg_error_t +is_element_owner (struct client_s *client, xmlNodePtr n) +{ + xmlChar *acl = node_has_attribute (n, (xmlChar *) "_acl"); + char **users; + gpg_error_t rc = GPG_ERR_EPERM; + + if (!acl || !*acl) + { + xmlFree (acl); + return 0; + } + + users = str_split((char *)acl, ",", 0); + if (users && *users) + { + char *user; + +#ifdef WITH_GNUTLS + if (client->thd->remote) + user = str_asprintf ("#%s", client->thd->tls->fp); + else + user = get_username (client->thd->peer->uid); +#else + user = get_username (client->thd->peer->uid); +#endif + + rc = !strcmp (*users, user) ? 0 : GPG_ERR_EPERM; + xfree (user); + } + + strv_free (users); + return rc; +} diff --git a/src/xml.h b/src/xml.h index 68ffc244..80820cca 100644 --- a/src/xml.h +++ b/src/xml.h @@ -70,13 +70,14 @@ int valid_element_path (char **path, int content); xmlNodePtr find_text_node (xmlNodePtr node); gpg_error_t create_path_list (struct client_s *, xmlDocPtr doc, struct element_list_s *elements, char *path); -gpg_error_t recurse_xpath_nodeset (xmlDocPtr doc, xmlNodeSetPtr nodes, - xmlChar * value, xmlBufferPtr * result, - int, const xmlChar *); +gpg_error_t recurse_xpath_nodeset (struct client_s *, xmlDocPtr doc, + xmlNodeSetPtr nodes, xmlChar * value, + xmlBufferPtr * result, int, const xmlChar *); gpg_error_t add_attribute (xmlNodePtr node, const char *name, const char *value); xmlChar *node_has_attribute (xmlNodePtr n, xmlChar * attr); -gpg_error_t delete_attribute (xmlNodePtr n, const xmlChar * name); +gpg_error_t delete_attribute (struct client_s *, xmlNodePtr n, + const xmlChar * name); gpg_error_t convert_pre_212_elements (xmlDocPtr doc); gpg_error_t validate_import (xmlNodePtr); xmlNodePtr find_element (struct client_s *, xmlNodePtr node, char *element, @@ -90,5 +91,6 @@ gpg_error_t build_realpath (struct client_s *, xmlDocPtr doc, char *line, gpg_error_t validate_target_attribute (struct client_s *, xmlDocPtr doc, const char *src, xmlNodePtr dst); int valid_xml_attribute (const char *str); +gpg_error_t is_element_owner (struct client_s *, xmlNodePtr); #endif -- 2.11.4.GIT