[SSSD] [PATCH] add krb5ccache_dir and krb5ccname_template option

Sumit Bose sbose at redhat.com
Mon Sep 14 11:32:39 UTC 2009


Hi,

this patch add two new option to make the Kerberos provider of sssd more
compatible with pam_krb5.

bye,
Sumit
-------------- next part --------------
>From 021ee9186ac3a6f9939f1ae0bc748aae112de660 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 10 Sep 2009 14:43:33 +0200
Subject: [PATCH] add krb5ccache_dir and krb5ccname_template option

The configuration options krb5ccache_dir and krb5ccname_template
are added to the Kerberos provider to create the user's credential
caches the same way as pam_krb5 does. Due to the design of the sssd
and the supported ccache types of MIT Kerberos only files are
allowed.
---
 server/Makefile.am                            |   16 ++-
 server/krb5_plugin/sssd_krb5_locator_plugin.c |    6 +-
 server/krb5_plugin/sssd_krb5_locator_plugin.h |   28 ---
 server/man/sssd-krb5.5.xml                    |   71 ++++++
 server/providers/krb5/krb5_auth.c             |   90 ++++++--
 server/providers/krb5/krb5_auth.h             |   22 ++
 server/providers/krb5/krb5_child.c            |  116 ++++++++---
 server/providers/krb5/krb5_utils.c            |  141 ++++++++++++
 server/providers/krb5/krb5_utils.h            |   37 +++
 server/tests/krb5_utils-tests.c               |  292 +++++++++++++++++++++++++
 10 files changed, 736 insertions(+), 83 deletions(-)
 delete mode 100644 server/krb5_plugin/sssd_krb5_locator_plugin.h
 create mode 100644 server/providers/krb5/krb5_utils.c
 create mode 100644 server/providers/krb5/krb5_utils.h
 create mode 100644 server/tests/krb5_utils-tests.c

diff --git a/server/Makefile.am b/server/Makefile.am
index 558e7c6..5b52425 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -59,7 +59,8 @@ if HAVE_CHECK
     non_interactive_check_based_tests = \
         sysdb-tests \
         strtonum-tests \
-        resolv-tests
+        resolv-tests \
+        krb5-utils-tests
 endif
 
 check_PROGRAMS = \
@@ -228,10 +229,10 @@ dist_noinst_HEADERS = \
     providers/dp_backend.h \
     providers/providers.h \
     providers/krb5/krb5_auth.h \
+    providers/krb5/krb5_utils.h \
     providers/ldap/sdap.h \
     providers/ldap/sdap_async.h \
     tools/tools_util.h \
-    krb5_plugin/sssd_krb5_locator_plugin.h \
     resolv/async_resolv.h \
     resolv/ares/ares_parse_srv_reply.h \
     resolv/ares/ares_parse_txt_reply.h
@@ -355,6 +356,16 @@ strtonum_tests_CFLAGS = \
 strtonum_tests_LDADD = \
     $(SSSD_LIBS) \
     $(CHECK_LIBS)
+
+krb5_utils_tests_SOURCES = \
+    $(SSSD_DEBUG_OBJ) \
+    tests/krb5_utils-tests.c \
+    providers/krb5/krb5_utils.c
+krb5_utils_tests_CFLAGS = \
+    $(CHECK_CFLAGS)
+krb5_utils_tests_LDADD = \
+    $(CHECK_LIBS) \
+    $(TALLOC_LIBS)
 endif
 
 stress_tests_SOURCES = \
@@ -404,6 +415,7 @@ libsss_proxy_la_LDFLAGS = \
     -module
 
 libsss_krb5_la_SOURCES = \
+    providers/krb5/krb5_utils.c \
     providers/krb5/krb5_auth.c
 libsss_krb5_la_CFLAGS = \
     $(AM_CFLAGS) \
diff --git a/server/krb5_plugin/sssd_krb5_locator_plugin.c b/server/krb5_plugin/sssd_krb5_locator_plugin.c
index 5b289fa..85e8bdc 100644
--- a/server/krb5_plugin/sssd_krb5_locator_plugin.c
+++ b/server/krb5_plugin/sssd_krb5_locator_plugin.c
@@ -28,7 +28,7 @@
 
 #include <krb5/locate_plugin.h>
 
-#include "krb5_plugin/sssd_krb5_locator_plugin.h"
+#include "providers/krb5/krb5_auth.h"
 
 struct sssd_ctx {
     char *sssd_realm;
@@ -48,12 +48,12 @@ krb5_error_code sssd_krb5_locator_init(krb5_context context,
     ctx = calloc(1,sizeof(struct sssd_ctx));
     if (ctx == NULL) return ENOMEM;
 
-    dummy = getenv(SSSD_REALM);
+    dummy = getenv(SSSD_KRB5_REALM);
     if (dummy == NULL) goto failed;
     ctx->sssd_realm = strdup(dummy);
     if (ctx->sssd_realm == NULL) goto failed;
 
-    dummy = getenv(SSSD_KDC);
+    dummy = getenv(SSSD_KRB5_KDC);
     if (dummy == NULL) goto failed;
     ctx->sssd_kdc = strdup(dummy);
     if (ctx->sssd_kdc == NULL) goto failed;
diff --git a/server/krb5_plugin/sssd_krb5_locator_plugin.h b/server/krb5_plugin/sssd_krb5_locator_plugin.h
deleted file mode 100644
index 1016670..0000000
--- a/server/krb5_plugin/sssd_krb5_locator_plugin.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-    Authors:
-        Sumit Bose <sbose at 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 __SSSD_KRB5_LOCATOR_PLUGIN_H__
-#define __SSSD_KRB5_LOCATOR_PLUGIN_H__
-
-#define SSSD_KDC "SSSD_KDC"
-#define SSSD_REALM "SSSD_REALM"
-
-#endif /* __SSSD_KRB5_LOCATOR_PLUGIN_H__ */
-
diff --git a/server/man/sssd-krb5.5.xml b/server/man/sssd-krb5.5.xml
index 188cc91..4b26c02 100644
--- a/server/man/sssd-krb5.5.xml
+++ b/server/man/sssd-krb5.5.xml
@@ -91,6 +91,77 @@
                         </para>
                     </listitem>
                 </varlistentry>
+
+                <varlistentry>
+                    <term>krb5ccache_dir (string)</term>
+                    <listitem>
+                        <para>
+                            Directory to store credential caches.
+                        </para>
+                        <para>
+                            Default: /tmp
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                    <term>krb5ccname_template (string)</term>
+                    <listitem>
+                        <para>
+                            Location of the user's credential cache. Currently
+                            only file based credential caches are supported. In
+                            the template the following sequences are
+                            substituted:
+                            <variablelist>
+                                <varlistentry>
+                                    <term>%u</term>
+                                    <listitem><para>login name</para></listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%U</term>
+                                    <listitem><para>login UID</para></listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%p</term>
+                                    <listitem><para>principle name</para>
+                                    </listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%r</term>
+                                    <listitem><para>realm name</para></listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%h</term>
+                                    <listitem><para>home directory</para>
+                                    </listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%d</term>
+                                    <listitem><para>value of krb5ccache_dir
+                                              </para>
+                                    </listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%P</term>
+                                    <listitem><para>the process ID of the sssd
+                                                    client</para>
+                                    </listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%%</term>
+                                    <listitem><para>a literal '%'</para>
+                                    </listitem>
+                                </varlistentry>
+                            </variablelist>
+                            If the template ends with 'XXXXXX' mkstemp(3) is
+                            used to create a unique filename in a safe way.
+                        </para>
+                        <para>
+                            Default: FILE:%d/krb5cc_%U_XXXXXX
+                        </para>
+                    </listitem>
+                </varlistentry>
+
             </variablelist>
         </para>
     </refsect1>
diff --git a/server/providers/krb5/krb5_auth.c b/server/providers/krb5/krb5_auth.c
index 73d3ccd..779ada0 100644
--- a/server/providers/krb5/krb5_auth.c
+++ b/server/providers/krb5/krb5_auth.c
@@ -31,14 +31,16 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <pwd.h>
+#include <sys/stat.h>
+
 
 #include <security/pam_modules.h>
 
 #include "util/util.h"
 #include "providers/dp_backend.h"
 #include "db/sysdb.h"
-#include "krb5_plugin/sssd_krb5_locator_plugin.h"
 #include "providers/krb5/krb5_auth.h"
+#include "providers/krb5/krb5_utils.h"
 
 #ifndef SSSD_LIBEXEC_PATH
 #error "SSSD_LIBEXEC_PATH not defined"
@@ -46,16 +48,6 @@
 #define KRB5_CHILD SSSD_LIBEXEC_PATH"/krb5_child"
 #endif
 
-struct krb5child_req {
-    pid_t child_pid;
-    int read_from_child_fd;
-    int write_to_child_fd;
-
-    struct be_req *req;
-    struct pam_data *pd;
-    struct krb5_ctx *krb5_ctx;
-};
-
 static errno_t become_user(uid_t uid, gid_t gid)
 {
     int ret;
@@ -102,7 +94,8 @@ errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf)
         return ENOMEM;
     }
 
-    buf->size = 3*sizeof(int) + strlen(kr->pd->upn) + kr->pd->authtok_size;
+    buf->size = 4*sizeof(int) + strlen(kr->pd->upn) + strlen(kr->ccname) +
+                kr->pd->authtok_size;
     if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
         buf->size += sizeof(int) + kr->pd->newauthtok_size;
     }
@@ -118,12 +111,18 @@ errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf)
     ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->cmd;
     rp += sizeof(uint32_t);
 
-    ((uint32_t *)(&buf->data[rp]))[0] = strlen(kr->pd->upn);
+    ((uint32_t *)(&buf->data[rp]))[0] = (uint32_t) strlen(kr->pd->upn);
     rp += sizeof(uint32_t);
 
     memcpy(&buf->data[rp], kr->pd->upn, strlen(kr->pd->upn));
     rp += strlen(kr->pd->upn);
 
+    ((uint32_t *)(&buf->data[rp]))[0] = (uint32_t) strlen(kr->ccname);
+    rp += sizeof(uint32_t);
+
+    memcpy(&buf->data[rp], kr->ccname, strlen(kr->ccname));
+    rp += strlen(kr->ccname);
+
     ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->authtok_size;
     rp += sizeof(uint32_t);
 
@@ -168,7 +167,8 @@ static void krb5_cleanup(struct krb5child_req *kr)
     talloc_zfree(kr);
 }
 
-static errno_t krb5_setup(struct be_req *req, struct krb5child_req **krb5_req)
+static errno_t krb5_setup(struct be_req *req, struct krb5child_req **krb5_req,
+                          const char *homedir)
 {
     struct krb5child_req *kr = NULL;
     struct krb5_ctx *krb5_ctx;
@@ -190,6 +190,14 @@ static errno_t krb5_setup(struct be_req *req, struct krb5child_req **krb5_req)
     kr->pd = pd;
     kr->req = req;
     kr->krb5_ctx = krb5_ctx;
+    kr->homedir = homedir;
+
+    kr->ccname = expand_ccname_template(kr, kr, krb5_ctx->ccname_template);
+    if (kr->ccname == NULL) {
+        DEBUG(1, ("expand_ccname_template failed.\n"));
+        err = EINVAL;
+        goto failed;
+    }
 
     *krb5_req = kr;
 
@@ -495,13 +503,14 @@ static void krb5_pam_handler(struct be_req *be_req)
         goto done;
     }
 
-    attrs = talloc_array(be_req, const char *, 2);
+    attrs = talloc_array(be_req, const char *, 3);
     if (attrs == NULL) {
         goto done;
     }
 
     attrs[0] = SYSDB_UPN;
-    attrs[1] = NULL;
+    attrs[1] = SYSDB_HOMEDIR;
+    attrs[2] = NULL;
 
     ret = sysdb_get_user_attr(be_req, be_req->be_ctx->sysdb,
                               be_req->be_ctx->domain, pd->user, attrs,
@@ -528,7 +537,7 @@ static void get_user_upn_done(void *pvt, int err, struct ldb_result *res)
     int ret;
     struct pam_data *pd;
     int pam_status=PAM_SYSTEM_ERR;
-    //const char *upn = NULL;
+    const char *homedir = NULL;
 
     pd = talloc_get_type(be_req->req_data, struct pam_data);
     krb5_ctx = talloc_get_type(be_req->be_ctx->bet_info[BET_AUTH].pvt_bet_data,
@@ -557,6 +566,12 @@ static void get_user_upn_done(void *pvt, int err, struct ldb_result *res)
                 DEBUG(9, ("Using simple UPN [%s].\n", pd->upn));
             }
         }
+
+        homedir = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_HOMEDIR,
+                                              NULL);
+        if (homedir == NULL) {
+            DEBUG(4, ("Home directory for user [%s] not known.\n", pd->user));
+        }
         break;
 
     default:
@@ -570,7 +585,7 @@ static void get_user_upn_done(void *pvt, int err, struct ldb_result *res)
         goto failed;
     }
 
-    ret = krb5_setup(be_req, &kr);
+    ret = krb5_setup(be_req, &kr, homedir);
     if (ret != EOK) {
         DEBUG(1, ("krb5_setup failed.\n"));
         goto failed;
@@ -654,7 +669,7 @@ static void krb5_pam_handler_done(struct tevent_req *req)
     pd->pam_status = *msg_status;
 
     if (pd->pam_status == PAM_SUCCESS && pd->cmd == SSS_PAM_AUTHENTICATE) {
-        env = talloc_asprintf(pd, "%s=%s", SSSD_REALM, krb5_ctx->realm);
+        env = talloc_asprintf(pd, "%s=%s", SSSD_KRB5_REALM, krb5_ctx->realm);
         if (env == NULL) {
             DEBUG(1, ("talloc_asprintf failed.\n"));
             goto done;
@@ -665,7 +680,7 @@ static void krb5_pam_handler_done(struct tevent_req *req)
             goto done;
         }
 
-        env = talloc_asprintf(pd, "%s=%s", SSSD_KDC, krb5_ctx->kdcip);
+        env = talloc_asprintf(pd, "%s=%s", SSSD_KRB5_KDC, krb5_ctx->kdcip);
         if (env == NULL) {
             DEBUG(1, ("talloc_asprintf failed.\n"));
             goto done;
@@ -761,6 +776,7 @@ int sssm_krb5_auth_init(struct be_ctx *bectx, struct bet_ops **ops,
     bool bool_value;
     int ret;
     struct tevent_signal *sige;
+    struct stat stat_buf;
 
     ctx = talloc_zero(bectx, struct krb5_ctx);
     if (!ctx) {
@@ -776,10 +792,10 @@ int sssm_krb5_auth_init(struct be_ctx *bectx, struct bet_ops **ops,
     if (value == NULL) {
         DEBUG(2, ("Missing krb5KDCIP, authentication might fail.\n"));
     } else {
-        ret = setenv(SSSD_KDC, value, 1);
+        ret = setenv(SSSD_KRB5_KDC, value, 1);
         if (ret != EOK) {
             DEBUG(2, ("setenv %s failed, authentication might fail.\n",
-                      SSSD_KDC));
+                      SSSD_KRB5_KDC));
         }
     }
     ctx->kdcip = value;
@@ -790,14 +806,40 @@ int sssm_krb5_auth_init(struct be_ctx *bectx, struct bet_ops **ops,
     if (value == NULL) {
         DEBUG(4, ("Missing krb5REALM authentication might fail.\n"));
     } else {
-        ret = setenv(SSSD_REALM, value, 1);
+        ret = setenv(SSSD_KRB5_REALM, value, 1);
         if (ret != EOK) {
             DEBUG(2, ("setenv %s failed, authentication might fail.\n",
-                      SSSD_REALM));
+                      SSSD_KRB5_REALM));
         }
     }
     ctx->realm = value;
 
+    ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path,
+                            "krb5ccache_dir", "/tmp", &value);
+    if (ret != EOK) goto fail;
+    ret = lstat(value, &stat_buf);
+    if (ret != EOK) {
+        DEBUG(1, ("lstat for [%s] failed: [%d][%s].\n", value, errno,
+                  strerror(errno)));
+        goto fail;
+    }
+    if ( !S_ISDIR(stat_buf.st_mode) ) {
+        DEBUG(1, ("Value of krb5ccache_dir [%s] is not a directory.\n", value));
+        goto fail;
+    }
+    ctx->ccache_dir = value;
+
+    ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path,
+                            "krb5ccname_template", "FILE:%d/krb5cc_%U_XXXXXX",
+                            &value);
+    if (ret != EOK) goto fail;
+    if (value[0] != '/' && strncmp(value, "FILE:", 5) != 0) {
+        DEBUG(1, ("Currently only file based credential caches are supported "
+                  "and krb5ccname_template must start with '/' or 'FILE:'\n"));
+        goto fail;
+    }
+    ctx->ccname_template = value;
+
     ret = confdb_get_bool(bectx->cdb, ctx, bectx->conf_path,
                           "krb5try_simple_upn", false, &bool_value);
     if (ret != EOK) goto fail;
diff --git a/server/providers/krb5/krb5_auth.h b/server/providers/krb5/krb5_auth.h
index 123a189..d238cb6 100644
--- a/server/providers/krb5/krb5_auth.h
+++ b/server/providers/krb5/krb5_auth.h
@@ -26,12 +26,32 @@
 #ifndef __KRB5_AUTH_H__
 #define __KRB5_AUTH_H__
 
+#include <stdbool.h>
+#include <krb5/krb5.h>
+
 #define MAX_CHILD_MSG_SIZE 255
 #define CCACHE_ENV_NAME "KRB5CCNAME"
+
 #define SSSD_KRB5_CHANGEPW_PRINCIPLE "SSSD_KRB5_CHANGEPW_PRINCIPLE"
+#define SSSD_KRB5_KDC "SSSD_KRB5_KDC"
+#define SSSD_KRB5_REALM "SSSD_KRB5_REALM"
+
 
 typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
 
+struct krb5child_req {
+    pid_t child_pid;
+    int read_from_child_fd;
+    int write_to_child_fd;
+
+    struct be_req *req;
+    struct pam_data *pd;
+    struct krb5_ctx *krb5_ctx;
+
+    char *ccname;
+    const char *homedir;
+};
+
 struct krb5_ctx {
     /* opts taken from kinit */
     /* in seconds */
@@ -64,6 +84,8 @@ struct krb5_ctx {
     char *realm;
     bool try_simple_upn;
     char *changepw_principle;
+    char *ccache_dir;
+    char *ccname_template;
 };
 
 #endif /* __KRB5_AUTH_H__ */
diff --git a/server/providers/krb5/krb5_child.c b/server/providers/krb5/krb5_child.c
index e272a7f..578e822 100644
--- a/server/providers/krb5/krb5_child.c
+++ b/server/providers/krb5/krb5_child.c
@@ -24,12 +24,15 @@
 
 #include <krb5/krb5.h>
 #include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
 
 #include <security/pam_modules.h>
 
 #include "util/util.h"
 #include "providers/dp_backend.h"
 #include "providers/krb5/krb5_auth.h"
+#include "providers/krb5/krb5_utils.h"
 
 struct krb5_req {
     krb5_context ctx;
@@ -46,6 +49,8 @@ struct krb5_req {
     struct pam_data *pd;
     struct krb5_ctx *krb5_ctx;
     errno_t (*child_req)(int fd, struct krb5_req *kr);
+
+    char *ccname;
 };
 
 static krb5_context krb5_error_ctx;
@@ -107,7 +112,6 @@ static errno_t pack_response_packet(struct response *resp, int status, int type,
 static struct response *prepare_response_message(struct krb5_req *kr,
                                         krb5_error_code kerr, int pam_status)
 {
-    const char *cc_name = NULL;
     char *msg = NULL;
     const char *krb5_msg = NULL;
     int ret;
@@ -120,13 +124,12 @@ static struct response *prepare_response_message(struct krb5_req *kr,
     }
 
     if (kerr == 0) {
-        cc_name = krb5_cc_get_name(kr->ctx, kr->cc);
-        if (cc_name == NULL) {
-            DEBUG(1, ("krb5_cc_get_name failed.\n"));
+        if (kr->cc == NULL || kr->ccname == NULL) {
+            DEBUG(1, ("Error obtaining ccname.\n"));
             return NULL;
         }
 
-        msg = talloc_asprintf(kr, "%s=%s",CCACHE_ENV_NAME, cc_name);
+        msg = talloc_asprintf(kr, "%s=%s",CCACHE_ENV_NAME, kr->ccname);
         if (msg == NULL) {
             DEBUG(1, ("talloc_asprintf failed.\n"));
             return NULL;
@@ -157,6 +160,9 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
                                         char *password)
 {
     krb5_error_code kerr = 0;
+    int fd = -1;
+    size_t ccname_len = 0;
+    size_t offset = 0;
 
     kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
                                         password, NULL, NULL, 0, NULL,
@@ -166,16 +172,37 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
         return kerr;
     }
 
-    kerr = krb5_cc_default(kr->ctx, &kr->cc);
+    if (kr->ccname[0] == '/' || strncmp(kr->ccname, "FILE:", 5) == 0) {
+        offset = 0;
+        if (kr->ccname[0] == 'F') {
+            offset = 5;
+        }
+        ccname_len = strlen(kr->ccname + offset);
+        if (ccname_len >= 6 &&
+            strcmp(kr->ccname + (ccname_len-6), "XXXXXX")==0 ) {
+            fd = mkstemp(kr->ccname + offset);
+            if (fd == -1) {
+                DEBUG(1, ("mkstemp failed [%d][%s].\n", errno,
+                          strerror(errno)));
+                kerr = KRB5KRB_ERR_GENERIC;
+                goto done;
+            }
+        }
+    }
+
+    kerr = krb5_cc_resolve(kr->ctx, kr->ccname, &kr->cc);
     if (kerr != 0) {
         KRB5_DEBUG(1, kerr);
-        return kerr;
+        goto done;
     }
 
     kerr = krb5_cc_initialize(kr->ctx, kr->cc, kr->princ);
+    if (fd != -1) {
+        close(fd);
+    }
     if (kerr != 0) {
         KRB5_DEBUG(1, kerr);
-        return kerr;
+        goto done;
     }
 
     kerr = krb5_cc_store_cred(kr->ctx, kr->cc, kr->creds);
@@ -183,12 +210,15 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
         KRB5_DEBUG(1, kerr);
         krb5_cc_destroy(kr->ctx, kr->cc);
         kr->cc = NULL;
-        return kerr;
+        goto done;
     }
 
+    kerr = 0;
+
+done:
     krb5_free_cred_contents(kr->ctx, kr->creds);
 
-    return 0;
+    return kerr;
 
 }
 
@@ -205,15 +235,6 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
     krb5_data result_code_string;
     krb5_data result_string;
 
-    char *changepw_principle = NULL;
-
-    changepw_principle = getenv(SSSD_KRB5_CHANGEPW_PRINCIPLE);
-    if (changepw_principle == NULL) {
-        DEBUG(1, ("Change password principle not available.\n"));
-        kerr = KRB5KRB_ERR_GENERIC;
-        goto sendresponse;
-    }
-
     pass_str = talloc_strndup(kr, (const char *) kr->pd->authtok,
                               kr->pd->authtok_size);
     if (pass_str == NULL) {
@@ -224,7 +245,7 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
 
     kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
                                         pass_str, NULL, NULL, 0,
-                                        changepw_principle,
+                                        kr->krb5_ctx->changepw_principle,
                                         kr->options);
     if (kerr != 0) {
         KRB5_DEBUG(1, kerr);
@@ -362,7 +383,8 @@ sendresponse:
     return EOK;
 }
 
-static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd)
+static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
+                             char **ccname)
 {
     size_t p = 0;
     uint32_t *len;
@@ -385,6 +407,14 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd)
     str = talloc_memdup(pd, buf+p, sizeof(char) * (*len + 1));
     if (str == NULL) return ENOMEM;
     str[*len] = '\0';
+    *ccname = (char *) str;
+    p += *len;
+
+    len = ((uint32_t *)(buf+p));
+    p += sizeof(uint32_t);
+    str = talloc_memdup(pd, buf+p, sizeof(char) * (*len + 1));
+    if (str == NULL) return ENOMEM;
+    str[*len] = '\0';
     pd->authtok = str;
     pd->authtok_size = *len + 1;
     p += *len;
@@ -411,12 +441,12 @@ static int krb5_cleanup(void *ptr)
     struct krb5_req *kr = talloc_get_type(ptr, struct krb5_req);
     if (kr == NULL) return EOK;
 
-    /* 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)
+    if (kr->creds != NULL) {
         krb5_free_cred_contents(kr->ctx, kr->creds);
         krb5_free_creds(kr->ctx, kr->creds);
+    }
     if (kr->name != NULL)
         krb5_free_unparsed_name(kr->ctx, kr->name);
     if (kr->princ != NULL)
@@ -426,6 +456,9 @@ static int krb5_cleanup(void *ptr)
     if (kr->ctx != NULL)
         krb5_free_context(kr->ctx);
 
+    if (kr->krb5_ctx != NULL) {
+        memset(kr->krb5_ctx, 0, sizeof(struct krb5_ctx));
+    }
     memset(kr, 0, sizeof(struct krb5_req));
 
     return EOK;
@@ -445,6 +478,27 @@ static int krb5_setup(struct pam_data *pd, const char *user_princ_str,
     }
     talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup);
 
+    kr->krb5_ctx = talloc_zero(kr, struct krb5_ctx);
+    if (kr->krb5_ctx == NULL) {
+        DEBUG(1, ("talloc failed.\n"));
+        kerr = ENOMEM;
+        goto failed;
+    }
+
+    kr->krb5_ctx->changepw_principle = getenv(SSSD_KRB5_CHANGEPW_PRINCIPLE);
+    if (kr->krb5_ctx->changepw_principle == NULL) {
+        DEBUG(1, ("Cannot read [%s] from environment.\n",
+                  SSSD_KRB5_CHANGEPW_PRINCIPLE));
+        if (pd->cmd == SSS_PAM_CHAUTHTOK) {
+            goto failed;
+        }
+    }
+
+    kr->krb5_ctx->realm = getenv(SSSD_KRB5_REALM);
+    if (kr->krb5_ctx->realm == NULL) {
+        DEBUG(2, ("Cannot read [%s] from environment.\n", SSSD_KRB5_REALM));
+    }
+
     kr->pd = pd;
 
     switch(pd->cmd) {
@@ -513,12 +567,15 @@ failed:
     return kerr;
 }
 
-int main(void)
+int main(int argc, char *argv[])
 {
     uint8_t *buf = NULL;
     int ret;
     struct pam_data *pd = NULL;
     struct krb5_req *kr = NULL;
+    char *ccname;
+
+    debug_prg_name = argv[0];
 
     pd = talloc(NULL, struct pam_data);
 
@@ -536,14 +593,21 @@ int main(void)
     }
     close(STDIN_FILENO);
 
-    ret = unpack_buffer(buf, ret, pd);
+    ret = unpack_buffer(buf, ret, pd, &ccname);
     if (ret != EOK) {
         DEBUG(1, ("unpack_buffer failed.\n"));
         talloc_free(pd);
         exit(-1);
     }
 
-    krb5_setup(pd, pd->upn, &kr);
+    ret = krb5_setup(pd, pd->upn, &kr);
+    if (ret != EOK) {
+        DEBUG(1, ("krb5_setup failed.\n"));
+        talloc_free(pd);
+        exit(-1);
+    }
+    kr->ccname = ccname;
+
     ret = kr->child_req(STDOUT_FILENO, kr);
     if (ret != EOK) {
         DEBUG(1, ("Child request failed.\n"));
diff --git a/server/providers/krb5/krb5_utils.c b/server/providers/krb5/krb5_utils.c
new file mode 100644
index 0000000..68254ab
--- /dev/null
+++ b/server/providers/krb5/krb5_utils.c
@@ -0,0 +1,141 @@
+/*
+    SSSD
+
+    Kerberos 5 Backend Module -- Utilities
+
+    Authors:
+        Sumit Bose <sbose at 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 <string.h>
+#include <stdlib.h>
+
+#include "providers/krb5/krb5_utils.h"
+#include "providers/krb5/krb5_auth.h"
+#include "util/util.h"
+
+char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
+                             const char *template)
+{
+    char *copy;
+    char *p;
+    char *n;
+    char *result = NULL;
+
+    copy = talloc_strdup(mem_ctx, template);
+    if (copy == NULL) {
+        DEBUG(1, ("talloc_strdup failed.\n"));
+        return NULL;
+    }
+
+    result = talloc_strdup(mem_ctx, "");
+    if (result == NULL) {
+        DEBUG(1, ("talloc_strdup failed.\n"));
+        return NULL;
+    }
+
+    p = copy;
+    while ( (n = strchr(p, '%')) != NULL) {
+        *n = '\0';
+        n++;
+        if ( *n == '\0' ) {
+            DEBUG(1, ("format error, single %% at the end of the template.\n"));
+            return NULL;
+        }
+
+        switch( *n ) {
+            case 'u':
+                if (kr->pd->user == NULL) {
+                    DEBUG(1, ("Cannot expand user name template "
+                              "because user name is empty.\n"));
+                    return NULL;
+                }
+                result = talloc_asprintf_append(result, "%s%s", p,
+                                                kr->pd->user);
+                break;
+            case 'U':
+                if (kr->pd->pw_uid <= 0) {
+                    DEBUG(1, ("Cannot expand uid template "
+                              "because uid is invalid.\n"));
+                    return NULL;
+                }
+                result = talloc_asprintf_append(result, "%s%d", p,
+                                                kr->pd->pw_uid);
+                break;
+            case 'p':
+                if (kr->pd->upn == NULL) {
+                    DEBUG(1, ("Cannot expand user principal name template "
+                              "because upn is empty.\n"));
+                    return NULL;
+                }
+                result = talloc_asprintf_append(result, "%s%s", p, kr->pd->upn);
+                break;
+            case '%':
+                result = talloc_asprintf_append(result, "%s%%", p);
+                break;
+            case 'r':
+                if (kr->krb5_ctx->realm == NULL) {
+                    DEBUG(1, ("Cannot expand realm template "
+                              "because value is not available.\n"));
+                    return NULL;
+                }
+                result = talloc_asprintf_append(result, "%s%s", p,
+                                                kr->krb5_ctx->realm);
+                break;
+            case 'h':
+                if (kr->homedir == NULL) {
+                    DEBUG(1, ("Cannot expand home directory template "
+                              "because the path is not available.\n"));
+                    return NULL;
+                }
+                result = talloc_asprintf_append(result, "%s%s", p, kr->homedir);
+                break;
+            case 'd':
+                if (kr->krb5_ctx->ccache_dir == NULL) {
+                    DEBUG(1, ("Cannot expand ccache directory template "
+                              "because value is not available.\n"));
+                    return NULL;
+                }
+                result = talloc_asprintf_append(result, "%s%s", p,
+                                                kr->krb5_ctx->ccache_dir);
+                break;
+            case 'P':
+                if (kr->pd->cli_pid == 0) {
+                    DEBUG(1, ("Cannot expand PID template "
+                              "because PID is not available.\n"));
+                    return NULL;
+                }
+                result = talloc_asprintf_append(result, "%s%d", p,
+                                                kr->pd->cli_pid);
+                break;
+            default:
+                DEBUG(1, ("format error, unknown template [%%%c].\n", *n));
+                return NULL;
+        }
+
+        if (result == NULL) {
+            DEBUG(1, ("talloc_asprintf_append failed.\n"));
+            return NULL;
+        }
+
+        p = n + 1;
+    }
+
+    result = talloc_asprintf_append(result, "%s", p);
+
+    return result;
+}
diff --git a/server/providers/krb5/krb5_utils.h b/server/providers/krb5/krb5_utils.h
new file mode 100644
index 0000000..dec6f7f
--- /dev/null
+++ b/server/providers/krb5/krb5_utils.h
@@ -0,0 +1,37 @@
+/*
+    SSSD
+
+    Kerberos Backend, header file for utilities
+
+    Authors:
+        Sumit Bose <sbose at 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_UTILS_H__
+#define __KRB5_UTILS_H__
+
+#include <talloc.h>
+
+#include "providers/krb5/krb5_auth.h"
+#include "providers/data_provider.h"
+
+char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
+                             const char *template);
+
+#endif /* __KRB5_UTILS_H__ */
diff --git a/server/tests/krb5_utils-tests.c b/server/tests/krb5_utils-tests.c
new file mode 100644
index 0000000..1ebebe6
--- /dev/null
+++ b/server/tests/krb5_utils-tests.c
@@ -0,0 +1,292 @@
+/*
+    SSSD
+
+    Kerberos 5 Backend Module -- Utilities tests 
+
+    Authors:
+        Sumit Bose <sbose at 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 <stdlib.h>
+#include <check.h>
+
+#include "providers/krb5/krb5_utils.h"
+#include "providers/krb5/krb5_auth.h"
+
+#define BASE "/abc/def"
+
+#define USERNAME "testuser"
+#define UID "12345"
+#define PRINCIPLE_NAME "testuser at EXAMPLE.COM"
+#define REALM "REALM.ORG"
+#define HOME_DIRECTORY "/home/testuser"
+#define CCACHE_DIR "/var/tmp"
+#define PID "4321"
+
+TALLOC_CTX *tmp_ctx = NULL;
+struct krb5child_req *kr;
+
+void setup_talloc_context(void)
+{
+    int ret;
+    struct pam_data *pd;
+    struct krb5_ctx *krb5_ctx;
+    fail_unless(tmp_ctx == NULL, "Talloc context already initialized.");
+    tmp_ctx = talloc_new(NULL);
+    fail_unless(tmp_ctx != NULL, "Cannot create talloc context.");
+
+    kr = talloc_zero(tmp_ctx, struct krb5child_req);
+    fail_unless(kr != NULL, "Cannot create krb5child_req structure.");
+
+    pd = talloc_zero(tmp_ctx, struct pam_data);
+    fail_unless(pd != NULL, "Cannot create pam_data structure.");
+
+    krb5_ctx = talloc_zero(tmp_ctx, struct krb5_ctx);
+    fail_unless(pd != NULL, "Cannot create krb5_ctx structure.");
+
+    pd->user = USERNAME;
+    pd->pw_uid = atoi(UID);
+    pd->upn = PRINCIPLE_NAME;
+    pd->cli_pid = atoi(PID);
+
+    krb5_ctx->realm = REALM;
+    krb5_ctx->ccache_dir = CCACHE_DIR;
+
+    kr->homedir = HOME_DIRECTORY;
+
+    kr->pd = pd;
+    kr->krb5_ctx = krb5_ctx;
+
+}
+
+void free_talloc_context(void)
+{
+    int ret;
+    fail_unless(tmp_ctx != NULL, "Talloc context already freed.");
+    ret = talloc_free(tmp_ctx);
+    fail_unless(ret == 0, "Connot free talloc context.");
+}
+
+START_TEST(test_multiple_substitutions)
+{
+    char *test_template = BASE"_%u_%U_%u";
+    char *expected = BASE"_"USERNAME"_"UID"_"USERNAME;
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    fail_unless(strcmp(result, expected) == 0,
+                "Expansion failed, result [%s], expected [%s].",
+                result, expected);
+}
+END_TEST
+
+START_TEST(test_username)
+{
+    char *test_template = BASE"_%u";
+    char *expected = BASE"_"USERNAME;
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    fail_unless(strcmp(result, expected) == 0,
+                "Expansion failed, result [%s], expected [%s].",
+                result, expected);
+}
+END_TEST
+
+START_TEST(test_uid)
+{
+    char *test_template = BASE"_%U";
+    char *expected = BASE"_"UID;
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    fail_unless(strcmp(result, expected) == 0,
+                "Expansion failed, result [%s], expected [%s].",
+                result, expected);
+}
+END_TEST
+
+START_TEST(test_upn)
+{
+    char *test_template = BASE"_%p";
+    char *expected = BASE"_"PRINCIPLE_NAME;
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    fail_unless(strcmp(result, expected) == 0,
+                "Expansion failed, result [%s], expected [%s].",
+                result, expected);
+}
+END_TEST
+
+START_TEST(test_realm)
+{
+    char *test_template = BASE"_%r";
+    char *expected = BASE"_"REALM;
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    fail_unless(strcmp(result, expected) == 0,
+                "Expansion failed, result [%s], expected [%s].",
+                result, expected);
+}
+END_TEST
+
+START_TEST(test_home)
+{
+    char *test_template = BASE"_%h";
+    char *expected = BASE"_"HOME_DIRECTORY;
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    fail_unless(strcmp(result, expected) == 0,
+                "Expansion failed, result [%s], expected [%s].",
+                result, expected);
+}
+END_TEST
+
+START_TEST(test_ccache_dir)
+{
+    char *test_template = BASE"_%d";
+    char *expected = BASE"_"CCACHE_DIR;
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    fail_unless(strcmp(result, expected) == 0,
+                "Expansion failed, result [%s], expected [%s].",
+                result, expected);
+}
+END_TEST
+
+START_TEST(test_pid)
+{
+    char *test_template = BASE"_%P";
+    char *expected = BASE"_"PID;
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    fail_unless(strcmp(result, expected) == 0,
+                "Expansion failed, result [%s], expected [%s].",
+                result, expected);
+}
+END_TEST
+
+START_TEST(test_percent)
+{
+    char *test_template = BASE"_%%";
+    char *expected = BASE"_%";
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    fail_unless(strcmp(result, expected) == 0,
+                "Expansion failed, result [%s], expected [%s].",
+                result, expected);
+}
+END_TEST
+
+START_TEST(test_unknow_template)
+{
+    char *test_template = BASE"_%X";
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result == NULL, "Unknown template [%s] should fail.",
+                test_template);
+}
+END_TEST
+
+START_TEST(test_NULL)
+{
+    char *test_template = NULL;
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result == NULL, "Expected NULL as a result for an empty input.",
+                test_template);
+}
+END_TEST
+
+START_TEST(test_no_substitution)
+{
+    char *test_template = BASE;
+    char *result;
+
+    result = expand_ccname_template(tmp_ctx, kr, test_template);
+
+    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    fail_unless(strcmp(result, test_template) == 0,
+                "Expansion failed, result [%s], expected [%s].",
+                result, test_template);
+}
+END_TEST
+
+Suite *krb5_utils_suite (void)
+{
+    Suite *s = suite_create ("krb5_utils");
+
+    TCase *tc_ccname_template = tcase_create ("ccname_template");
+    tcase_add_checked_fixture (tc_ccname_template, setup_talloc_context,
+                               free_talloc_context);
+    tcase_add_test (tc_ccname_template, test_no_substitution);
+    tcase_add_test (tc_ccname_template, test_NULL);
+    tcase_add_test (tc_ccname_template, test_unknow_template);
+    tcase_add_test (tc_ccname_template, test_username);
+    tcase_add_test (tc_ccname_template, test_uid);
+    tcase_add_test (tc_ccname_template, test_upn);
+    tcase_add_test (tc_ccname_template, test_realm);
+    tcase_add_test (tc_ccname_template, test_home);
+    tcase_add_test (tc_ccname_template, test_ccache_dir);
+    tcase_add_test (tc_ccname_template, test_pid);
+    tcase_add_test (tc_ccname_template, test_percent);
+    tcase_add_test (tc_ccname_template, test_multiple_substitutions);
+    suite_add_tcase (s, tc_ccname_template);
+
+    return s;
+}
+
+int main(void)
+{
+  int number_failed;
+  Suite *s = krb5_utils_suite ();
+  SRunner *sr = srunner_create (s);
+  srunner_run_all (sr, CK_NORMAL);
+  number_failed = srunner_ntests_failed (sr);
+  srunner_free (sr);
+  return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
-- 
1.6.2.5



More information about the sssd-devel mailing list