summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2009-06-15 15:07:39 +0200
committerStephen Gallagher <sgallagh@redhat.com>2009-07-02 08:59:47 -0400
commit1a0eed71352d7d2c4a9f335d8583e0fee3cae491 (patch)
tree6f2006821d1b4c1c957ee0c811c095b5459299a6
parent43133b835fe97592eba30344805177faab9fb644 (diff)
downloadsssd-1a0eed71352d7d2c4a9f335d8583e0fee3cae491.tar.gz
sssd-1a0eed71352d7d2c4a9f335d8583e0fee3cae491.tar.bz2
sssd-1a0eed71352d7d2c4a9f335d8583e0fee3cae491.zip
added kerberos backend with tevent_req event handling
-rw-r--r--server/Makefile.am17
-rw-r--r--server/man/sssd-krb5.5.xml98
-rw-r--r--server/providers/data_provider.h2
-rw-r--r--server/providers/dp_auth_util.c6
-rw-r--r--server/providers/krb5/krb5_auth.c567
-rw-r--r--server/providers/krb5/krb5_auth.h91
-rw-r--r--server/providers/krb5/tgt_req_child.c179
-rw-r--r--server/responder/pam/pamsrv_cmd.c20
-rw-r--r--sss_client/pam_sss.c2
-rw-r--r--sssd.spec.in1
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