summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2012-06-05 23:41:12 +0200
committerStephen Gallagher <sgallagh@redhat.com>2012-06-14 15:52:29 -0400
commit9a3ba9ca00e73adc3fb17ce8afa532076768023b (patch)
treebeef4c810b5c340305fdebaa30b623ffec44c3cb /src
parentfd8595874aa06c8057740001ec465ba76b4af142 (diff)
downloadsssd-9a3ba9ca00e73adc3fb17ce8afa532076768023b.tar.gz
sssd-9a3ba9ca00e73adc3fb17ce8afa532076768023b.tar.xz
sssd-9a3ba9ca00e73adc3fb17ce8afa532076768023b.zip
Add support for storing credential caches in the DIR: back end
https://fedorahosted.org/sssd/ticket/974
Diffstat (limited to 'src')
-rw-r--r--src/man/sssd-krb5.5.xml10
-rw-r--r--src/providers/krb5/krb5_child.c228
-rw-r--r--src/providers/krb5/krb5_common.c34
-rw-r--r--src/providers/krb5/krb5_opts.h4
-rw-r--r--src/providers/krb5/krb5_utils.c307
-rw-r--r--src/providers/krb5/krb5_utils.h7
-rw-r--r--src/tests/krb5_child-test.c46
-rw-r--r--src/tests/krb5_utils-tests.c66
-rw-r--r--src/util/sss_krb5.c21
-rw-r--r--src/util/sss_krb5.h3
10 files changed, 615 insertions, 111 deletions
diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index 247337a90..1baee36e9 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -157,10 +157,12 @@
<term>krb5_ccname_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:
+ Location of the user's credential cache. Two credential
+ cache types are currently supported - <quote>FILE</quote>
+ and <quote>DIR</quote>. The cache can either be specified
+ as <replaceable>TYPE:RESIDUAL</replaceable>, or an absolute
+ path, which implies the <quote>FILE</quote> type. In the
+ template the following sequences are substituted:
<variablelist>
<varlistentry>
<term>%u</term>
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 195c431af..6b8722a8a 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -221,6 +221,51 @@ done:
return kerr;
}
+static krb5_error_code
+store_creds_in_ccache(krb5_context ctx, krb5_principal princ,
+ krb5_ccache cc, krb5_creds *creds)
+{
+ krb5_error_code kerr;
+ krb5_creds *l_cred;
+
+ kerr = krb5_cc_initialize(ctx, cc, princ);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+ goto done;
+ }
+
+ if (creds == NULL) {
+ kerr = create_empty_cred(ctx, princ, &l_cred);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+ goto done;
+ }
+ } else {
+ l_cred = creds;
+ }
+
+ kerr = krb5_cc_store_cred(ctx, cc, l_cred);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+ goto done;
+ }
+
+ kerr = krb5_cc_switch(ctx, cc);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+ goto done;
+ }
+
+ kerr = krb5_cc_close(ctx, cc);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+ goto done;
+ }
+
+done:
+ return kerr;
+}
+
static krb5_error_code create_ccache_file(krb5_context ctx,
krb5_principal princ,
char *ccname, krb5_creds *creds)
@@ -232,7 +277,6 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
size_t ccname_len;
char *dummy;
char *tmp_ccname;
- krb5_creds *l_cred;
TALLOC_CTX *tmp_ctx = NULL;
mode_t old_umask;
@@ -285,38 +329,16 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
goto done;
}
- kerr = krb5_cc_initialize(ctx, tmp_cc, princ);
- if (kerr != 0) {
- KRB5_DEBUG(1, kerr);
- goto done;
- }
+ kerr = store_creds_in_ccache(ctx, princ, tmp_cc, creds);
if (fd != -1) {
close(fd);
fd = -1;
}
-
- if (creds == NULL) {
- kerr = create_empty_cred(ctx, princ, &l_cred);
- if (kerr != 0) {
- KRB5_DEBUG(1, kerr);
- goto done;
- }
- } else {
- l_cred = creds;
- }
-
- kerr = krb5_cc_store_cred(ctx, tmp_cc, l_cred);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
goto done;
}
- kerr = krb5_cc_close(ctx, tmp_cc);
- if (kerr != 0) {
- KRB5_DEBUG(1, kerr);
- goto done;
- }
- tmp_cc = NULL;
ccname_len = strlen(cc_file_name);
if (ccname_len >= 6 && strcmp(cc_file_name + (ccname_len-6), "XXXXXX")==0 ) {
@@ -338,16 +360,161 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
DEBUG(SSSDBG_TRACE_LIBS, ("Created ccache file: [%s]\n", ccname));
done:
+ if (kerr != 0 && tmp_cc != NULL) {
+ krb5_cc_destroy(ctx, tmp_cc);
+ }
+
if (fd != -1) {
close(fd);
}
+
+ talloc_free(tmp_ctx);
+ return kerr;
+}
+
+static errno_t
+create_ccdir(const char *dirname, uid_t uid, gid_t gid)
+{
+ mode_t old_umask;
+ struct stat statbuf;
+ errno_t ret;
+
+ old_umask = umask(0000);
+ ret = mkdir(dirname, 0700);
+ umask(old_umask);
+ if (ret == -1) {
+ /* Failing the mkdir is only OK if the directory already
+ * exists AND it is owned by the same user and group and
+ * has the correct permissions.
+ */
+ ret = errno;
+ if (ret == EEXIST) {
+ errno = 0;
+ ret = stat(dirname, &statbuf);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("stat failed [%d]: %s\n", ret, strerror(ret)));
+ return EIO;
+ }
+
+ if (statbuf.st_uid != uid || statbuf.st_gid != gid) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("The directory %s is owned by %d/%d, expected %d/%d\n",
+ dirname, statbuf.st_uid, statbuf.st_gid, uid, gid));
+ return EACCES;
+ }
+
+ if ((statbuf.st_mode & ~S_IFMT) != 0700) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("The directory %s has wrong permissions %o, expected 0700\n",
+ dirname, (statbuf.st_mode & ~S_IFMT)));
+ return EACCES;
+ }
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("mkdir [%s] failed [%d]: %s\n",
+ dirname, ret, strerror(ret)));
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static krb5_error_code
+create_ccache_in_dir(uid_t uid, gid_t gid,
+ krb5_context ctx,
+ krb5_principal princ,
+ char *ccname, krb5_creds *creds)
+{
+ krb5_error_code kerr;
+ krb5_ccache tmp_cc = NULL;
+ const char *dirname;
+
+ DEBUG(SSSDBG_FUNC_DATA, ("Creating ccache at [%s]\n", ccname));
+
+ dirname = sss_krb5_residual_check_type(ccname, SSS_KRB5_TYPE_DIR);
+ if (dirname == NULL) {
+ return EIO;
+ }
+
+ kerr = become_user(uid, gid);
+ if (kerr != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("become_user failed.\n"));
+ goto done;
+ }
+
+ if (dirname[0] == ':') {
+ /* Cache name in the form of DIR::filepath represents a single
+ * ccache in a collection that we are trying to reuse.
+ * See src/lib/krb5/ccache/cc_dir.c in the MIT Kerberos tree.
+ */
+ kerr = krb5_cc_resolve(ctx, ccname, &tmp_cc);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+ goto done;
+ }
+ } else if (dirname[0] == '/') {
+ /* An absolute path denotes that krb5_child should create a new
+ * ccache. We can afford to just call mkdir(dirname) because we
+ * only want the last component to be created.
+ */
+
+ kerr = create_ccdir(dirname, uid, gid);
+ if (kerr) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("Cannot create directory %s\n", dirname));
+ goto done;
+ }
+
+ kerr = krb5_cc_set_default_name(ctx, ccname);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+ goto done;
+ }
+
+ kerr = krb5_cc_new_unique(ctx, "DIR", NULL, &tmp_cc);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+ goto done;
+ }
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Wrong residual format for DIR in ccache %s\n", ccname));
+ return EIO;
+ }
+
+ kerr = store_creds_in_ccache(ctx, princ, tmp_cc, creds);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+ goto done;
+ }
+
+done:
if (kerr != 0 && tmp_cc != NULL) {
krb5_cc_destroy(ctx, tmp_cc);
}
+ return kerr;
+}
- talloc_free(tmp_ctx);
+static krb5_error_code
+create_ccache(uid_t uid, gid_t gid, krb5_context ctx,
+ krb5_principal princ, char *ccname, krb5_creds *creds)
+{
+ enum sss_krb5_cc_type cctype;
+
+ cctype = sss_krb5_get_type(ccname);
+ switch (cctype) {
+ case SSS_KRB5_TYPE_FILE:
+ return create_ccache_file(ctx, princ, ccname, creds);
+ case SSS_KRB5_TYPE_DIR:
+ return create_ccache_in_dir(uid, gid, ctx, princ, ccname, creds);
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown cache type\n"));
+ return EINVAL;
+ }
- return kerr;
+ return EINVAL; /* Should never get here */
}
static errno_t pack_response_packet(struct response *resp, int status,
@@ -729,9 +896,9 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
}
/* Use the updated principal in the creds in case canonicalized */
- kerr = create_ccache_file(kr->ctx,
- kr->creds ? kr->creds->client : kr->princ,
- kr->ccname, kr->creds);
+ kerr = create_ccache(kr->uid, kr->gid, kr->ctx,
+ kr->creds ? kr->creds->client : kr->princ,
+ kr->ccname, kr->creds);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
goto done;
@@ -1142,7 +1309,8 @@ static errno_t create_empty_ccache(int fd, struct krb5_req *kr)
DEBUG(SSSDBG_TRACE_LIBS, ("Creating empty ccache\n"));
- ret = create_ccache_file(kr->ctx, kr->princ, kr->ccname, NULL);
+ ret = create_ccache(kr->uid, kr->gid, kr->ctx,
+ kr->princ, kr->ccname, NULL);
if (ret != 0) {
KRB5_DEBUG(1, ret);
pam_status = PAM_SYSTEM_ERR;
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 91816662b..f4033d295 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -182,13 +182,37 @@ errno_t check_and_export_options(struct dp_option *opts,
}
cc_be = sss_krb5_get_type(dummy);
- if (cc_be != SSS_KRB5_TYPE_FILE || dummy[0] != '/') {
- DEBUG(SSSDBG_CONF_SETTINGS,
- ("Currently only file based credential caches are supported "
- "and krb5ccname_template must start with '/' or 'FILE:'\n"));
+ switch (cc_be) {
+ case SSS_KRB5_TYPE_FILE:
+ DEBUG(SSSDBG_CONF_SETTINGS, ("ccache is of type FILE\n"));
+ krb5_ctx->cc_be = &file_cc;
+ if (dummy[0] != '/') {
+ /* FILE:/path/to/cc */
+ break;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS, ("The ccname template was "
+ "missing an explicit type, but is an absolute "
+ "path specifier. Assuming FILE:\n"));
+
+ dummy = talloc_asprintf(opts, "FILE:%s", dummy);
+ if (!dummy) return ENOMEM;
+
+ ret = dp_opt_set_string(opts, KRB5_CCNAME_TMPL, dummy);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("dp_opt_set_string failed.\n"));
+ return ret;
+ }
+ break;
+ case SSS_KRB5_TYPE_DIR:
+ DEBUG(SSSDBG_CONF_SETTINGS, ("ccache is of type DIR\n"));
+ krb5_ctx->cc_be = &dir_cc;
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE, ("Unkown ccname database\n"));
return EINVAL;
+ break;
}
- krb5_ctx->cc_be = &file_cc;
return EOK;
}
diff --git a/src/providers/krb5/krb5_opts.h b/src/providers/krb5/krb5_opts.h
index 7f47290c9..45dfec544 100644
--- a/src/providers/krb5/krb5_opts.h
+++ b/src/providers/krb5/krb5_opts.h
@@ -28,8 +28,8 @@
struct dp_option default_krb5_opts[] = {
{ "krb5_server", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING },
- { "krb5_ccachedir", DP_OPT_STRING, { "/tmp" }, NULL_STRING },
- { "krb5_ccname_template", DP_OPT_STRING, { "FILE:%d/krb5cc_%U_XXXXXX" }, NULL_STRING},
+ { "krb5_ccachedir", DP_OPT_STRING, { "/run/user/%u" }, NULL_STRING },
+ { "krb5_ccname_template", DP_OPT_STRING, { "DIR:%d" }, NULL_STRING},
{ "krb5_auth_timeout", DP_OPT_NUMBER, { .number = 15 }, NULL_NUMBER },
{ "krb5_keytab", DP_OPT_STRING, { "/etc/krb5.keytab" }, NULL_STRING },
{ "krb5_validate", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index 9f02fe332..35ece8117 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -23,6 +23,7 @@
*/
#include <string.h>
#include <stdlib.h>
+#include <libgen.h>
#include "providers/krb5/krb5_utils.h"
#include "providers/krb5/krb5_auth.h"
@@ -330,7 +331,7 @@ check_ccache_re(const char *filename, pcre *illegal_re)
return EFAULT;
}
-static errno_t
+errno_t
create_ccache_dir(const char *dirname, pcre *illegal_re,
uid_t uid, gid_t gid, bool private_path)
{
@@ -518,6 +519,42 @@ done:
return EOK;
}
+static errno_t
+create_ccache_dir_head(const char *parent, pcre *illegal_re,
+ uid_t uid, gid_t gid, bool private_path)
+{
+ char *dirname;
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *end;
+ errno_t ret;
+
+ dirname = talloc_strdup(tmp_ctx, parent);
+ if (dirname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strdup failed.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* We'll remove all trailing slashes from the back so that
+ * we only pass /some/path to find_ccdir_parent_data, not
+ * /some/path/ */
+ do {
+ end = strrchr(dirname, '/');
+ if (end == NULL || end == dirname) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot find parent directory of [%s], "
+ "/ is not allowed.\n", dirname));
+ ret = EINVAL;
+ goto done;
+ }
+ *end = '\0';
+ } while (*(end+1) == '\0');
+
+ ret = create_ccache_dir(dirname, illegal_re, uid, gid, private_path);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
/*======== ccache back end utilities ========*/
struct sss_krb5_cc_be *
get_cc_be_ops(enum sss_krb5_cc_type type)
@@ -528,6 +565,9 @@ get_cc_be_ops(enum sss_krb5_cc_type type)
case SSS_KRB5_TYPE_FILE:
be = &file_cc;
break;
+ case SSS_KRB5_TYPE_DIR:
+ be = &dir_cc;
+ break;
case SSS_KRB5_TYPE_UNKNOWN:
be = NULL;
break;
@@ -551,10 +591,6 @@ cc_file_create(const char *location, pcre *illegal_re,
uid_t uid, gid_t gid, bool private_path)
{
const char *filename;
- char *dirname;
- char *end;
- TALLOC_CTX *tmp_ctx = NULL;
- errno_t ret;
filename = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_FILE);
if (filename == NULL) {
@@ -562,31 +598,7 @@ cc_file_create(const char *location, pcre *illegal_re,
return EINVAL;
}
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new failed.\n"));
- return ENOMEM;
- }
-
- dirname = talloc_strdup(tmp_ctx, filename);
- if (dirname == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strdup failed.\n"));
- ret = ENOMEM;
- goto done;
- }
-
- end = strrchr(dirname, '/');
- if (end == NULL || end == dirname) {
- DEBUG(SSSDBG_CRIT_FAILURE, ("Missing filename in [%s].\n", dirname));
- ret = EINVAL;
- goto done;
- }
- *end = '\0';
-
- ret = create_ccache_dir(dirname, illegal_re, uid, gid, private_path);
-done:
- talloc_free(tmp_ctx);
- return ret;
+ return create_ccache_dir_head(filename, illegal_re, uid, gid, private_path);
}
static errno_t
@@ -619,6 +631,9 @@ cc_residual_is_used(uid_t uid, const char *ccname,
}
switch (type) {
+ case SSS_KRB5_TYPE_DIR:
+ ret = S_ISDIR(stat_buf.st_mode);
+ break;
case SSS_KRB5_TYPE_FILE:
ret = S_ISREG(stat_buf.st_mode);
break;
@@ -661,6 +676,9 @@ cc_file_check_existing(const char *location, uid_t uid,
bool active;
bool valid;
const char *filename;
+ krb5_ccache ccache = NULL;
+ krb5_context context = NULL;
+ krb5_error_code kerr;
filename = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_FILE);
if (!filename) {
@@ -679,11 +697,26 @@ cc_file_check_existing(const char *location, uid_t uid,
return ret;
}
- ret = check_for_valid_tgt(location, realm, princ, &valid);
- if (ret != EOK) {
+ kerr = krb5_init_context(&context);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to init kerberos context\n"));
+ return EIO;
+ }
+
+ kerr = krb5_cc_resolve(context, location, &ccache);
+ if (kerr != 0) {
+ krb5_free_context(context);
+ DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_cc_resolve failed.\n"));
+ return EIO;
+ }
+
+ kerr = check_for_valid_tgt(context, ccache, realm, princ, &valid);
+ krb5_free_context(context);
+ krb5_cc_close(context, ccache);
+ if (kerr != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
("Could not check if ccache contains a valid principal\n"));
- return ret;
+ return EIO;
}
*_active = active;
@@ -735,3 +768,211 @@ struct sss_krb5_cc_be file_cc = {
.ccache_for_princ = cc_file_cache_for_princ,
.remove = cc_file_remove,
};
+
+/*======== Operations on the DIR: back end ========*/
+errno_t
+cc_dir_create(const char *location, pcre *illegal_re,
+ uid_t uid, gid_t gid, bool private_path)
+{
+ const char *dirname;
+
+ dirname = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_DIR);
+ if (dirname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Bad residual type\n"));
+ return EINVAL;
+ }
+
+ return create_ccache_dir_head(dirname, illegal_re, uid, gid, private_path);
+}
+
+static krb5_error_code
+get_ccache_for_princ(krb5_context context, const char *location,
+ const char *princ, krb5_ccache *_ccache)
+{
+ krb5_error_code krberr;
+ krb5_principal client_principal = NULL;
+
+ krberr = krb5_cc_set_default_name(context, location);
+ if (krberr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, ("krb5_cc_resolve failed.\n"));
+ return krberr;
+ }
+
+ krberr = krb5_parse_name(context, princ, &client_principal);
+ if (krberr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, ("krb5_parse_name failed.\n"));
+ return krberr;
+ }
+
+ krberr = krb5_cc_cache_match(context, client_principal, _ccache);
+ krb5_free_principal(context, client_principal);
+ return krberr;
+}
+
+errno_t
+cc_dir_check_existing(const char *location, uid_t uid,
+ const char *realm, const char *princ,
+ bool *_active, bool *_valid)
+{
+ bool active = false;
+ bool valid = false;
+ krb5_ccache ccache = NULL;
+ krb5_context context = NULL;
+ krb5_error_code krberr;
+ enum sss_krb5_cc_type type;
+ const char *filename;
+ const char *dir;
+ char *tmp;
+ errno_t ret;
+
+ type = sss_krb5_get_type(location);
+ if (type != SSS_KRB5_TYPE_DIR) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("%s is not of type DIR:\n", location));
+ return EINVAL;
+ }
+
+ filename = sss_krb5_cc_file_path(location);
+ if (!filename) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Existing ccname does not contain path into the collection"));
+ return EINVAL;
+ }
+
+ if (filename[0] != '/') {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Only absolute path names are allowed.\n"));
+ return EINVAL;
+ }
+
+ tmp = talloc_strdup(NULL, filename);
+ if (!tmp) return ENOMEM;
+
+ dir = dirname(tmp);
+ if (!dir) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Cannot base get directory of %s\n", location));
+ return EINVAL;
+ }
+
+ ret = cc_residual_is_used(uid, dir, SSS_KRB5_TYPE_DIR, &active);
+ talloc_free(tmp);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, ("Could not check if ccache is active\n"));
+ return ret;
+ }
+
+ krberr = krb5_init_context(&context);
+ if (krberr) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to init kerberos context\n"));
+ return EIO;
+ }
+
+ krberr = krb5_cc_resolve(context, location, &ccache);
+ if (krberr == KRB5_FCC_NOFILE || ccache == NULL) {
+ /* KRB5_FCC_NOFILE would be returned if the directory components
+ * of the DIR cache do not exist, which is the case in /run
+ * after a reboot
+ */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ ("ccache %s is missing or empty\n", location));
+ valid = false;
+ ret = EOK;
+ goto done;
+ } else if (krberr != 0) {
+ KRB5_DEBUG(SSSDBG_OP_FAILURE, context, krberr);
+ DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_cc_resolve failed.\n"));
+ ret = EIO;
+ goto done;
+ }
+
+ krberr = check_for_valid_tgt(context, ccache, realm, princ, &valid);
+ if (krberr != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Could not check if ccache contains a valid principal\n"));
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ if (ccache) krb5_cc_close(context, ccache);
+ krb5_free_context(context);
+ *_active = active;
+ *_valid = valid;
+ return ret;
+}
+
+const char *
+cc_dir_cache_for_princ(TALLOC_CTX *mem_ctx, const char *location,
+ const char *princ)
+{
+ krb5_context context = NULL;
+ krb5_error_code krberr;
+ krb5_ccache ccache = NULL;
+ char *name;
+ const char *ccname;
+
+ ccname = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_DIR);
+ if (!ccname) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot get ccname file from %s\n",
+ location));
+ return NULL;
+ }
+
+ /* ccname already points to a subsidiary cache */
+ if (ccname[0] == ':' && ccname[1] && ccname[1] == '/') {
+ return talloc_strdup(mem_ctx, location);
+ }
+
+ krberr = krb5_init_context(&context);
+ if (krberr) {
+ DEBUG(SSSDBG_OP_FAILURE, ("Failed to init kerberos context\n"));
+ return NULL;
+ }
+
+ krberr = get_ccache_for_princ(context, location, princ, &ccache);
+ if (krberr) {
+ DEBUG(SSSDBG_TRACE_FUNC, ("No principal for %s in %s\n",
+ princ, location));
+ krb5_free_context(context);
+ return NULL;
+ }
+
+ krberr = krb5_cc_get_full_name(context, ccache, &name);
+ if (ccache) krb5_cc_close(context, ccache);
+ krb5_free_context(context);
+ if (krberr) {
+ DEBUG(SSSDBG_TRACE_FUNC, ("Could not get full name of ccache\n"));
+ return NULL;
+ }
+
+ return talloc_strdup(mem_ctx, name);
+}
+
+errno_t
+cc_dir_remove(const char *location)
+{
+ const char *subsidiary;
+
+ if (sss_krb5_get_type(location) != SSS_KRB5_TYPE_DIR) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("%s is not of type DIR\n", location));
+ return EINVAL;
+ }
+
+ subsidiary = sss_krb5_cc_file_path(location);
+ if (!subsidiary) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot get subsidiary cache from %s\n",
+ location));
+ return EINVAL;
+ }
+
+ return cc_file_remove(subsidiary);
+}
+
+struct sss_krb5_cc_be dir_cc = {
+ .type = SSS_KRB5_TYPE_DIR,
+ .create = cc_dir_create,
+ .check_existing = cc_dir_check_existing,
+ .ccache_for_princ = cc_dir_cache_for_princ,
+ .remove = cc_dir_remove
+};
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
index 87bba2bae..5f677cae2 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -53,10 +53,17 @@ struct sss_krb5_cc_be {
};
struct sss_krb5_cc_be file_cc;
+struct sss_krb5_cc_be dir_cc;
+
+errno_t create_ccache_dir(const char *dirname, pcre *illegal_re,
+ uid_t uid, gid_t gid, bool private_path);
errno_t cc_file_create(const char *filename, pcre *illegal_re,
uid_t uid, gid_t gid, bool private_path);
+errno_t cc_dir_create(const char *location, pcre *illegal_re,
+ uid_t uid, gid_t gid, bool private_path);
+
struct sss_krb5_cc_be *get_cc_be_ops(enum sss_krb5_cc_type type);
struct sss_krb5_cc_be *get_cc_be_ops_ccache(const char *ccache);
diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c
index 2ca655310..015bd39ae 100644
--- a/src/tests/krb5_child-test.c
+++ b/src/tests/krb5_child-test.c
@@ -40,6 +40,7 @@
extern struct dp_option default_krb5_opts[];
extern struct sss_krb5_cc_be file_cc;
+extern struct sss_krb5_cc_be dir_cc;
static krb5_context krb5_error_ctx;
#define KRB5_DEBUG(level, krb5_error) do { \
@@ -119,8 +120,6 @@ create_dummy_krb5_ctx(TALLOC_CTX *mem_ctx, const char *realm)
krb5_ctx = talloc_zero(mem_ctx, struct krb5_ctx);
if (!krb5_ctx) return NULL;
- krb5_ctx->cc_be = &file_cc;
-
krb5_ctx->illegal_path_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
&errval, &errstr, &errpos, NULL);
if (krb5_ctx->illegal_path_re == NULL) {
@@ -202,10 +201,12 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
const char *ccname, const char *ccname_template,
int timeout)
{
+ enum sss_krb5_cc_type cc_be;
struct krb5child_req *kr;
struct passwd *pwd;
bool private;
errno_t ret;
+ const char *tmpl;
/* The top level child request */
kr = talloc_zero(mem_ctx, struct krb5child_req);
@@ -237,6 +238,9 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL,
ccname_template);
if (ret != EOK) goto fail;
+ tmpl = ccname_template;
+ } else {
+ tmpl = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL);
}
if (timeout) {
@@ -252,18 +256,42 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
DEBUG(SSSDBG_FUNC_DATA, ("ccname [%s] uid [%llu] gid [%llu]\n",
kr->ccname, kr->uid, kr->gid));
-
- ret = kr->krb5_ctx->cc_be->create(kr->ccname,
- kr->krb5_ctx->illegal_path_re,
- kr->uid, kr->gid, private);
- if (ret != EOK) {
- DEBUG(SSSDBG_OP_FAILURE, ("create_ccache_dir failed.\n"));
- }
} else {
kr->ccname = talloc_strdup(kr, ccname);
}
if (!kr->ccname) goto fail;
+ cc_be = sss_krb5_get_type(kr->ccname);
+ switch (cc_be) {
+ case SSS_KRB5_TYPE_FILE:
+ kr->krb5_ctx->cc_be = &file_cc;
+ break;
+ case SSS_KRB5_TYPE_DIR:
+ kr->krb5_ctx->cc_be = &dir_cc;
+ break;
+ default:
+ if (tmpl[0] != '/') {
+ DEBUG(SSSDBG_OP_FAILURE, ("Unkown ccname database\n"));
+ ret = EINVAL;
+ goto fail;
+ }
+ DEBUG(SSSDBG_CONF_SETTINGS, ("The ccname template was "
+ "missing an explicit type, but looks like an absolute "
+ "path specifier. Assuming FILE:\n"));
+ kr->krb5_ctx->cc_be = &file_cc;
+ break;
+ }
+ DEBUG(SSSDBG_FUNC_DATA, ("ccname [%s] uid [%llu] gid [%llu]\n",
+ kr->ccname, kr->uid, kr->gid));
+
+ ret = kr->krb5_ctx->cc_be->create(kr->ccname,
+ kr->krb5_ctx->illegal_path_re,
+ kr->uid, kr->gid, private);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, ("create_ccache_dir failed.\n"));
+ goto fail;
+ }
+
return kr;
fail:
diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c
index 4a572488f..bcd9acb19 100644
--- a/src/tests/krb5_utils-tests.c
+++ b/src/tests/krb5_utils-tests.c
@@ -329,34 +329,83 @@ START_TEST(test_illegal_patterns)
filename = talloc_asprintf(tmp_ctx, "abc/./ccfile");
fail_unless(filename != NULL, "talloc_asprintf failed.");
- ret = cc_file_create(filename, illegal_re, uid, gid, true);
- fail_unless(ret == EINVAL, "cc_file_create allowed relative path [%s].",
+ ret = create_ccache_dir(filename, illegal_re, uid, gid, true);
+ fail_unless(ret == EINVAL, "create_ccache_dir allowed relative path [%s].",
filename);
filename = talloc_asprintf(tmp_ctx, "%s/abc/./ccfile", dirname);
fail_unless(filename != NULL, "talloc_asprintf failed.");
- ret = cc_file_create(filename, illegal_re, uid, gid, true);
- fail_unless(ret == EINVAL, "cc_file_create allowed "
+ ret = create_ccache_dir(filename, illegal_re, uid, gid, true);
+ fail_unless(ret == EINVAL, "create_ccache_dir allowed "
"illegal pattern '/./' in filename [%s].",
filename);
filename = talloc_asprintf(tmp_ctx, "%s/abc/../ccfile", dirname);
fail_unless(filename != NULL, "talloc_asprintf failed.");
- ret = cc_file_create(filename, illegal_re, uid, gid, true);
- fail_unless(ret == EINVAL, "cc_file_create allowed "
+ ret = create_ccache_dir(filename, illegal_re, uid, gid, true);
+ fail_unless(ret == EINVAL, "create_ccache_dir allowed "
"illegal pattern '/../' in filename [%s].",
filename);
filename = talloc_asprintf(tmp_ctx, "%s/abc//ccfile", dirname);
fail_unless(filename != NULL, "talloc_asprintf failed.");
- ret = cc_file_create(filename, illegal_re, uid, gid, true);
- fail_unless(ret == EINVAL, "cc_file_create allowed "
+ ret = create_ccache_dir(filename, illegal_re, uid, gid, true);
+ fail_unless(ret == EINVAL, "create_ccache_dir allowed "
"illegal pattern '//' in filename [%s].",
filename);
}
END_TEST
+START_TEST(test_cc_dir_create)
+{
+ char *residual;
+ char *dirname;
+ char *cwd;
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+ pcre *illegal_re;
+ errno_t ret;
+ const char *errstr;
+ int errval;
+ int errpos;
+
+ illegal_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
+ &errval, &errstr, &errpos, NULL);
+ fail_unless(illegal_re != NULL, "Invalid Regular Expression pattern at "
+ " position %d. (Error: %d [%s])\n",
+ errpos, errval, errstr);
+
+ cwd = getcwd(NULL, 0);
+ fail_unless(cwd != NULL, "getcwd failed.");
+
+ dirname = talloc_asprintf(tmp_ctx, "%s/%s/user_dir",
+ cwd, TESTS_PATH);
+ fail_unless(dirname != NULL, "talloc_asprintf failed.");
+ residual = talloc_asprintf(tmp_ctx, "DIR:%s/%s", dirname, "ccdir");
+ fail_unless(residual != NULL, "talloc_asprintf failed.");
+
+ ret = cc_dir_create(residual, illegal_re, uid, gid, true);
+ fail_unless(ret == EOK, "cc_dir_create failed\n");
+ ret = rmdir(dirname);
+ fail_unless(ret == 0, "Cannot remove %s: %s\n", dirname, strerror(ret));
+ talloc_free(residual);
+
+ dirname = talloc_asprintf(tmp_ctx, "%s/%s/user_dir2",
+ cwd, TESTS_PATH);
+ fail_unless(dirname != NULL, "talloc_asprintf failed.");
+ residual = talloc_asprintf(tmp_ctx, "DIR:%s/%s", dirname, "ccdir/");
+ fail_unless(residual != NULL, "talloc_asprintf failed.");
+
+ ret = cc_dir_create(residual, illegal_re, uid, gid, true);
+ fail_unless(ret == EOK, "cc_dir_create failed\n");
+ ret = rmdir(dirname);
+ fail_unless(ret == 0, "Cannot remove %s: %s\n", dirname, strerror(ret));
+ talloc_free(residual);
+ free(cwd);
+}
+END_TEST
+
void setup_talloc_context(void)
{
int ret;
@@ -645,6 +694,7 @@ Suite *krb5_utils_suite (void)
tcase_add_checked_fixture (tc_create_dir, setup_create_dir,
teardown_create_dir);
tcase_add_test (tc_create_dir, test_illegal_patterns);
+ tcase_add_test (tc_create_dir, test_cc_dir_create);
if (getuid() == 0) {
tcase_add_test (tc_create_dir, test_priv_ccache_dir);
tcase_add_test (tc_create_dir, test_private_ccache_dir_in_user_dir);
diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
index 5bc1140d0..8a6bfe2dc 100644
--- a/src/util/sss_krb5.c
+++ b/src/util/sss_krb5.c
@@ -604,11 +604,10 @@ void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name)
}
-krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm,
+krb5_error_code check_for_valid_tgt(krb5_context context,
+ krb5_ccache ccache, const char *realm,
const char *client_princ_str, bool *result)
{
- krb5_context context = NULL;
- krb5_ccache ccache = NULL;
krb5_error_code krberr;
TALLOC_CTX *tmp_ctx = NULL;
krb5_creds mcred;
@@ -625,18 +624,6 @@ krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm,
return ENOMEM;
}
- krberr = krb5_init_context(&context);
- if (krberr) {
- DEBUG(1, ("Failed to init kerberos context\n"));
- goto done;
- }
-
- krberr = krb5_cc_resolve(context, ccname, &ccache);
- if (krberr != 0) {
- DEBUG(1, ("krb5_cc_resolve failed.\n"));
- goto done;
- }
-
server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm);
if (server_name == NULL) {
DEBUG(1, ("talloc_asprintf failed.\n"));
@@ -685,10 +672,6 @@ done:
if (server_principal != NULL) {
krb5_free_principal(context, server_principal);
}
- if (ccache != NULL) {
- krb5_cc_close(context, ccache);
- }
- if (context != NULL) krb5_free_context(context);
talloc_free(tmp_ctx);
return krberr;
}
diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
index bba2a7eda..89ec00021 100644
--- a/src/util/sss_krb5.h
+++ b/src/util/sss_krb5.h
@@ -55,7 +55,8 @@ void KRB5_CALLCONV sss_krb5_get_init_creds_opt_free (krb5_context context,
void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name);
-krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm,
+krb5_error_code check_for_valid_tgt(krb5_context context,
+ krb5_ccache ccache, const char *realm,
const char *client_princ_str, bool *result);
int sss_krb5_verify_keytab(const char *principal,