From 945e1f1fab935616bde0d1d64d9e16225b44c183 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Thu, 10 Sep 2009 14:43:33 +0200 Subject: 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/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 ++++++++++ 5 files changed, 356 insertions(+), 50 deletions(-) create mode 100644 server/providers/krb5/krb5_utils.c create mode 100644 server/providers/krb5/krb5_utils.h (limited to 'server/providers/krb5') diff --git a/server/providers/krb5/krb5_auth.c b/server/providers/krb5/krb5_auth.c index 14562760e..0fb74ddc4 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; @@ -501,13 +509,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, @@ -534,7 +543,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, @@ -563,6 +572,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: @@ -576,7 +591,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; @@ -665,7 +680,7 @@ static void krb5_pam_handler_done(struct tevent_req *req) } 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; @@ -676,7 +691,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; @@ -770,6 +785,7 @@ int sssm_krb5_auth_init(struct be_ctx *bectx, bool bool_value; int ret; struct tevent_signal *sige; + struct stat stat_buf; ctx = talloc_zero(bectx, struct krb5_ctx); if (!ctx) { @@ -785,10 +801,10 @@ int sssm_krb5_auth_init(struct be_ctx *bectx, 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; @@ -799,14 +815,40 @@ int sssm_krb5_auth_init(struct be_ctx *bectx, 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 123a1895f..d238cb635 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 e272a7f3b..578e82275 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; @@ -380,6 +402,14 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd) pd->upn = (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'; + *ccname = (char *) str; + p += *len; + len = ((uint32_t *)(buf+p)); p += sizeof(uint32_t); str = talloc_memdup(pd, buf+p, sizeof(char) * (*len + 1)); @@ -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 000000000..68254abce --- /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 000000000..dec6f7f2f --- /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__ */ -- cgit