summaryrefslogtreecommitdiff
path: root/src/providers
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2010-02-23 09:43:51 +0100
committerStephen Gallagher <sgallagh@redhat.com>2010-03-11 09:22:41 -0500
commit29752834fbf3a19e4e117668abfce4e4c7c48ee4 (patch)
treea39de5ce10404f2c8397a587db535c6ae795a8c7 /src/providers
parent3ea37e96974387d57593dfb1010ee6974c7d9e1e (diff)
downloadsssd-29752834fbf3a19e4e117668abfce4e4c7c48ee4.tar.gz
sssd-29752834fbf3a19e4e117668abfce4e4c7c48ee4.tar.bz2
sssd-29752834fbf3a19e4e117668abfce4e4c7c48ee4.zip
Add expandable sequences to krb5_ccachedir
As with krb5_ccname_template sequences like %u can be used in the krb5_ccachedir parameter which are expanded at runtime. If the directory does not exist, it will be created. Depending on the used sequences it is created as a public or private directory.
Diffstat (limited to 'src/providers')
-rw-r--r--src/providers/krb5/krb5_auth.c13
-rw-r--r--src/providers/krb5/krb5_auth.h6
-rw-r--r--src/providers/krb5/krb5_common.c13
-rw-r--r--src/providers/krb5/krb5_init.c12
-rw-r--r--src/providers/krb5/krb5_utils.c263
-rw-r--r--src/providers/krb5/krb5_utils.h6
6 files changed, 292 insertions, 21 deletions
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 5be42a81..b8b498a0 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -881,6 +881,7 @@ static void krb5_resolve_done(struct tevent_req *req)
struct be_req *be_req = kr->req;
char *msg;
size_t offset = 0;
+ bool private_path = false;
ret = be_resolve_server_recv(req, &kr->srv);
talloc_zfree(req);
@@ -916,12 +917,20 @@ static void krb5_resolve_done(struct tevent_req *req)
}
kr->ccname = expand_ccname_template(kr, kr,
dp_opt_get_cstring(kr->krb5_ctx->opts,
- KRB5_CCNAME_TMPL)
- );
+ KRB5_CCNAME_TMPL),
+ true, &private_path);
if (kr->ccname == NULL) {
DEBUG(1, ("expand_ccname_template failed.\n"));
goto done;
}
+
+ ret = create_ccache_dir(kr, kr->ccname,
+ kr->krb5_ctx->illegal_path_re,
+ kr->uid, kr->gid, private_path);
+ if (ret != EOK) {
+ DEBUG(1, ("create_ccache_dir failed.\n"));
+ goto done;
+ }
}
if (be_is_offline(be_req->be_ctx)) {
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 3e11f270..825f3d64 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -26,6 +26,8 @@
#ifndef __KRB5_AUTH_H__
#define __KRB5_AUTH_H__
+#include <pcre.h>
+
#include "util/sss_krb5.h"
#include "providers/dp_backend.h"
#include "providers/krb5/krb5_common.h"
@@ -33,6 +35,8 @@
#define CCACHE_ENV_NAME "KRB5CCNAME"
#define SSSD_KRB5_CHANGEPW_PRINCIPLE "SSSD_KRB5_CHANGEPW_PRINCIPLE"
+#define ILLEGAL_PATH_PATTERN "//|/\\./|/\\.\\./"
+
typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
struct krb5child_req {
@@ -87,6 +91,8 @@ struct krb5_ctx {
struct dp_option *opts;
struct krb5_service *service;
int child_debug_fd;
+
+ pcre *illegal_path_re;
};
void krb5_pam_handler(struct be_req *be_req);
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 86676f44..7619e6a5 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -47,7 +47,6 @@ errno_t check_and_export_options(struct dp_option *opts,
char *value;
const char *realm;
const char *dummy;
- struct stat stat_buf;
char **list;
realm = dp_opt_get_cstring(opts, KRB5_REALM);
@@ -83,18 +82,6 @@ errno_t check_and_export_options(struct dp_option *opts,
talloc_free(list);
}
- dummy = dp_opt_get_cstring(opts, KRB5_CCACHEDIR);
- ret = lstat(dummy, &stat_buf);
- if (ret != EOK) {
- DEBUG(1, ("lstat for [%s] failed: [%d][%s].\n", dummy, errno,
- strerror(errno)));
- return ret;
- }
- if ( !S_ISDIR(stat_buf.st_mode) ) {
- DEBUG(1, ("Value of krb5ccache_dir [%s] is not a directory.\n", dummy));
- return EINVAL;
- }
-
dummy = dp_opt_get_cstring(opts, KRB5_CCNAME_TMPL);
if (dummy == NULL) {
DEBUG(1, ("Missing credential cache name template.\n"));
diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c
index 43cbc1bc..4d212381 100644
--- a/src/providers/krb5/krb5_init.c
+++ b/src/providers/krb5/krb5_init.c
@@ -53,6 +53,9 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
FILE *debug_filep;
const char *krb5_servers;
const char *krb5_realm;
+ const char *errstr;
+ int errval;
+ int errpos;
if (krb5_options == NULL) {
krb5_options = talloc_zero(bectx, struct krb5_options);
@@ -135,6 +138,15 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
fcntl(ctx->child_debug_fd, F_SETFD, v & ~FD_CLOEXEC);
}
+ ctx->illegal_path_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
+ &errval, &errstr, &errpos, NULL);
+ if (ctx->illegal_path_re == NULL) {
+ DEBUG(1, ("Invalid Regular Expression pattern at position %d. "
+ "(Error: %d [%s])\n", errpos, errval, errstr));
+ ret = EFAULT;
+ goto fail;
+ }
+
*ops = &krb5_auth_ops;
*pvt_auth_data = ctx;
return EOK;
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index a75ad782..83971abf 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -29,13 +29,17 @@
#include "util/util.h"
char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
- const char *template)
+ const char *template, bool file_mode,
+ bool *private_path)
{
char *copy;
char *p;
char *n;
char *result = NULL;
const char *dummy;
+ const char *cache_dir_tmpl;
+
+ *private_path = false;
if (template == NULL) {
DEBUG(1, ("Missing template.\n"));
@@ -72,6 +76,7 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
}
result = talloc_asprintf_append(result, "%s%s", p,
kr->pd->user);
+ if (!file_mode) *private_path = true;
break;
case 'U':
if (kr->uid <= 0) {
@@ -81,6 +86,7 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
}
result = talloc_asprintf_append(result, "%s%d", p,
kr->uid);
+ if (!file_mode) *private_path = true;
break;
case 'p':
if (kr->upn == NULL) {
@@ -89,6 +95,7 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
return NULL;
}
result = talloc_asprintf_append(result, "%s%s", p, kr->upn);
+ if (!file_mode) *private_path = true;
break;
case '%':
result = talloc_asprintf_append(result, "%s%%", p);
@@ -108,16 +115,35 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
return NULL;
}
result = talloc_asprintf_append(result, "%s%s", p, kr->homedir);
+ if (!file_mode) *private_path = true;
break;
case 'd':
- dummy = dp_opt_get_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR);
- if (dummy == NULL) {
- DEBUG(1, ("Missing credential cache directory.\n"));
+ if (file_mode) {
+ cache_dir_tmpl = dp_opt_get_string(kr->krb5_ctx->opts,
+ KRB5_CCACHEDIR);
+ if (cache_dir_tmpl == NULL) {
+ DEBUG(1, ("Missing credential cache directory.\n"));
+ return NULL;
+ }
+
+ dummy = expand_ccname_template(mem_ctx, kr, cache_dir_tmpl,
+ false, private_path);
+ if (dummy == NULL) {
+ DEBUG(1, ("Expanding credential cache directory "
+ "template failed.\n"));
+ return NULL;
+ }
+ result = talloc_asprintf_append(result, "%s%s", p, dummy);
+ } else {
+ DEBUG(1, ("'%%d' is not allowed in this template.\n"));
return NULL;
}
- result = talloc_asprintf_append(result, "%s%s", p, dummy);
break;
case 'P':
+ if (!file_mode) {
+ DEBUG(1, ("'%%P' is not allowed in this template.\n"));
+ return NULL;
+ }
if (kr->pd->cli_pid == 0) {
DEBUG(1, ("Cannot expand PID template "
"because PID is not available.\n"));
@@ -143,3 +169,230 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
return result;
}
+
+static errno_t check_parent_stat(bool private_path, struct stat *parent_stat,
+ uid_t uid, gid_t gid)
+{
+ if (private_path) {
+ if (!((parent_stat->st_uid == 0 && parent_stat->st_gid == 0) ||
+ parent_stat->st_uid == uid)) {
+ DEBUG(1, ("Private directory can only be created below a "
+ "directory belonging to root or to [%d][%d].\n",
+ uid, gid));
+ return EINVAL;
+ }
+
+ if (parent_stat->st_uid == uid) {
+ if (!(parent_stat->st_mode & S_IXUSR)) {
+ DEBUG(1, ("Parent directory does have the search bit set for "
+ "the owner.\n"));
+ return EINVAL;
+ }
+ } else {
+ if (!(parent_stat->st_mode & S_IXOTH)) {
+ DEBUG(1, ("Parent directory does have the search bit set for "
+ "others.\n"));
+ return EINVAL;
+ }
+ }
+ } else {
+ if (parent_stat->st_uid != 0 || parent_stat->st_gid != 0) {
+ DEBUG(1, ("Public directory cannot be created below a user "
+ "directory.\n"));
+ return EINVAL;
+ }
+
+ if (!(parent_stat->st_mode & S_IXOTH)) {
+ DEBUG(1, ("Parent directory does have the search bit set for "
+ "others.\n"));
+ return EINVAL;
+ }
+ }
+
+ return EOK;
+}
+
+struct string_list {
+ struct string_list *next;
+ struct string_list *prev;
+ char *s;
+};
+
+static errno_t find_ccdir_parent_data(TALLOC_CTX *mem_ctx, const char *dirname,
+ struct stat *parent_stat,
+ struct string_list **missing_parents)
+{
+ int ret = EFAULT;
+ char *parent = NULL;
+ char *end;
+ struct string_list *li;
+
+ ret = stat(dirname, parent_stat);
+ if (ret == EOK) {
+ if ( !S_ISDIR(parent_stat->st_mode) ) {
+ DEBUG(1, ("[%s] is not a directory.\n", dirname));
+ return EINVAL;
+ }
+ return EOK;
+ } else {
+ if (errno != ENOENT) {
+ ret = errno;
+ DEBUG(1, ("stat for [%s] failed: [%d][%s].\n", dirname, ret,
+ strerror(ret)));
+ return ret;
+ }
+ }
+
+ li = talloc_zero(mem_ctx, struct string_list);
+ if (li == NULL) {
+ DEBUG(1, ("talloc_zero failed.\n"));
+ return ENOMEM;
+ }
+
+ li->s = talloc_strdup(li, dirname);
+ if (li->s == NULL) {
+ DEBUG(1, ("talloc_strdup failed.\n"));
+ return ENOMEM;
+ }
+
+ DLIST_ADD(*missing_parents, li);
+
+ parent = talloc_strdup(mem_ctx, dirname);
+ if (parent == NULL) {
+ DEBUG(1, ("talloc_strdup failed.\n"));
+ return ENOMEM;
+ }
+ end = strrchr(parent, '/');
+ if (end == NULL || end == parent) {
+ DEBUG(1, ("Cannot find parent directory of [%s], / is not allowed.\n",
+ dirname));
+ ret = EINVAL;
+ goto done;
+ }
+ *end = '\0';
+
+ ret = find_ccdir_parent_data(mem_ctx, parent, parent_stat, missing_parents);
+
+done:
+ talloc_free(parent);
+ return ret;
+}
+
+errno_t create_ccache_dir(TALLOC_CTX *mem_ctx, const char *filename,
+ pcre *illegal_re, uid_t uid, gid_t gid,
+ bool private_path)
+{
+ int ret = EFAULT;
+ char *dirname;
+ char *end;
+ struct stat parent_stat;
+ struct string_list *missing_parents = NULL;
+ struct string_list *li = NULL;
+ mode_t old_umask;
+ mode_t new_dir_mode;
+ size_t offset = 0;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ DEBUG(1, ("talloc_new failed.\n"));
+ return ENOMEM;
+ }
+
+ if (strncmp(filename, "FILE:", 5) == 0) {
+ offset = 5;
+ }
+
+ dirname = talloc_strdup(tmp_ctx, filename + offset);
+ if (dirname == NULL) {
+ DEBUG(1, ("talloc_strndup failed.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (*dirname != '/') {
+ DEBUG(1, ("Only absolute paths are allowed, not [%s] .\n", dirname));
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (illegal_re != NULL) {
+ ret = pcre_exec(illegal_re, NULL, dirname, strlen(dirname),
+ 0, 0, NULL, 0);
+ if (ret == 0) {
+ DEBUG(1, ("Illegal pattern in ccache directory name [%s].\n",
+ dirname));
+ ret = EINVAL;
+ goto done;
+ } else if ( ret == PCRE_ERROR_NOMATCH) {
+ DEBUG(9, ("Ccache directory name [%s] does not contain "
+ "illegal patterns.\n", dirname));
+ } else {
+ DEBUG(1, ("pcre_exec failed [%d].\n", ret));
+ ret = EFAULT;
+ goto done;
+ }
+ }
+
+ end = strrchr(dirname, '/');
+ if (end == NULL || end == dirname) {
+ DEBUG(1, ("Missing filename in [%s].\n", dirname));
+ ret = EINVAL;
+ goto done;
+ }
+ *end = '\0';
+
+ ret = find_ccdir_parent_data(tmp_ctx, dirname, &parent_stat,
+ &missing_parents);
+ if (ret != EOK) {
+ DEBUG(1, ("find_ccdir_parent_data failed.\n"));
+ goto done;
+ }
+
+ ret = check_parent_stat(private_path, &parent_stat, uid, gid);
+ if (ret != EOK) {
+ DEBUG(1, ("check_parent_stat failed for %s directory [%s].\n",
+ private_path ? "private" : "public", dirname));
+ goto done;
+ }
+
+ DLIST_FOR_EACH(li, missing_parents) {
+ DEBUG(9, ("Creating directory [%s].\n", li->s));
+ if (li->next == NULL) {
+ new_dir_mode = private_path ? 0700 : 01777;
+ } else {
+ if (private_path &&
+ parent_stat.st_uid == uid && parent_stat.st_gid == gid) {
+ new_dir_mode = 0700;
+ } else {
+ new_dir_mode = 0755;
+ }
+ }
+
+ old_umask = umask(0000);
+ ret = mkdir(li->s, new_dir_mode);
+ umask(old_umask);
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(1, ("mkdir [%s] failed: [%d][%s].\n", li->s, ret,
+ strerror(ret)));
+ goto done;
+ }
+ if (private_path &&
+ ((parent_stat.st_uid == uid && parent_stat.st_gid == gid) ||
+ li->next == NULL)) {
+ ret = chown(li->s, uid, gid);
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(1, ("chown failed [%d][%s].\n", ret, strerror(ret)));
+ goto done;
+ }
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
index 7637041a..be162bc7 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -32,8 +32,12 @@
#include "providers/data_provider.h"
char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
- const char *template);
+ const char *template, bool file_mode,
+ bool *private_path);
errno_t become_user(uid_t uid, gid_t gid);
+errno_t create_ccache_dir(TALLOC_CTX *mem_ctx, const char *filename,
+ pcre *illegal_re, uid_t uid, gid_t gid,
+ bool private_path);
#endif /* __KRB5_UTILS_H__ */