>From 021ee9186ac3a6f9939f1ae0bc748aae112de660 Mon Sep 17 00:00:00 2001 From: Sumit Bose 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 -#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 - - 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 . -*/ - -#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 @@ + + + krb5ccache_dir (string) + + + Directory to store credential caches. + + + Default: /tmp + + + + + + krb5ccname_template (string) + + + Location of the user's credential cache. Currently + only file based credential caches are supported. In + the template the following sequences are + substituted: + + + %u + login name + + + %U + login UID + + + %p + principle name + + + + %r + realm name + + + %h + home directory + + + + %d + value of krb5ccache_dir + + + + + %P + the process ID of the sssd + client + + + + %% + a literal '%' + + + + If the template ends with 'XXXXXX' mkstemp(3) is + used to create a unique filename in a safe way. + + + Default: FILE:%d/krb5cc_%U_XXXXXX + + + + 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 #include #include +#include + #include #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 +#include + #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 #include +#include +#include #include #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 + + 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 . +*/ +#include +#include + +#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 + + 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 . +*/ + +#ifndef __KRB5_UTILS_H__ +#define __KRB5_UTILS_H__ + +#include + +#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 + + 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 . +*/ + +#include +#include + +#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@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