diff options
-rw-r--r-- | Makefile.am | 17 | ||||
-rw-r--r-- | src/confdb/confdb.h | 1 | ||||
-rw-r--r-- | src/config/SSSDConfig/__init__.py.in | 3 | ||||
-rwxr-xr-x | src/config/SSSDConfigTest.py | 6 | ||||
-rw-r--r-- | src/config/etc/sssd.api.conf | 4 | ||||
-rw-r--r-- | src/man/sssd.conf.5.xml | 31 | ||||
-rw-r--r-- | src/responder/common/responder.h | 10 | ||||
-rw-r--r-- | src/responder/common/responder_common.c | 136 | ||||
-rw-r--r-- | src/responder/pac/pacsrv.c | 19 | ||||
-rw-r--r-- | src/tests/responder_socket_access-tests.c | 178 |
10 files changed, 394 insertions, 11 deletions
diff --git a/Makefile.am b/Makefile.am index 4e78ae13..3c66b6cf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -123,7 +123,8 @@ if HAVE_CHECK util-tests \ debug-tests \ ipa_hbac-tests \ - sss_idmap-tests + sss_idmap-tests \ + responder_socket_access-tests if BUILD_PAC_RESPONDER non_interactive_check_based_tests += pac_responder-tests @@ -1028,6 +1029,20 @@ pac_responder_tests_LDADD = \ libsss_debug.la \ libsss_util.la \ libsss_test_common.la + +responder_socket_access_tests_SOURCES = \ + src/tests/responder_socket_access-tests.c \ + src/responder/common/responder_common.c \ + src/responder/common/responder_packet.c \ + src/responder/common/responder_cmd.c +responder_socket_access_tests_CFLAGS = \ + $(AM_CFLAGS) \ + $(CHECK_CFLAGS) +responder_socket_access_tests_LDADD = \ + $(CHECK_LIBS) \ + $(TALLOC_LIBS) \ + libsss_test_common.la \ + libsss_util.la endif stress_tests_SOURCES = \ diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index 8ed23565..6f6b730a 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -57,6 +57,7 @@ #define CONFDB_SERVICE_FORCE_TIMEOUT "force_timeout" #define CONFDB_SERVICE_RECON_RETRIES "reconnection_retries" #define CONFDB_SERVICE_FD_LIMIT "fd_limit" +#define CONFDB_SERVICE_ALLOWED_UIDS "allowed_uids" /* Monitor */ #define CONFDB_MONITOR_CONF_ENTRY "config/sssd" diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in index f6030678..18586ad6 100644 --- a/src/config/SSSDConfig/__init__.py.in +++ b/src/config/SSSDConfig/__init__.py.in @@ -88,6 +88,9 @@ option_strings = { # [ssh] 'ssh_hash_known_hosts': _('Whether to hash host names and addresses in the known_hosts file'), + # [pac] + 'allowed_uids': _('List of UIDs or user names allowed to access the PAC responder'), + # [provider] 'id_provider' : _('Identity provider'), 'auth_provider' : _('Authentication provider'), diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py index c1fbe481..dc4bcc96 100755 --- a/src/config/SSSDConfigTest.py +++ b/src/config/SSSDConfigTest.py @@ -1178,7 +1178,8 @@ class SSSDConfigTestSSSDConfig(unittest.TestCase): 'pam', 'sudo', 'autofs', - 'ssh'] + 'ssh', + 'pac'] for section in control_list: self.assertTrue(sssdconfig.has_section(section), "Section [%s] missing" % @@ -1270,7 +1271,8 @@ class SSSDConfigTestSSSDConfig(unittest.TestCase): 'nss', 'sudo', 'autofs', - 'ssh'] + 'ssh', + 'pac'] service_list = sssdconfig.list_services() for service in control_list: self.assertTrue(service in service_list, diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf index f1cd067d..35ebb2e4 100644 --- a/src/config/etc/sssd.api.conf +++ b/src/config/etc/sssd.api.conf @@ -63,6 +63,10 @@ autofs_negative_timeout = int, None, false # ssh service ssh_hash_known_hosts = bool, None, false +[pac] +# PAC responder +allowed_uids = str, None, false + [provider] #Available provider types id_provider = str, None, true diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml index 26748856..ed2d1e05 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -792,10 +792,6 @@ <refsect2 id='PAC_RESPONDER' condition="with_pac_responder"> <title>PAC responder configuration options</title> <para> - Currently there are no PAC responder specific configuration - options. - </para> - <para> <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/experimental.xml" /> </para> @@ -822,6 +818,33 @@ groups.</para></listitem> </itemizedlist> </para> + <para> + These options can be used to configure the PAC responder. + </para> + <variablelist> + <varlistentry> + <term>allowed_uids (string)</term> + <listitem> + <para> + Specifies the comma-separated list of UID values or + user names that are allowed to access the PAC + responder. User names are resolved to UIDs at + startup. + </para> + <para> + Default: 0 (only the root user is allowed to access + the PAC responder) + </para> + <para> + Please note that although the UID 0 is used as the + default it will be overwritten with this option. If + you still want to allow the root user to access the + PAC responder, which would be the typical case, you + have to add 0 to the list of allowed UIDs as well. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> </refsect1> diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h index 43a4fa02..c09262d1 100644 --- a/src/responder/common/responder.h +++ b/src/responder/common/responder.h @@ -98,6 +98,9 @@ struct resp_ctx { struct timeval get_domains_last_call; + size_t allowed_uids_count; + uid_t *allowed_uids; + void *pvt_ctx; }; @@ -289,4 +292,11 @@ struct tevent_req *sss_dp_get_domains_send(TALLOC_CTX *mem_ctx, const char *hint); errno_t sss_dp_get_domains_recv(struct tevent_req *req); + +errno_t csv_string_to_uid_array(TALLOC_CTX *mem_ctx, const char *cvs_string, + bool allow_sss_loop, + size_t *_uid_count, uid_t **_uids); + +errno_t check_allowed_uids(uid_t uid, size_t allowed_uids_count, + uid_t *allowed_uids); #endif /* __SSS_RESPONDER_H__ */ diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c index e5577403..e44c351a 100644 --- a/src/responder/common/responder_common.c +++ b/src/responder/common/responder_common.c @@ -33,6 +33,7 @@ #include <errno.h> #include <popt.h> #include "util/util.h" +#include "util/strtonum.h" #include "db/sysdb.h" #include "confdb/confdb.h" #include "dbus/dbus.h" @@ -104,15 +105,15 @@ static int client_destructor(struct cli_ctx *ctx) static errno_t get_client_cred(struct cli_ctx *cctx) { + cctx->client_euid = -1; + cctx->client_egid = -1; + cctx->client_pid = -1; + #ifdef HAVE_UCRED int ret; struct ucred client_cred; socklen_t client_cred_len = sizeof(client_cred); - cctx->client_euid = -1; - cctx->client_egid = -1; - cctx->client_pid = -1; - ret = getsockopt(cctx->cfd, SOL_SOCKET, SO_PEERCRED, &client_cred, &client_cred_len); if (ret != EOK) { @@ -136,6 +137,107 @@ static errno_t get_client_cred(struct cli_ctx *cctx) return EOK; } +errno_t check_allowed_uids(uid_t uid, size_t allowed_uids_count, + uid_t *allowed_uids) +{ + size_t c; + + if (allowed_uids == NULL) { + return EINVAL; + } + + for (c = 0; c < allowed_uids_count; c++) { + if (uid == allowed_uids[c]) { + return EOK; + } + } + + return EACCES; +} + +errno_t csv_string_to_uid_array(TALLOC_CTX *mem_ctx, const char *cvs_string, + bool allow_sss_loop, + size_t *_uid_count, uid_t **_uids) +{ + int ret; + size_t c; + char **list = NULL; + int list_size; + uid_t *uids = NULL; + char *endptr; + struct passwd *pwd; + + ret = split_on_separator(mem_ctx, cvs_string, ',', true, &list, &list_size); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("split_on_separator failed [%d][%s].\n", + ret, strerror(ret))); + goto done; + } + + uids = talloc_array(mem_ctx, uint32_t, list_size); + if (uids == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_array failed.\n")); + ret = ENOMEM; + goto done; + } + + if (allow_sss_loop) { + ret = unsetenv("_SSS_LOOPS"); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Failed to unset _SSS_LOOPS, getpwnam " + "might not find sssd users.\n")); + } + } + + for (c = 0; c < list_size; c++) { + errno = 0; + if (*list[c] == '\0') { + DEBUG(SSSDBG_OP_FAILURE, ("Empty list item.\n")); + ret = EINVAL; + goto done; + } + + uids[c] = strtouint32(list[c], &endptr, 10); + if (errno != 0 || *endptr != '\0') { + ret = errno; + if (ret == ERANGE) { + DEBUG(SSSDBG_OP_FAILURE, ("List item [%s] is out of range.\n", + list[c])); + goto done; + } + + errno = 0; + pwd = getpwnam(list[c]); + if (pwd == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("List item [%s] is neither a valid " + "UID nor a user name which cloud be " + "resolved by getpwnam().\n", list[c])); + ret = EINVAL; + goto done; + } + + uids[c] = pwd->pw_uid; + } + } + + *_uid_count = list_size; + *_uids = uids; + + ret = EOK; + +done: + if(setenv("_SSS_LOOPS", "NO", 0) != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("Failed to set _SSS_LOOPS.\n")); + } + talloc_free(list); + if (ret != EOK) { + talloc_free(uids); + } + + return ret; +} + + static void client_send(struct cli_ctx *cctx) { int ret; @@ -320,6 +422,32 @@ static void accept_fd_handler(struct tevent_context *ev, "client cred may not be available.\n")); } + if (rctx->allowed_uids_count != 0) { + if (cctx->client_euid == -1) { + DEBUG(SSSDBG_CRIT_FAILURE, ("allowed_uids configured, " \ + "but platform does not support " \ + "reading peer credential from the " \ + "socket. Access denied.\n")); + close(cctx->cfd); + talloc_free(cctx); + return; + } + + ret = check_allowed_uids(cctx->client_euid, rctx->allowed_uids_count, + rctx->allowed_uids); + if (ret != EOK) { + if (ret == EACCES) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Access denied for uid [%d].\n", + cctx->client_euid)); + } else { + DEBUG(SSSDBG_OP_FAILURE, ("check_allowed_uids failed.\n")); + } + close(cctx->cfd); + talloc_free(cctx); + return; + } + } + cctx->cfde = tevent_add_fd(ev, cctx, cctx->cfd, TEVENT_FD_READ, client_fd_handler, cctx); if (!cctx->cfde) { diff --git a/src/responder/pac/pacsrv.c b/src/responder/pac/pacsrv.c index db6e6b49..348fc6f4 100644 --- a/src/responder/pac/pacsrv.c +++ b/src/responder/pac/pacsrv.c @@ -45,6 +45,7 @@ #define SSS_PAC_PIPE_NAME "pac" #define DEFAULT_PAC_FD_LIMIT 8192 +#define DEFAULT_ALLOWED_UIDS "0" struct sbus_method monitor_pac_methods[] = { { MON_CLI_METHOD_PING, monitor_common_pong }, @@ -124,6 +125,7 @@ int pac_process_init(TALLOC_CTX *mem_ctx, int ret, max_retries; enum idmap_error_code err; int fd_limit; + char *uid_str; pac_ctx = talloc_zero(mem_ctx, struct pac_ctx); if (!pac_ctx) { @@ -147,6 +149,23 @@ int pac_process_init(TALLOC_CTX *mem_ctx, } pac_ctx->rctx->pvt_ctx = pac_ctx; + + ret = confdb_get_string(pac_ctx->rctx->cdb, pac_ctx->rctx, + CONFDB_PAC_CONF_ENTRY, CONFDB_SERVICE_ALLOWED_UIDS, + DEFAULT_ALLOWED_UIDS, &uid_str); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, ("Failed to get allowed UIDs.\n")); + return ret; + } + + ret = csv_string_to_uid_array(pac_ctx->rctx, uid_str, true, + &pac_ctx->rctx->allowed_uids_count, + &pac_ctx->rctx->allowed_uids); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, ("Failed to set allowed UIDs.\n")); + return ret; + } + /* Enable automatic reconnection to the Data Provider */ ret = confdb_get_int(pac_ctx->rctx->cdb, CONFDB_PAC_CONF_ENTRY, diff --git a/src/tests/responder_socket_access-tests.c b/src/tests/responder_socket_access-tests.c new file mode 100644 index 00000000..734bf1cb --- /dev/null +++ b/src/tests/responder_socket_access-tests.c @@ -0,0 +1,178 @@ +/* + SSSD - Test for routine to check to access to responder sockets + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <popt.h> +#include <check.h> +#include <string.h> + +#include "tests/common.h" +#include "responder/common/responder.h" + +struct cli_protocol_version *register_cli_protocol_version(void) +{ + static struct cli_protocol_version responder_test_cli_protocol_version[] = { + {0, NULL, NULL} + }; + + return responder_test_cli_protocol_version; +} + +struct s2a_data { + const char *inp; + int exp_ret; + size_t exp_count; + uid_t *exp_uids; +}; + +struct s2a_data s2a_data[] = { + {"1,2,3", 0, 3, (uid_t []){1, 2, 3}}, + {"1,2,3, 4,5 , 6 , 7 ", 0, 7, (uid_t []){1, 2, 3, 4, 5, 6, 7}}, + {"1", 0, 1, (uid_t []){1}}, + {"1, +2,3", 0, 3, (uid_t []){1, 2, 3}}, + {"1, -2,3", ERANGE, 0, NULL}, + {"1, 2ab, 3, 4", EINVAL, 0, NULL}, + {"1,", EINVAL, 0, NULL}, + {"", EINVAL, 0, NULL}, + {"1, 2, 4294967295", 0, 3, (uid_t []){1, 2, 4294967295U}}, + {"1, 2, 4294967296", ERANGE, 0, NULL}, + {"1, 2, root, 4, 5", 0, 5, (uid_t []){1, 2, 0, 4, 5}}, + {NULL, EINVAL, 0, NULL}, + {NULL, -1, 0, NULL} +}; + +START_TEST(resp_str_to_array_test) +{ + int ret; + size_t uid_count; + uid_t *uids = NULL; + size_t c; + size_t d; + + for (c = 0; s2a_data[c].exp_ret != -1; c++) { + ret = csv_string_to_uid_array(global_talloc_context, s2a_data[c].inp, + true, &uid_count, &uids); + fail_unless(ret == s2a_data[c].exp_ret, + "csv_string_to_uid_array failed [%d][%s].", ret, + strerror(ret)); + if (ret == 0) { + fail_unless(uid_count == s2a_data[c].exp_count, + "Wrong number of values, expected [%d], got [%d].", + s2a_data[c].exp_count, uid_count); + for (d = 0; d < s2a_data[c].exp_count; d++) { + fail_unless(uids[d] == s2a_data[c].exp_uids[d], + "Wrong value, expected [%d], got [%d].\n", + s2a_data[c].exp_uids[d], uids[d]); + } + } + + talloc_free(uids); + uids = NULL; + } + +} +END_TEST + +struct uid_check_data { + uid_t uid; + size_t allowed_uids_count; + uid_t *allowed_uids; + int exp_ret; +}; + +struct uid_check_data uid_check_data[] = { + {1, 3, (uid_t []){1, 2, 3}, 0}, + {2, 3, (uid_t []){1, 2, 3}, 0}, + {3, 3, (uid_t []){1, 2, 3}, 0}, + {4, 3, (uid_t []){1, 2, 3}, EACCES}, + {4, 0, NULL, EINVAL}, + {0, 0, NULL, -1} +}; + +START_TEST(check_allowed_uids_test) +{ + int ret; + size_t c; + + for (c = 0; uid_check_data[c].exp_ret == -1; c++) { + ret = check_allowed_uids(uid_check_data[c].uid, + uid_check_data[c].allowed_uids_count, + uid_check_data[c].allowed_uids); + fail_unless(ret == uid_check_data[c].exp_ret, + "check_allowed_uids failed [%d][%s].", ret, strerror(ret)); + } +} +END_TEST + +Suite *responder_test_suite(void) +{ + Suite *s = suite_create ("Responder socket access"); + + TCase *tc_utils = tcase_create("Utility test"); + + tcase_add_test(tc_utils, resp_str_to_array_test); + tcase_add_test(tc_utils, check_allowed_uids_test); + + suite_add_tcase(s, tc_utils); + + return s; +} + +int main(int argc, const char *argv[]) +{ + int opt; + int number_failed; + poptContext pc; + + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_MAIN_OPTS + POPT_TABLEEND + }; + + /* Set debug level to invalid value so we can deside if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + CONVERT_AND_SET_DEBUG_LEVEL(debug_level); + tests_set_cwd(); + + Suite *s = responder_test_suite(); + SRunner *sr = srunner_create(s); + + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} |