summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am8
-rw-r--r--configure.ac7
-rw-r--r--docs/remctld.pod6
-rw-r--r--server/config.c83
-rw-r--r--server/remctld.c3
-rw-r--r--tests/TESTS1
-rw-r--r--tests/server/acl-unxgrp-t.c187
-rw-r--r--tests/server/getgrnam_r.c74
-rw-r--r--tests/server/getgrnam_r.h64
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