diff options
-rw-r--r-- | Makefile.am | 8 | ||||
-rw-r--r-- | configure.ac | 7 | ||||
-rw-r--r-- | docs/remctld.pod | 6 | ||||
-rw-r--r-- | server/config.c | 83 | ||||
-rw-r--r-- | server/remctld.c | 3 | ||||
-rw-r--r-- | tests/TESTS | 1 | ||||
-rw-r--r-- | tests/server/acl-unxgrp-t.c | 187 | ||||
-rw-r--r-- | tests/server/getgrnam_r.c | 74 | ||||
-rw-r--r-- | tests/server/getgrnam_r.h | 64 |
9 files changed, 432 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 901d56b..0620540 100644 --- a/Makefile.am +++ b/Makefile.am @@ -328,6 +328,7 @@ check_PROGRAMS = tests/runtests tests/client/api-t tests/client/ccache-t \ tests/portable/mkstemp-t tests/portable/setenv-t \ tests/portable/snprintf-t tests/portable/strlcat-t \ tests/portable/strlcpy-t tests/server/accept-t tests/server/acl-t \ + tests/server/acl-unxgrp-t \ tests/server/bind-t tests/server/config-t tests/server/continue-t \ tests/server/empty-t tests/server/env-t tests/server/errors-t \ tests/server/help-t tests/server/invalid-t tests/server/logging-t \ @@ -432,6 +433,13 @@ tests_server_acl_t_LDFLAGS = $(GPUT_LDFLAGS) $(PCRE_LDFLAGS) \ $(LIBEVENT_LDFLAGS) tests_server_acl_t_LDADD = tests/tap/libtap.a util/libutil.la \ portable/libportable.la $(GPUT_LIBS) $(PCRE_LIBS) $(LIBEVENT_LIBS) + +tests_server_acl_unxgrp_t_SOURCES = tests/server/acl-unxgrp-t.c tests/server/getgrnam_r.c $(SERVER_FILES) +tests_server_acl_unxgrp_t_LDFLAGS = $(GPUT_LDFLAGS) $(PCRE_LDFLAGS) \ + $(LIBEVENT_LDFLAGS) +tests_server_acl_unxgrp_t_LDADD = tests/tap/libtap.a util/libutil.la \ + portable/libportable.la $(GPUT_LIBS) $(PCRE_LIBS) $(LIBEVENT_LIBS) + tests_server_bind_t_LDFLAGS = $(KRB5_LDFLAGS) tests_server_bind_t_LDADD = client/libremctl.la tests/tap/libtap.a \ util/libutil.la portable/libportable.la $(KRB5_LIBS) diff --git a/configure.ac b/configure.ac index 04da6db..6d05b21 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,8 @@ dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl See LICENSE for licensing terms. -AC_PREREQ([2.64]) +AC_PREREQ([2.63]) +dnl AC_PREREQ([2.64]) AC_INIT([remctl], [3.8], [eagle@eyrie.org]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_LIBOBJ_DIR([portable]) @@ -88,6 +89,10 @@ dnl Check for regex libraries for pcre:* and regex:* ACL support. RRA_LIB_PCRE_OPTIONAL AC_CHECK_HEADER([regex.h], [AC_CHECK_FUNCS([regcomp])]) +dnl Check for POSIX getgrnam_r, get*nam_r functions. +AC_CHECK_FUNCS([getgrnam_r]) +AC_DEFINE([HAVE_GETGRNAM_R], [0]) + dnl General C library and networking probes. AC_SEARCH_LIBS([gethostbyname], [nsl]) AC_SEARCH_LIBS([socket], [socket], [], diff --git a/docs/remctld.pod b/docs/remctld.pod index 432c8ab..24ebd62 100644 --- a/docs/remctld.pod +++ b/docs/remctld.pod @@ -420,6 +420,12 @@ identity. To deny access, use the C<deny:regex:I<regex>> syntax. This method is supported only if a library for POSIX-compatible regular expressions was found when B<remctld> was built. +=item unxgrp + +This method is used to grant or deny access based on Unix group. +The user name is first sanitized (instances and REALM are removed from principal name), +and then compared to members of B<group>. + =back To see the list of ACL types supported by a particular build of diff --git a/server/config.c b/server/config.c index f6d7608..85bd795 100644 --- a/server/config.c +++ b/server/config.c @@ -27,6 +27,11 @@ # include <regex.h> #endif #include <sys/stat.h> +#ifdef HAVE_GETGRNAM_R +# include <sys/types.h> +# include <grp.h> +# include <unistd.h> +#endif #include <server/internal.h> #include <util/macros.h> @@ -885,6 +890,79 @@ acl_check_regex(const char *user, const char *data, const char *file, } #endif /* HAVE_REGCOMP */ +#ifdef HAVE_GETGRNAM_R +static enum config_status +acl_check_unxgrp (const char *user, const char *data, const char *file, + int lineno) +{ + struct group grp; + struct group *tempgrp = NULL; + int status = -1; + enum config_status result = CONFIG_ERROR; + long buf_len = -1; + char *buf = NULL; + size_t buf_sz = 1024; + + memset(&grp, 0x0, sizeof(grp)); + + buf_len = sysconf(_SC_GETGR_R_SIZE_MAX); + if (buf_len > 0) { + buf_sz = (size_t) buf_sz; + } + + buf = (char *) xmalloc(sizeof(char) * buf_sz); + memset(buf, 0x0, buf_sz); + + do { + status = getgrnam_r(data, &grp, buf, buf_sz, &tempgrp); + if (status != 0 && status != EINTR) { + warn("%s:%d: resolving unix group '%s' failed with status %d", file, + lineno, data, status); + result = CONFIG_ERROR; + goto die; + } + } while (status == EINTR); + + /* No group matching */ + if (tempgrp == NULL) { + result = CONFIG_NOMATCH; + } + else { + /* Sanitize and search for match in group members */ + char *cur_member = grp.gr_mem[0]; + char *p_user = user; + int i = 0; + char *sanitized_user = NULL; + + /* Sanitize username, remove instances and REALM */ + sanitized_user = xmalloc(sizeof(char)* strlen(user)); + memset(sanitized_user, 0x0, strlen(user)); + + while (p_user != NULL && *p_user != '\0') { + if (*p_user == '\x40' || *p_user == '\x2F') + break; + sanitized_user[i++] = *p_user; + p_user += 1; + } + + /* Check if sanitized user is within group members */ + i = 0; + while (cur_member != NULL && *cur_member != '\0') { + if (strcmp(cur_member, sanitized_user) == 0) { + result = CONFIG_SUCCESS; + goto die; + } + cur_member = grp.gr_mem[++i]; + } + + result = CONFIG_NOMATCH; + } + +die: + return result; +} +#endif /* HAVE_GETGRNAM_R */ + /* * The table relating ACL scheme names to functions. The first two ACL * schemes must remain in their current slots or the index constants set at @@ -909,6 +987,11 @@ static const struct acl_scheme schemes[] = { #else { "regex", NULL }, #endif +#ifdef HAVE_GETGRNAM_R + { "unxgrp", acl_check_unxgrp }, +#else + { "unxgrp", NULL }, +#endif { NULL, NULL } }; diff --git a/server/remctld.c b/server/remctld.c index 2fde503..3209b1e 100644 --- a/server/remctld.c +++ b/server/remctld.c @@ -107,6 +107,9 @@ usage(int status) #ifdef HAVE_REGCOMP fprintf(output, ", regex"); #endif +#ifdef HAVE_GETGRNAM_R + fprintf(output, ", unxgrp"); +#endif fprintf(output, "\n"); exit(status); } diff --git a/tests/TESTS b/tests/TESTS index b7e5c4e..f260ad1 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -23,6 +23,7 @@ portable/strlcat portable/strlcpy server/accept server/acl +server/acl-unxgrp server/bind server/config server/continue diff --git a/tests/server/acl-unxgrp-t.c b/tests/server/acl-unxgrp-t.c new file mode 100644 index 0000000..3d05a94 --- /dev/null +++ b/tests/server/acl-unxgrp-t.c @@ -0,0 +1,187 @@ +/** + * Test suite for the server ACL unxgrp checking. + * This file is part of the remctl project. + * + * Copyright 2014 - IN2P3 Computing Centre + * + * IN2P3 Computing Centre - written by <remi.ferrand@cc.in2p3.fr> + * + * This file is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + * + * See LICENSE for full licensing terms + */ + +#include <config.h> +#include <portable/system.h> + +#include <server/internal.h> +#include <tests/tap/basic.h> +#include <tests/tap/messages.h> + +#include <sys/types.h> +#include "getgrnam_r.h" + +#define STACK_GETGRNAM_RESP(grp, rc) \ +do { \ + struct faked_getgrnam_call s; \ + memset(&s, 0x0, sizeof(s)); \ + s.getgrnam_grp = grp; \ + s.getgrnam_r_rc = rc; \ + memcpy(&getgrnam_r_responses[call_idx], &s, sizeof(s)); \ + call_idx++; \ +} while(0) + +#define RESET_GETGRNAM_CALL_IDX(v) \ +do { \ + call_idx = v; \ +} while(0) + +/** + * Dummy group definitions used to override return value of getgrnam + */ +struct group emptygrp = { "emptygrp", NULL, 42, (char *[]) { NULL } }; +struct group goodguys = { "goodguys", NULL, 42, (char *[]) { "remi", "eagle", NULL } }; +struct group badguys = { "badguys", NULL, 42, (char *[]) { "darth-vader", "darth-maul", "boba-fett", NULL } }; + +int +main(void) +{ + struct rule rule = { + NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, NULL, NULL, NULL + }; + const char *acls[5]; + + memset(&getgrnam_r_responses, 0x0, sizeof(getgrnam_r_responses)); + + plan(11); + if (chdir(getenv("SOURCE")) < 0) + sysbail("can't chdir to SOURCE"); + + rule.file = (char *) "TEST"; + rule.acls = (char **) acls; + + acls[0] = "unxgrp:foobargroup"; + acls[1] = NULL; + +#ifdef HAVE_GETGRNAM_R + + /* Check behavior with empty groups */ + STACK_GETGRNAM_RESP(&emptygrp, 0); + RESET_GETGRNAM_CALL_IDX(0); + acls[0] = "unxgrp:emptygrp"; + acls[1] = NULL; + + ok(!server_config_acl_permit(&rule, "someone@EXAMPLE.ORG"), + "... with empty group"); + + RESET_GETGRNAM_CALL_IDX(0); + + /* Check behavior when user is expected to be in supplied group ... */ + STACK_GETGRNAM_RESP(&goodguys, 0); + RESET_GETGRNAM_CALL_IDX(0); + acls[0] = "unxgrp:goodguys"; + acls[1] = NULL; + ok(server_config_acl_permit(&rule, "remi@EXAMPLE.ORG"), + "... with user within group"); + + RESET_GETGRNAM_CALL_IDX(0); + + /* ... and when it's not ... */ + ok(!server_config_acl_permit(&rule, "someoneelse@EXAMPLE.ORG"), + "... with user not in group"); + + RESET_GETGRNAM_CALL_IDX(0); + + /* ... and when principal is complex */ + ok(server_config_acl_permit(&rule, "remi/admin@EXAMPLE.ORG"), + "... with principal with instances but main user in group"); + + RESET_GETGRNAM_CALL_IDX(0); + + /* Check behavior when syscall fails */ + STACK_GETGRNAM_RESP(&goodguys, 2); + RESET_GETGRNAM_CALL_IDX(0); + errors_capture(); + ok(!server_config_acl_permit(&rule, "remi@EXAMPLE.ORG"), + "... with getgrnam_r failing"); + is_string("TEST:0: resolving unix group 'goodguys' failed with status 2\n", errors, + "... with getgrnam_r error handling"); + errors_uncapture(); + + RESET_GETGRNAM_CALL_IDX(0); + + /* Check that deny group works as expected */ + STACK_GETGRNAM_RESP(&badguys, 0); + RESET_GETGRNAM_CALL_IDX(0); + acls[0] = "deny:unxgrp:badguys"; + acls[1] = NULL; + + ok(!server_config_acl_permit(&rule, "boba-fett@EXAMPLE.ORG"), + "... with denied user in group"); + + RESET_GETGRNAM_CALL_IDX(0); + + ok(!server_config_acl_permit(&rule, "remi@EXAMPLE.ORG"), + "... with user not in denied group but not allowed"); + + RESET_GETGRNAM_CALL_IDX(0); + + /* Check that both deny and "allow" pragma work together */ + STACK_GETGRNAM_RESP(&goodguys, 0); + STACK_GETGRNAM_RESP(&badguys, 0); + RESET_GETGRNAM_CALL_IDX(0); + acls[0] = "unxgrp:goodguys"; + acls[1] = "deny:unxgrp:badguys"; + acls[2] = NULL; + + ok(server_config_acl_permit(&rule, "eagle@EXAMPLE.ORG"), + "... with user within group plus a deny pragma"); + + RESET_GETGRNAM_CALL_IDX(0); + + ok(!server_config_acl_permit(&rule, "darth-maul@EXAMPLE.ORG"), + "... with user in denied group plus a allow group pragma"); + + RESET_GETGRNAM_CALL_IDX(0); + + ok(!server_config_acl_permit(&rule, "anyoneelse@EXAMPLE.ORG"), + "... with user neither in allowed or denied group"); + + RESET_GETGRNAM_CALL_IDX(0); + +#else + errors_capture(); + ok(!server_config_acl_permit(&rule, "foobaruser@EXAMPLE.ORG"), + "UNXGRP"); + is_string("TEST:0: ACL scheme 'unxgrp' is not supported\n", errors, + "...with not supported error"); + errors_uncapture(); + skip_block(9, "UNXGRP support not configured"); +#endif + + return 0; +} diff --git a/tests/server/getgrnam_r.c b/tests/server/getgrnam_r.c new file mode 100644 index 0000000..66801c0 --- /dev/null +++ b/tests/server/getgrnam_r.c @@ -0,0 +1,74 @@ +/** + * Fake getgrnam_r function. + * + * This file is part of the remctl project. + * + * Copyright 2014 - IN2P3 Computing Centre + * + * IN2P3 Computing Centre - written by <remi.ferrand@cc.in2p3.fr> + * + * This file is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + * + * See LICENSE for full licensing terms + */ + + +#include "getgrnam_r.h" + +int call_idx = 0; // Call index that allow + // to fake multiple calls to + // getgrnam_r + +struct faked_getgrnam_call getgrnam_r_responses[PRE_ALLOC_ANSWERS_MAX_IDX]; + +/** + * Fake system function and only feed + * required structure with the one provided + * in global variable. + * This is only used for the tests suite. + */ +int getgrnam_r(const char *name, struct group *grp, + char *buf, size_t buflen, struct group **result) { + + int rc = -1; + + if (call_idx < 0) + call_idx = 0; + + if (call_idx >= PRE_ALLOC_ANSWERS_MAX_IDX) + call_idx = 0; + + printf("call_idx = %d\n", call_idx); + + *grp = *(getgrnam_r_responses[call_idx].getgrnam_grp); + *result = grp; + rc = getgrnam_r_responses[call_idx].getgrnam_r_rc; + + call_idx++; + + return rc; +} diff --git a/tests/server/getgrnam_r.h b/tests/server/getgrnam_r.h new file mode 100644 index 0000000..50ba4ef --- /dev/null +++ b/tests/server/getgrnam_r.h @@ -0,0 +1,64 @@ +/** + * This file is part of the remctl project. + * + * Copyright 2014 - IN2P3 Computing Centre + * + * IN2P3 Computing Centre - written by <remi.ferrand@cc.in2p3.fr> + * + * This file is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + * + * See LICENSE for full licensing terms + */ + +#ifndef TESTS_SERVER_FAKE_GETGRNAM_R_H +#define TESTS_SERVER_FAKE_GETGRNAM_R_H + +#include <stdio.h> +#include <sys/types.h> +#include <grp.h> + +#define PRE_ALLOC_ANSWERS_MAX_IDX 5 + +struct faked_getgrnam_call { + struct group *getgrnam_grp; // Group struct returned + int getgrnam_r_rc; // syscall return code +}; + +/* Call index iterator + * incremented at every getgrnam_r call + */ +extern int call_idx; + +/* Stack of pre-made answers + * The current answer is decided based on *call_idx* value + */ +extern struct faked_getgrnam_call getgrnam_r_responses[PRE_ALLOC_ANSWERS_MAX_IDX]; + +int getgrnam_r(const char *name, struct group *grp, + char *buf, size_t buflen, struct group **result); + +#endif |