diff options
-rw-r--r-- | server/Makefile.am | 17 | ||||
-rw-r--r-- | server/man/sssd-krb5.5.xml | 98 | ||||
-rw-r--r-- | server/providers/data_provider.h | 2 | ||||
-rw-r--r-- | server/providers/dp_auth_util.c | 6 | ||||
-rw-r--r-- | server/providers/krb5/krb5_auth.c | 567 | ||||
-rw-r--r-- | server/providers/krb5/krb5_auth.h | 91 | ||||
-rw-r--r-- | server/providers/krb5/tgt_req_child.c | 179 | ||||
-rw-r--r-- | server/responder/pam/pamsrv_cmd.c | 20 | ||||
-rw-r--r-- | sss_client/pam_sss.c | 2 | ||||
-rw-r--r-- | sssd.spec.in | 1 |
10 files changed, 980 insertions, 3 deletions
diff --git a/server/Makefile.am b/server/Makefile.am index b15c2306..01f3037c 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -76,6 +76,7 @@ endif sssdlib_LTLIBRARIES = \ libsss_ldap.la \ + libsss_krb5.la \ libsss_proxy.la ldblib_LTLIBRARIES = \ @@ -211,6 +212,7 @@ dist_noinst_HEADERS = \ providers/dp_interfaces.h \ providers/dp_backend.h \ providers/providers.h \ + providers/krb5/krb5_auth.h \ tools/tools_util.h \ krb5_plugin/sssd_krb5_locator_plugin.h \ $(infopipe_headers) \ @@ -400,6 +402,19 @@ libsss_proxy_la_LDFLAGS = \ -version-info 1:0:0 \ -module +libsss_krb5_la_SOURCES = \ + providers/krb5/krb5_auth.c \ + providers/krb5/tgt_req_child.c +libsss_krb5_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(KRB5_CFLAGS) +libsss_krb5_la_LIBADD = \ + $(PAM_LIBS) \ + $(KRB5_LIBS) +libsss_krb5_la_LDFLAGS = \ + -version-info 1:0:0 \ + -module + memberof_la_SOURCES = \ ldb_modules/memberof.c memberof_la_CFLAGS = \ @@ -429,7 +444,7 @@ XSLTPROC_FLAGS = --catalogs --xinclude --nonet dist_man_MANS = man/sss_useradd.8 man/sss_userdel.8 man/sss_usermod.8 \ man/sss_groupadd.8 man/sss_groupdel.8 man/sss_groupmod.8 \ - man/sssd.8 man/sssd.conf.5 man/sssd-ldap.5 + man/sssd.8 man/sssd.conf.5 man/sssd-ldap.5 man/sssd-krb5.5 SUFFIXES = .1.xml .1 .3.xml .3 .5.xml .5 .8.xml .8 .1.xml.1: diff --git a/server/man/sssd-krb5.5.xml b/server/man/sssd-krb5.5.xml new file mode 100644 index 00000000..a75193a6 --- /dev/null +++ b/server/man/sssd-krb5.5.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V4.5//EN" +"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> +<reference> +<title>SSSD Manual pages</title> +<refentry> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/upstream.xml" /> + + <refmeta> + <refentrytitle>sssd-krb5</refentrytitle> + <manvolnum>5</manvolnum> + <refmiscinfo class="manual">File Formats and Conventions</refmiscinfo> + </refmeta> + + <refnamediv id='name'> + <refname>sssd-krb5</refname> + <refpurpose>the configuration file for SSSD</refpurpose> + </refnamediv> + + <refsect1 id='description'> + <title>DESCRIPTION</title> + <para> + This manual page describes the configuration of the Kerberos + 5 authentication backend for + <citerefentry> + <refentrytitle>sssd</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry>. + For a detailed syntax reference, please refer to the <quote>FILE FORMAT</quote> section of the + <citerefentry> + <refentrytitle>sssd.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> manual page + </para> + </refsect1> + + <refsect1 id='file-format'> + <title>CONFIGURATION OPTIONS</title> + <para> + If the auth-module krb5 is used in a SSSD domain, the following + options must be used. See the + <citerefentry> + <refentrytitle>sssd.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> manual page, section <quote>DOMAIN SECTIONS</quote> + for details on the configuration of a SSSD domain. + <variablelist> + <varlistentry> + <term>krb5KDCIP (string)</term> + <listitem> + <para> + Specifies the IP address of the Kerberos server. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>krb5REALM (string)</term> + <listitem> + <para> + The name of the Kerberos realm. + </para> + </listitem> + </varlistentry> + </variablelist> + </para> + </refsect1> + + <refsect1 id='example'> + <title>EXAMPLE</title> + <para> + The following example assumes that SSSD is correctly + configured and FOO is one of the domains in the + <replaceable>[domains]</replaceable> section. + </para> + <para> +<programlisting> + [domains/FOO] + auth-module = krb5 + krb5KDCIP = 192.168.1.1 + krb5REALM = EXAMPLE.COM +</programlisting> + </para> + </refsect1> + + <refsect1 id='see_also'> + <title>SEE ALSO</title> + <para> + <citerefentry> + <refentrytitle>sssd.conf</refentrytitle><manvolnum>5</manvolnum> + </citerefentry>, + <citerefentry> + <refentrytitle>sssd</refentrytitle><manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> +</refentry> +</reference> diff --git a/server/providers/data_provider.h b/server/providers/data_provider.h index 95a1b379..b747e69b 100644 --- a/server/providers/data_provider.h +++ b/server/providers/data_provider.h @@ -117,6 +117,8 @@ struct pam_data { bool offline_auth; int priv; + uid_t pw_uid; + gid_t gr_gid; }; void pam_print_data(int l, struct pam_data *pd); diff --git a/server/providers/dp_auth_util.c b/server/providers/dp_auth_util.c index 279e50b6..7219917d 100644 --- a/server/providers/dp_auth_util.c +++ b/server/providers/dp_auth_util.c @@ -35,6 +35,8 @@ void pam_print_data(int l, struct pam_data *pd) DEBUG(l, ("newauthtok type: %d\n", pd->newauthtok_type)); DEBUG(l, ("newauthtok size: %d\n", pd->newauthtok_size)); DEBUG(l, ("priv: %d\n", pd->priv)); + DEBUG(l, ("pw_uid: %d\n", pd->pw_uid)); + DEBUG(l, ("gr_gid: %d\n", pd->gr_gid)); } int pam_add_response(struct pam_data *pd, enum response_type type, @@ -83,6 +85,8 @@ bool dp_pack_pam_request(DBusMessage *msg, struct pam_data *pd) &(pd->newauthtok), pd->newauthtok_size, DBUS_TYPE_INT32, &(pd->priv), + DBUS_TYPE_INT32, &(pd->pw_uid), + DBUS_TYPE_INT32, &(pd->gr_gid), DBUS_TYPE_INVALID); return ret; @@ -109,6 +113,8 @@ bool dp_unpack_pam_request(DBusMessage *msg, struct pam_data *pd, DBusError *dbu &(pd->newauthtok), &(pd->newauthtok_size), DBUS_TYPE_INT32, &(pd->priv), + DBUS_TYPE_INT32, &(pd->pw_uid), + DBUS_TYPE_INT32, &(pd->gr_gid), DBUS_TYPE_INVALID); return ret; diff --git a/server/providers/krb5/krb5_auth.c b/server/providers/krb5/krb5_auth.c new file mode 100644 index 00000000..5206bd08 --- /dev/null +++ b/server/providers/krb5/krb5_auth.c @@ -0,0 +1,567 @@ +/* + SSSD + + Kerberos 5 Backend Module + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2009 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 <errno.h> +#include <sys/time.h> +#include <krb5/krb5.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <fcntl.h> +#include <pwd.h> + +#include <security/pam_modules.h> + +#include "util/util.h" +#include "providers/dp_backend.h" +#include "db/sysdb.h" +#include "../sss_client/sss_cli.h" +#include "krb5_plugin/sssd_krb5_locator_plugin.h" +#include "providers/krb5/krb5_auth.h" + +static void fd_nonblocking(int fd) { + int flags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + DEBUG(1, ("F_GETFL failed [%d][%s].\n", errno, strerror(errno))); + return; + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + DEBUG(1, ("F_SETFL failed [%d][%s].\n", errno, strerror(errno))); + } + + return; +} + +static void krb5_cleanup(struct krb5_req *kr) +{ + if (kr == NULL) return; + + /* FIXME: is it safe to drop the "!= NULL" checks? */ + if (kr->options != NULL) + krb5_get_init_creds_opt_free(kr->ctx, kr->options); + if (kr->creds != NULL) + krb5_free_cred_contents(kr->ctx, kr->creds); + if (kr->name != NULL) + krb5_free_unparsed_name(kr->ctx, kr->name); + if (kr->princ != NULL) + krb5_free_principal(kr->ctx, kr->princ); + if (kr->cc != NULL) + krb5_cc_close(kr->ctx, kr->cc); + if (kr->ctx != NULL) + krb5_free_context(kr->ctx); + + talloc_free(kr); +} + +static int krb5_setup(struct be_req *req, struct krb5_req **krb5_req) +{ + struct krb5_req *kr = NULL; + struct krb5_ctx *krb5_ctx; + struct pam_data *pd; + krb5_error_code kerr = 0; + char *user_princ_str = NULL; + + pd = talloc_get_type(req->req_data, struct pam_data); + + krb5_ctx = talloc_get_type(req->be_ctx->pvt_auth_data, struct krb5_ctx); + + kr = talloc_zero(req, struct krb5_req); + if (kr == NULL) { + DEBUG(1, ("talloc failed.\n")); + kerr = ENOMEM; + goto failed; + } + + kr->pd = pd; + kr->req = req; + + kerr = krb5_init_context(&kr->ctx); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto failed; + } + +/* TODO: try to read user principal from id backend, use user + realm as a + fallback */ + if (kr->pd->user != NULL && krb5_ctx->realm != NULL) { + user_princ_str = talloc_asprintf(kr, "%s@%s", kr->pd->user, + krb5_ctx->realm); + } + if (user_princ_str == NULL) { + DEBUG(1, ("talloc_asprintf failed.\n")); + kerr = ENOMEM; + goto failed; + } + + kerr = krb5_parse_name(kr->ctx, user_princ_str, &kr->princ); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto failed; + } + + kerr = krb5_unparse_name(kr->ctx, kr->princ, &kr->name); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto failed; + } + + kr->creds = talloc_zero(kr, krb5_creds); + if (kr->creds == NULL) { + DEBUG(1, ("talloc_zero failed.\n")); + kerr = ENOMEM; + goto failed; + } + + kerr = krb5_get_init_creds_opt_alloc(kr->ctx, &kr->options); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto failed; + } + +/* TODO: set options, e.g. + * krb5_get_init_creds_opt_set_tkt_life + * krb5_get_init_creds_opt_set_renew_life + * krb5_get_init_creds_opt_set_forwardable + * krb5_get_init_creds_opt_set_proxiable + * krb5_get_init_creds_opt_set_etype_list + * krb5_get_init_creds_opt_set_address_list + * krb5_get_init_creds_opt_set_preauth_list + * krb5_get_init_creds_opt_set_salt + * krb5_get_init_creds_opt_set_change_password_prompt + * krb5_get_init_creds_opt_set_pa + */ + + *krb5_req = kr; + return EOK; + +failed: + krb5_cleanup(kr); + + return kerr; +} + +/* TODO: remove later + * These functions are available in the latest tevent and are the ones that + * should be used as tevent_req is rightfully opaque there */ +#ifndef tevent_req_data +#define tevent_req_data(req, type) ((type *)req->private_state) +#endif + +#ifndef tevent_req_set_callback +#define tevent_req_set_callback(req, func, data) \ + do { req->async.fn = func; req->async.private_data = data; } while(0) +#endif + +#ifndef tevent_req_callback_data +#define tevent_req_callback_data(req, type) ((type *)req->async.private_data) +#endif + +static void wait_for_child_handler(struct tevent_context *ev, + struct tevent_signal *sige, int signum, + int count, void *__siginfo, void *pvt) +{ + int ret; + int child_status; + siginfo_t *siginfo = (siginfo_t *)__siginfo; + + errno = 0; + do { + ret = waitpid(siginfo->si_pid, &child_status, WNOHANG); + } while (ret == -1 && errno == EINTR); + if (ret == siginfo->si_pid) { + DEBUG(4, ("child status [%d].\n", child_status)); + if (WEXITSTATUS(child_status) != 0) { + DEBUG(1, ("child failed.\n")); + } + } else if (ret == 0) { + DEBUG(1, ("waitpid did not found a child with changed status.\n", ret)); + } else if (ret >= 0 && ret != siginfo->si_pid) { + DEBUG(1, ("waitpid returned wrong child pid [%d], continue waiting.\n", ret)); + } else if (ret == -1 && errno == ECHILD) { + DEBUG(1, ("no child with pid [%d].\n", siginfo->si_pid)); + } else { + DEBUG(1, ("waitpid failed [%s].\n", strerror(errno))); + } + + return; +} + +static int fork_tgt_req_child(struct krb5_req *kr) +{ + int pipefd[2]; + pid_t pid; + int ret; + + ret = pipe(pipefd); + if (ret == -1) { + DEBUG(1, ("pipe failed [%d][%s].\n", errno, strerror(errno))); + return -1; + } + pid = fork(); + + if (pid == 0) { /* child */ + close(pipefd[0]); + tgt_req_child(pipefd[1], kr); + } else if (pid > 0) { /* parent */ + kr->child_pid = pid; + kr->fd = pipefd[0]; + close(pipefd[1]); + + fd_nonblocking(kr->fd); + + } else { /* error */ + DEBUG(1, ("fork failed [%d][%s].\n", errno, strerror(errno))); + return -1; + } + + return EOK; +} + + +struct read_pipe_state { + int fd; + uint8_t *buf; + size_t len; +}; + +static void read_pipe_done(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *pvt); + +static struct tevent_req *read_pipe_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + int fd) +{ + struct tevent_req *req; + struct read_pipe_state *state; + struct tevent_fd *fde; + + + req = tevent_req_create(memctx, &state, struct read_pipe_state); + if (req == NULL) return NULL; + + state->fd = fd; + state->buf = talloc_array(state, uint8_t, MAX_CHILD_MSG_SIZE); + if (state->buf == NULL) goto fail; + + fde = tevent_add_fd(ev, state, fd, TEVENT_FD_READ, + read_pipe_done, req); + if (fde == NULL) { + DEBUG(1, ("tevent_add_fd failed.\n")); + goto fail; + } + + return req; + +fail: + talloc_zfree(req); + return NULL; +} + +static void read_pipe_done(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *pvt) +{ + ssize_t size; + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct read_pipe_state *state = tevent_req_data(req, struct read_pipe_state); + + if (flags & TEVENT_FD_WRITE) { + DEBUG(1, ("client_response_handler called with TEVENT_FD_WRITE, this should not happen.\n")); + tevent_req_error(req, EINVAL); + return; + } + + size = read(state->fd, state->buf, talloc_get_size(state->buf)); + if (size == -1) { + if (errno == EAGAIN || errno == EINTR) return; + DEBUG(1, ("read failed [%d][%s].\n", errno, strerror(errno))); + tevent_req_error(req, errno); + return; + } + state->len = size; + + tevent_req_done(req); + return; +} + +static ssize_t read_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **buf, uint64_t *error) { + struct read_pipe_state *state = tevent_req_data(req, + struct read_pipe_state); + enum tevent_req_state tstate; + + if (tevent_req_is_error(req, &tstate, error)) { + return -1; + } + + *buf = talloc_move(mem_ctx, &state->buf); + return state->len; +} + +struct tgt_req_state { + struct krb5_req *kr; + ssize_t len; + uint8_t *buf; +}; + +static void tgt_req_done(struct tevent_req *subreq); + +static struct tevent_req *tgt_req_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct krb5_req *kr) +{ + int ret; + struct tevent_req *req; + struct tevent_req *subreq; + struct tgt_req_state *state; + + ret = fork_tgt_req_child(kr); + if (ret != EOK) { + DEBUG(1, ("fork_tgt_req_child failed.\n")); + return NULL; + } + + req = tevent_req_create(mem_ctx, &state, struct tgt_req_state); + if (req == NULL) { + return NULL; + } + + state->kr = kr; + + subreq = read_pipe_send(state, ev, kr->fd); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tgt_req_done, req); + return req; +} + +static void tgt_req_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tgt_req_state *state = tevent_req_data(req, struct tgt_req_state); + uint64_t error; + + state->len = read_pipe_recv(subreq, state, &state->buf, &error); + talloc_zfree(subreq); + close(state->kr->fd); + if (state->len == -1) { + tevent_req_error(req, error); + return; + } + + tevent_req_done(req); + return; +} + +static ssize_t tgt_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **buf, uint64_t *error) { + struct tgt_req_state *state = tevent_req_data(req, struct tgt_req_state); + enum tevent_req_state tstate; + + if (tevent_req_is_error(req, &tstate, error)) { + return -1; + } + + *buf = talloc_move(mem_ctx, &state->buf); + return state->len; +} + +static void krb5_pam_handler_done(struct tevent_req *req); + +static void krb5_pam_handler(struct be_req *be_req) +{ + struct krb5_req *kr = NULL; + struct tevent_req *req; + int ret; + struct pam_data *pd; + int pam_status=PAM_SYSTEM_ERR; + + pd = talloc_get_type(be_req->req_data, struct pam_data); + + if (pd->cmd != SSS_PAM_AUTHENTICATE) { + DEBUG(4, ("krb5 does not handles pam task %d.\n", pd->cmd)); + pam_status = PAM_SUCCESS; + goto done; + } + + ret = krb5_setup(be_req, &kr); + if (ret != EOK) { + DEBUG(1, ("krb5_setup failed.\n")); + goto done; + } + + req = tgt_req_send(be_req, be_req->be_ctx->ev, kr); + if (req == NULL) { + DEBUG(1, ("tgt_req_send failed.\n")); + goto done; + } + + tevent_req_set_callback(req, krb5_pam_handler_done, kr); + return; + +done: + krb5_cleanup(kr); + + pd->pam_status = pam_status; + + be_req->fn(be_req, pam_status, NULL); +} + +static void krb5_pam_handler_done(struct tevent_req *req) +{ + struct krb5_req *kr = tevent_req_callback_data(req, struct krb5_req); + struct pam_data *pd = kr->pd; + struct be_req *be_req = kr->req; + struct krb5_ctx *krb5_ctx = talloc_get_type(be_req->be_ctx->pvt_auth_data, + struct krb5_ctx); + struct tgt_req_state *state = tevent_req_data(req, struct tgt_req_state); + int ret; + uint8_t *buf; + ssize_t len; + uint64_t error; + int p; + int32_t *msg_status; + int32_t *msg_type; + int32_t *msg_len; + + pd->pam_status = PAM_SYSTEM_ERR; + krb5_cleanup(kr); + + len = tgt_req_recv(req, state, &buf, &error); + talloc_zfree(req); + if (len == -1) { + DEBUG(1, ("tgt_req request failed\n")); + goto done; + } + + if ((size_t) len < 3*sizeof(int32_t)) { + DEBUG(1, ("message too short.\n")); + goto done; + } + + p=0; + msg_status = ((int32_t *)(buf+p)); + p += sizeof(int32_t); + + msg_type = ((int32_t *)(buf+p)); + p += sizeof(int32_t); + + msg_len = ((int32_t *)(buf+p)); + p += sizeof(int32_t); + + DEBUG(4, ("child response [%d][%d][%d].\n", *msg_status, *msg_type, + *msg_len)); + + if ((p + *msg_len) != len) { + DEBUG(1, ("message format error.\n")); + goto done; + } + + ret=pam_add_response(kr->pd, *msg_type, *msg_len, &buf[p]); + if (ret != EOK) { + DEBUG(1, ("pam_add_response failed.\n")); + goto done; + } + + pd->pam_status = *msg_status; + +done: + be_req->fn(be_req, pd->pam_status, NULL); +} + + +struct be_auth_ops krb5_auth_ops = { + .pam_handler = krb5_pam_handler, + .finalize = NULL, +}; + + +int sssm_krb5_auth_init(struct be_ctx *bectx, struct be_auth_ops **ops, + void **pvt_auth_data) +{ + struct krb5_ctx *ctx = NULL; + bool bool_value = FALSE; + char *value = NULL; + int ret; + struct tevent_signal *sige; + + ctx = talloc_zero(bectx, struct krb5_ctx); + if (!ctx) { + DEBUG(1, ("talloc failed.\n")); + return ENOMEM; + } + + ctx->action = INIT_PW; + + ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, + "krb5KDCIP", NULL, &value); + if (ret != EOK) goto fail; + if (value == NULL) { + DEBUG(2, ("Missing krb5KDCIP, authentication might fail.\n")); + } else { + ret = setenv(SSSD_KDC, value, 1); + if (ret != EOK) { + DEBUG(2, ("setenv %s failed, authentication might fail.\n", + SSSD_KDC)); + } + } + ctx->kdcip = value; + + ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, + "krb5REALM", NULL, &value); + if (ret != EOK) goto fail; + if (value == NULL) { + DEBUG(4, ("Missing krb5REALM authentication might fail.\n")); + } else { + ret = setenv(SSSD_REALM, value, 1); + if (ret != EOK) { + DEBUG(2, ("setenv %s failed, authentication might fail.\n", + SSSD_REALM)); + } + } + ctx->realm = value; + +/* TODO: set options */ + + sige = tevent_add_signal(bectx->ev, ctx, SIGCHLD, SA_SIGINFO, + wait_for_child_handler, NULL); + if (sige == NULL) { + DEBUG(1, ("tevent_add_signal failed.\n")); + ret = ENOMEM; + goto fail; + } + + + *ops = &krb5_auth_ops; + *pvt_auth_data = ctx; + return EOK; + +fail: + talloc_free(ctx); + return ret; +} diff --git a/server/providers/krb5/krb5_auth.h b/server/providers/krb5/krb5_auth.h new file mode 100644 index 00000000..d1c5c7c8 --- /dev/null +++ b/server/providers/krb5/krb5_auth.h @@ -0,0 +1,91 @@ +/* + SSSD + + Kerberos Backend, private header file + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2009 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/>. +*/ + +#ifndef __KRB5_AUTH_H__ +#define __KRB5_AUTH_H__ + +#define MAX_CHILD_MSG_SIZE 255 +#define CCACHE_ENV_NAME "KRB5CCNAME" + +typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type; + +struct krb5_ctx { + /* opts taken from kinit */ + /* in seconds */ + krb5_deltat starttime; + krb5_deltat lifetime; + krb5_deltat rlife; + + int forwardable; + int proxiable; + int addresses; + + int not_forwardable; + int not_proxiable; + int no_addresses; + + int verbose; + + char* principal_name; + char* service_name; + char* keytab_name; + char* k5_cache_name; + char* k4_cache_name; + + action_type action; + + int num_pa_opts; + krb5_gic_opt_pa_data *pa_opts; + + char *kdcip; + char *realm; +}; + +struct krb5_req { + krb5_context ctx; + krb5_ccache cc; + krb5_principal princ; + char* name; + krb5_creds *creds; + krb5_get_init_creds_opt *options; + pid_t child_pid; + int fd; + + struct be_req *req; + struct pam_data *pd; + struct krb5_ctx *krb5_ctx; +}; + +static krb5_context krb5_error_ctx; +static const char *__krb5_error_msg; +#define KRB5_DEBUG(level, krb5_error) do { \ + __krb5_error_msg = krb5_get_error_message(krb5_error_ctx, krb5_error); \ + DEBUG(level, ("%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg)); \ + krb5_free_error_message(krb5_error_ctx, __krb5_error_msg); \ +} while(0); + +void tgt_req_child(int fd, struct krb5_req *kr); + +#endif /* __KRB5_AUTH_H__ */ diff --git a/server/providers/krb5/tgt_req_child.c b/server/providers/krb5/tgt_req_child.c new file mode 100644 index 00000000..edb3d180 --- /dev/null +++ b/server/providers/krb5/tgt_req_child.c @@ -0,0 +1,179 @@ +/* + SSSD + + Kerberos 5 Backend Module -- tgt_req child + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2009 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 <krb5/krb5.h> +#include <sys/types.h> + +#include <security/pam_modules.h> + +#include "util/util.h" +#include "providers/dp_backend.h" +#include "providers/krb5/krb5_auth.h" + +static int pack_response_packet(uint8_t *buf, int status, int type, + const char *data) +{ + int len; + int p=0; + + if ((3*sizeof(int32_t) + strlen(data)+1) > MAX_CHILD_MSG_SIZE) { + return -1; + } + + ((int32_t *)(&buf[p]))[0] = status; + p += sizeof(int32_t); + + ((int32_t *)(&buf[p]))[0] = type; + p += sizeof(int32_t); + + len = strlen(data)+1; + ((int32_t *)(&buf[p]))[0] = len; + p += sizeof(int32_t); + + memcpy(&buf[p], data, len); + p += len; + + return p; +} + +void tgt_req_child(int fd, struct krb5_req *kr) +{ + int ret; + krb5_error_code kerr = 0; + char *pass_str = NULL; + uint8_t buf[MAX_CHILD_MSG_SIZE]; + int size = 0; + const char *cc_name; + char *env; + const char *krb5_error_msg; + + ret = setgid(kr->pd->gr_gid); + if (ret == -1) { + DEBUG(1, ("setgid failed [%d][%s].\n", errno, strerror(errno))); + _exit(-1); + } + + ret = setuid(kr->pd->pw_uid); + if (ret == -1) { + DEBUG(1, ("setuid failed [%d][%s].\n", errno, strerror(errno))); + _exit(-1); + } + + ret = setegid(kr->pd->gr_gid); + if (ret == -1) { + DEBUG(1, ("setegid failed [%d][%s].\n", errno, strerror(errno))); + _exit(-1); + } + + ret = seteuid(kr->pd->pw_uid); + if (ret == -1) { + DEBUG(1, ("seteuid failed [%d][%s].\n", errno, strerror(errno))); + _exit(-1); + } + + pass_str = talloc_strndup(kr, (const char *) kr->pd->authtok, + kr->pd->authtok_size); + if (pass_str == NULL) { + DEBUG(1, ("talloc_strndup failed.\n")); + _exit(-1); + } + + kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, + pass_str, NULL, NULL, 0, NULL, + kr->options); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto childfailed; + } + + memset(pass_str, 0, kr->pd->authtok_size); + talloc_free(pass_str); + memset(kr->pd->authtok, 0, kr->pd->authtok_size); + + kerr = krb5_cc_default(kr->ctx, &kr->cc); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto childfailed; + } + + kerr = krb5_cc_initialize(kr->ctx, kr->cc, kr->princ); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto childfailed; + } + + kerr = krb5_cc_store_cred(kr->ctx, kr->cc, kr->creds); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + krb5_cc_destroy(kr->ctx, kr->cc); + goto childfailed; + } + + cc_name = krb5_cc_get_name(kr->ctx, kr->cc); + if (cc_name == NULL) { + DEBUG(1, ("krb5_cc_get_name failed.\n")); + krb5_cc_destroy(kr->ctx, kr->cc); + _exit(-1); + } + + env = talloc_asprintf(kr, "%s=%s",CCACHE_ENV_NAME, cc_name); + if (env == NULL) { + DEBUG(1, ("talloc_asprintf failed.\n")); + krb5_cc_destroy(kr->ctx, kr->cc); + _exit(-1); + } + + size = pack_response_packet(buf, PAM_SUCCESS, PAM_ENV_ITEM, env); + if (size < 0) { + DEBUG(1, ("failed to create response message.\n")); + krb5_cc_destroy(kr->ctx, kr->cc); + _exit(-1); + } + + kerr = 0; + +childfailed: + if (kerr != 0 ) { + krb5_error_msg = krb5_get_error_message(krb5_error_ctx, kerr); + size = pack_response_packet(buf, PAM_SYSTEM_ERR, PAM_USER_INFO, + krb5_error_msg); + if (size < 0) { + DEBUG(1, ("failed to create response message.\n")); + krb5_cc_destroy(kr->ctx, kr->cc); + _exit(-1); + } + krb5_free_error_message(krb5_error_ctx, krb5_error_msg); + } + + ret = write(fd, buf, size); + if (ret == -1) { + DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno))); + krb5_cc_destroy(kr->ctx, kr->cc); + _exit(ret); + } + + krb5_cc_close(kr->ctx, kr->cc); + + + _exit(0); +} diff --git a/server/responder/pam/pamsrv_cmd.c b/server/responder/pam/pamsrv_cmd.c index e0654908..9b02146c 100644 --- a/server/responder/pam/pamsrv_cmd.c +++ b/server/responder/pam/pamsrv_cmd.c @@ -22,6 +22,7 @@ #include <time.h> #include "util/util.h" +#include "db/sysdb.h" #include "confdb/confdb.h" #include "responder/common/responder_packet.h" #include "responder/common/responder.h" @@ -387,7 +388,6 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd) size_t blen; int timeout; int ret; - preq = talloc_zero(cctx, struct pam_auth_req); if (!preq) { return ENOMEM; @@ -716,6 +716,24 @@ static void pam_check_user_callback(void *ptr, int status, case 1: /* BINGO */ + preq->pd->pw_uid = + ldb_msg_find_attr_as_int(res->msgs[0], SYSDB_UIDNUM, -1); + if (preq->pd->pw_uid == -1) { + DEBUG(1, ("Failed to find uid for user [%s] in domain [%s].\n", + preq->pd->user, preq->pd->domain)); + preq->pd->pam_status = PAM_SYSTEM_ERR; + pam_reply(preq); + } + + preq->pd->gr_gid = + ldb_msg_find_attr_as_int(res->msgs[0], SYSDB_GIDNUM, -1); + if (preq->pd->gr_gid == -1) { + DEBUG(1, ("Failed to find gid for user [%s] in domain [%s].\n", + preq->pd->user, preq->pd->domain)); + preq->pd->pam_status = PAM_SYSTEM_ERR; + pam_reply(preq); + } + pam_dom_forwarder(preq); return; diff --git a/sss_client/pam_sss.c b/sss_client/pam_sss.c index ccc205cc..a10e0eab 100644 --- a/sss_client/pam_sss.c +++ b/sss_client/pam_sss.c @@ -426,7 +426,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf) break; } logger(pamh, LOG_INFO, "user info: [%s]", &buf[p]); - ret = do_pam_conversation(pamh, PAM_USER_INFO, (char *) &buf[p], + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, (char *) &buf[p], NULL, NULL); if (ret != PAM_SUCCESS) { D(("do_pam_conversation, canot display user info.")); diff --git a/sssd.spec.in b/sssd.spec.in index 719e6b7c..1fa847d9 100644 --- a/sssd.spec.in +++ b/sssd.spec.in @@ -80,6 +80,7 @@ rm -f \ $RPM_BUILD_ROOT/%{_libdir}/ldb/memberof.la \ $RPM_BUILD_ROOT/%{_libdir}/sssd/libsss_ldap.la \ $RPM_BUILD_ROOT/%{_libdir}/sssd/libsss_proxy.la \ + $RPM_BUILD_ROOT/%{_libdir}/sssd/libsss_krb5.la \ $RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.la %clean |