summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am24
-rw-r--r--src/tests/krb5_child-test.c551
2 files changed, 575 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index 8af213e5..7bdc5930 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -120,6 +120,7 @@ endif
check_PROGRAMS = \
stress-tests \
+ krb5-child-test \
$(non_interactive_check_based_tests)
PYTHON_TESTS =
@@ -972,6 +973,29 @@ stress_tests_LDADD = \
libsss_util.la \
libsss_test_common.la
+krb5_child_test_SOURCES = \
+ src/tests/krb5_child-test.c \
+ src/providers/krb5/krb5_utils.c \
+ src/providers/krb5/krb5_child_handler.c \
+ src/providers/krb5/krb5_become_user.c \
+ src/providers/krb5/krb5_common.c \
+ src/util/sss_krb5.c \
+ src/providers/data_provider_fo.c \
+ src/providers/data_provider_opts.c \
+ src/providers/data_provider_callbacks.c \
+ $(SSSD_FAILOVER_OBJ)
+krb5_child_test_CFLAGS = \
+ $(AM_CFLAGS) \
+ -DKRB5_CHILD_DIR=\"$(builddir)\" \
+ $(CHECK_CFLAGS)
+krb5_child_test_LDADD = \
+ $(SSSD_LIBS)\
+ $(CARES_LIBS) \
+ $(KRB5_LIBS) \
+ $(CHECK_LIBS) \
+ libsss_util.la \
+ libsss_test_common.la
+
noinst_PROGRAMS = pam_test_client
if BUILD_SUDO
noinst_PROGRAMS += sss_sudo_cli
diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c
new file mode 100644
index 00000000..4dde996a
--- /dev/null
+++ b/src/tests/krb5_child-test.c
@@ -0,0 +1,551 @@
+/*
+ SSSD
+
+ Unit tests - exercise the krb5 child
+
+ Authors:
+ Jakub Hrozek <jhrozek@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 <stdio.h>
+#include <stdlib.h>
+#include <talloc.h>
+#include <popt.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "util/util.h"
+#include "src/tools/tools_util.h"
+
+/* Interfaces being tested */
+#include "providers/krb5/krb5_auth.h"
+#include "providers/krb5/krb5_common.h"
+#include "providers/krb5/krb5_utils.h"
+
+extern struct dp_option default_krb5_opts[];
+
+static krb5_context krb5_error_ctx;
+#define KRB5_DEBUG(level, krb5_error) do { \
+ const char * __krb5_error_msg; \
+ __krb5_error_msg = sss_krb5_get_error_message(krb5_error_ctx, krb5_error); \
+ DEBUG(level, ("%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg)); \
+ sss_log(SSS_LOG_ERR, "%s", __krb5_error_msg); \
+ sss_krb5_free_error_message(krb5_error_ctx, __krb5_error_msg); \
+} while(0)
+
+#define CHECK_KRET(kret, err) do { \
+ if (kret) { \
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kret); \
+ return err; \
+ } \
+} while(0) \
+
+#define CHECK_KRET_L(kret, err, label) do { \
+ if (kret) { \
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kret); \
+ goto label; \
+ } \
+} while(0) \
+
+struct krb5_child_test_ctx {
+ struct tevent_context *ev;
+ struct krb5child_req *kr;
+
+ bool done;
+ errno_t child_ret;
+
+ uint8_t *buf;
+ ssize_t len;
+ struct krb5_child_response *res;
+};
+
+static errno_t
+setup_krb5_child_test(TALLOC_CTX *mem_ctx, struct krb5_child_test_ctx **_ctx)
+{
+ struct krb5_child_test_ctx *ctx;
+
+ ctx = talloc_zero(mem_ctx, struct krb5_child_test_ctx);
+ if (!ctx) return ENOMEM;
+
+ ctx->ev = tevent_context_init(ctx);
+ if (ctx->ev == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Could not init tevent context"));
+ talloc_free(ctx);
+ return EFAULT;
+ }
+
+ *_ctx = ctx;
+ return EOK;
+}
+
+int re_destructor(void *memctx)
+{
+ struct krb5_ctx *ctx = (struct krb5_ctx *) memctx;
+
+ if (ctx->illegal_path_re) {
+ pcre_free(ctx->illegal_path_re);
+ ctx->illegal_path_re = NULL;
+ }
+ return 0;
+}
+
+static struct krb5_ctx *
+create_dummy_krb5_ctx(TALLOC_CTX *mem_ctx, const char *realm)
+{
+ struct krb5_ctx *krb5_ctx;
+ const char *errstr;
+ int errval;
+ int errpos;
+ int i;
+ errno_t ret;
+
+ krb5_ctx = talloc_zero(mem_ctx, struct krb5_ctx);
+ if (!krb5_ctx) return NULL;
+
+ krb5_ctx->illegal_path_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
+ &errval, &errstr, &errpos, NULL);
+ if (krb5_ctx->illegal_path_re == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("Invalid Regular Expression pattern at position %d. "
+ "(Error: %d [%s])\n", errpos, errval, errstr));
+ goto fail;
+ }
+ talloc_set_destructor((TALLOC_CTX *) krb5_ctx, re_destructor);
+
+ /* Kerberos options */
+ krb5_ctx->opts = talloc_zero_array(krb5_ctx, struct dp_option, KRB5_OPTS);
+ if (!krb5_ctx->opts) goto fail;
+ for (i = 0; i < KRB5_OPTS; i++) {
+ krb5_ctx->opts[i].opt_name = default_krb5_opts[i].opt_name;
+ krb5_ctx->opts[i].type = default_krb5_opts[i].type;
+ krb5_ctx->opts[i].def_val = default_krb5_opts[i].def_val;
+ switch (krb5_ctx->opts[i].type) {
+ case DP_OPT_STRING:
+ ret = dp_opt_set_string(krb5_ctx->opts, i,
+ default_krb5_opts[i].def_val.string);
+ break;
+ case DP_OPT_BLOB:
+ ret = dp_opt_set_blob(krb5_ctx->opts, i,
+ default_krb5_opts[i].def_val.blob);
+ break;
+ case DP_OPT_NUMBER:
+ ret = dp_opt_set_int(krb5_ctx->opts, i,
+ default_krb5_opts[i].def_val.number);
+ break;
+ case DP_OPT_BOOL:
+ ret = dp_opt_set_bool(krb5_ctx->opts, i,
+ default_krb5_opts[i].def_val.boolean);
+ break;
+ }
+ if (ret) goto fail;
+ }
+
+ ret = dp_opt_set_string(krb5_ctx->opts, KRB5_REALM, realm);
+ if (ret) goto fail;
+
+ return krb5_ctx;
+
+fail:
+ talloc_free(krb5_ctx);
+ return NULL;
+}
+
+static struct pam_data *
+create_dummy_pam_data(TALLOC_CTX *mem_ctx, const char *user,
+ const char *password)
+{
+ struct pam_data *pd;
+
+ pd = talloc_zero(mem_ctx, struct pam_data);
+ if (!pd) goto fail;
+
+ pd->cmd = SSS_PAM_AUTHENTICATE;
+ pd->user = talloc_strdup(pd, user);
+ if (!pd->user) goto fail;
+
+ pd->authtok = discard_const(talloc_strdup(pd, password));
+ if (!pd->authtok) goto fail;
+ pd->authtok_size = strlen(password);
+ pd->authtok_type = SSS_AUTHTOK_TYPE_PASSWORD;
+ DEBUG(SSSDBG_FUNC_DATA, ("Authtok [%s] len [%d]\n",
+ pd->authtok, pd->authtok_size));
+
+ return pd;
+
+fail:
+ talloc_free(pd);
+ return NULL;
+}
+
+static struct krb5child_req *
+create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
+ const char *password, const char *realm,
+ const char *ccname, const char *ccname_template,
+ int timeout)
+{
+ struct krb5child_req *kr;
+ struct passwd *pwd;
+ bool private;
+ errno_t ret;
+
+ /* The top level child request */
+ kr = talloc_zero(mem_ctx, struct krb5child_req);
+ if (!kr) return NULL;
+
+ pwd = getpwnam(user);
+ if (!pwd) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ ("Cannot get info on user [%s]\n", user));
+ goto fail;
+ }
+
+ kr->uid = pwd->pw_uid;
+ kr->gid = pwd->pw_gid;
+
+ /* The Kerberos context */
+ kr->krb5_ctx = create_dummy_krb5_ctx(kr, realm);
+ /* PAM Data structure */
+ kr->pd = create_dummy_pam_data(kr, user, password);
+
+ ret = krb5_get_simple_upn(kr, kr->krb5_ctx, kr->pd->user, &kr->upn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, ("krb5_get_simple_upn failed.\n"));
+ goto fail;
+ }
+
+ /* Override options with what was provided by the user */
+ if (ccname_template) {
+ ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL,
+ ccname_template);
+ if (ret != EOK) goto fail;
+ }
+
+ if (timeout) {
+ dp_opt_set_int(kr->krb5_ctx->opts, KRB5_AUTH_TIMEOUT, timeout);
+ }
+
+ if (!ccname) {
+ kr->ccname = expand_ccname_template(kr, kr,
+ dp_opt_get_cstring(kr->krb5_ctx->opts,
+ KRB5_CCNAME_TMPL),
+ true, true, &private);
+ if (!kr->ccname) goto fail;
+
+ DEBUG(SSSDBG_FUNC_DATA, ("ccname [%s] uid [%llu] gid [%llu]\n",
+ kr->ccname, kr->uid, kr->gid));
+
+ ret = create_ccache_dir(kr, kr->ccname,
+ kr->krb5_ctx->illegal_path_re,
+ kr->uid, kr->gid, private);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, ("create_ccache_dir failed.\n"));
+ goto fail;
+ }
+ }
+
+ return kr;
+
+fail:
+ talloc_free(kr);
+ return NULL;
+}
+
+static void
+child_done(struct tevent_req *req)
+{
+ struct krb5_child_test_ctx *ctx = tevent_req_callback_data(req,
+ struct krb5_child_test_ctx);
+ errno_t ret;
+
+ ret = handle_child_recv(req, ctx, &ctx->buf, &ctx->len);
+ talloc_free(req);
+ ctx->done = true;
+ ctx->child_ret = ret;
+}
+
+static void
+printtime(krb5_timestamp ts)
+{
+ krb5_error_code kret;
+ char timestring[BUFSIZ];
+ char fill = '\0';
+
+ kret = krb5_timestamp_to_sfstring(ts, timestring, BUFSIZ, &fill);
+ if (kret) {
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kret);
+ }
+ printf("%s", timestring);
+}
+
+static void
+print_creds(krb5_context kcontext, krb5_creds *cred, const char *defname)
+{
+ krb5_error_code kret;
+ char *name = NULL;
+ char *sname = NULL;
+
+ kret = krb5_unparse_name(kcontext, cred->client, &name);
+ CHECK_KRET_L(kret, EIO, done);
+
+ kret = krb5_unparse_name(kcontext, cred->server, &sname);
+ CHECK_KRET_L(kret, EIO, done);
+
+ if (!cred->times.starttime) {
+ cred->times.starttime = cred->times.authtime;
+ }
+
+
+ printf("\t\t%s\n", sname);
+ printf("\t\tValid from\t"); printtime(cred->times.starttime);
+ printf("\n\t\tValid until\t"); printtime(cred->times.endtime);
+ printf("\n");
+
+ if (strcmp(name, defname)) {
+ printf("\t\tfor client %s", name);
+ }
+
+done:
+ krb5_free_unparsed_name(kcontext, name);
+ krb5_free_unparsed_name(kcontext, sname);
+}
+
+static errno_t
+print_ccache(const char *cc)
+{
+ krb5_cc_cursor cur;
+ krb5_ccache cache = NULL;
+ krb5_error_code kret;
+ krb5_context kcontext = NULL;
+ krb5_principal_data *princ = NULL;
+ krb5_creds creds;
+ char *defname = NULL;
+ int i = 1;
+ errno_t ret;
+
+ kret = krb5_init_context(&kcontext);
+ CHECK_KRET_L(kret, EIO, done);
+
+ kret = krb5_cc_resolve(kcontext, cc, &cache);
+ CHECK_KRET_L(kret, EIO, done);
+
+ kret = krb5_cc_get_principal(kcontext, cache, &princ);
+ CHECK_KRET_L(kret, EIO, done);
+
+ kret = krb5_unparse_name(kcontext, princ, &defname);
+ CHECK_KRET_L(kret, EIO, done);
+
+ printf("\nTicket cache: %s:%s\nDefault principal: %s\n\n",
+ krb5_cc_get_type(kcontext, cache),
+ krb5_cc_get_name(kcontext, cache), defname);
+
+ kret = krb5_cc_start_seq_get(kcontext, cache, &cur);
+ CHECK_KRET_L(kret, EIO, done);
+
+ while (!(kret = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) {
+ printf("Ticket #%d:\n", i);
+ print_creds(kcontext, &creds, defname);
+ krb5_free_cred_contents(kcontext, &creds);
+ }
+
+ krb5_cc_end_seq_get(kcontext, cache, &cur);
+ CHECK_KRET_L(kret, EIO, done);
+
+ ret = EOK;
+done:
+ krb5_cc_close(kcontext, cache);
+ krb5_free_unparsed_name(kcontext, defname);
+ krb5_free_principal(kcontext, princ);
+ krb5_free_context(kcontext);
+ return ret;
+}
+
+static void
+remove_ccache(const char *cc)
+{
+ size_t offset = 0;
+ errno_t ret;
+
+ if (strncmp(cc, "FILE:", 5) == 0) {
+ offset = 5;
+ }
+ if (cc[offset] != '/') {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ ("ccname [%s] does not contain absolute path?\n", cc));
+ }
+
+ errno = 0;
+ ret = unlink(cc+offset);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ ("unlink [%s] failed [%d]: %s\n", cc, ret, strerror(ret)));
+ }
+}
+
+int
+main(int argc, const char *argv[])
+{
+ int opt;
+ errno_t ret;
+ struct krb5_child_test_ctx *ctx = NULL;
+ struct tevent_req *req;
+
+ int pc_debug = 0;
+ int pc_timeout = 0;
+ const char *pc_user = NULL;;
+ const char *pc_passwd = NULL;;
+ const char *pc_realm = NULL;;
+ const char *pc_ccname = NULL;;
+ const char *pc_ccname_tp = NULL;;
+ char *password = NULL;
+ bool rm_ccache = true;
+
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_debug, 0,
+ "The debug level to run with", NULL },
+ { "user", 'u', POPT_ARG_STRING, &pc_user, 0,
+ "The user to log in as", NULL },
+ { "password", 'w', POPT_ARG_STRING, &pc_passwd, 0,
+ "The authtok to use", NULL },
+ { "ask-password", 'W', POPT_ARG_NONE, NULL, 'W',
+ "Ask interactively for authtok", NULL },
+ { "ccname", 'c', POPT_ARG_STRING, &pc_ccname, 0,
+ "Force usage of a certain credential cache", NULL },
+ { "ccname-template", 't', POPT_ARG_STRING, &pc_ccname_tp, 0,
+ "Specify the credential cache template", NULL },
+ { "realm", 'r', POPT_ARG_STRING, &pc_realm, 0,
+ "The Kerberos realm to use", NULL },
+ { "keep-ccache", 'k', POPT_ARG_NONE, NULL, 'k',
+ "Do not delete the ccache when the tool finishes", NULL },
+ { "timeout", '\0', POPT_ARG_INT, &pc_timeout, 0,
+ "The timeout for the child, in seconds", NULL },
+ POPT_TABLEEND
+ };
+
+ debug_prg_name = argv[0];
+ pc = poptGetContext(NULL, argc, argv, long_options, 0);
+
+ while ((opt = poptGetNextOpt(pc)) > 0) {
+ switch(opt) {
+ case 'W':
+ errno = 0;
+ password = getpass("Enter password:");
+ if (!password) {
+ return 1;
+ }
+ break;
+ case 'k':
+ rm_ccache = false;
+ break;
+ default:
+ DEBUG(SSSDBG_FATAL_FAILURE, ("Unexpected option\n"));
+ return 1;
+ }
+ }
+
+ debug_level = debug_convert_old_level(pc_debug);
+
+ if (opt != -1) {
+ poptPrintUsage(pc, stderr, 0);
+ fprintf(stderr, "%s", poptStrerror(opt));
+ return 1;
+ }
+
+ if (!pc_user) {
+ DEBUG(SSSDBG_FATAL_FAILURE, ("Please specify the user\n"));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+
+ if (!pc_realm) {
+ DEBUG(SSSDBG_FATAL_FAILURE, ("Please specify the realm\n"));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+
+ if (!password && !pc_passwd) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ ("Password was not provided or asked for\n"));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+
+ if (pc_ccname && pc_ccname_tp) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Both ccname and ccname template specified, "
+ "will prefer ccname\n"));
+ }
+
+ ret = setup_krb5_child_test(NULL, &ctx);
+ if (ret != EOK) {
+ poptPrintUsage(pc, stderr, 0);
+ fprintf(stderr, "%s", poptStrerror(opt));
+ return 3;
+ }
+
+ ctx->kr = create_dummy_req(ctx, pc_user, password ? password : pc_passwd,
+ pc_realm, pc_ccname, pc_ccname_tp, pc_timeout);
+ if (!ctx->kr) {
+ DEBUG(SSSDBG_FATAL_FAILURE, ("Cannot create Kerberos request\n"));
+ ret = 4;
+ goto done;
+ }
+
+ req = handle_child_send(ctx, ctx->ev, ctx->kr);
+ if (!req) {
+ DEBUG(SSSDBG_FATAL_FAILURE, ("Cannot create child request\n"));
+ ret = 4;
+ goto done;
+ }
+ tevent_req_set_callback(req, child_done, ctx);
+
+ while (ctx->done == false) {
+ tevent_loop_once(ctx->ev);
+ }
+
+ printf("Child returned %d\n", ctx->child_ret);
+
+ ret = parse_krb5_child_response(ctx, ctx->buf, ctx->len,
+ ctx->kr->pd, 0, &ctx->res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, ("Could not parse child response\n"));
+ ret = 5;
+ goto done;
+ }
+
+ if (!ctx->res->ccname) {
+ fprintf(stderr, "No ccname returned\n");
+ ret = 6;
+ goto done;
+ }
+
+ print_ccache(ctx->res->ccname);
+
+ ret = 0;
+done:
+ if (rm_ccache && ctx->res && ctx->res->ccname) {
+ remove_ccache(ctx->res->ccname);
+ }
+ free(password);
+ talloc_free(ctx);
+ poptFreeContext(pc);
+ return ret;
+}