diff options
author | Sumit Bose <sbose@redhat.com> | 2009-11-09 21:54:06 +0100 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2009-11-20 11:18:51 -0500 |
commit | b9fda1a01873eab1887c53bdbc2f76d75d665681 (patch) | |
tree | d5bed1a691abda57eff8bd8bc92c6ce0a07bb557 | |
parent | 55ab3a9b2dcbe809dece953605ab359c5e12a139 (diff) | |
download | sssd-b9fda1a01873eab1887c53bdbc2f76d75d665681.tar.gz sssd-b9fda1a01873eab1887c53bdbc2f76d75d665681.tar.bz2 sssd-b9fda1a01873eab1887c53bdbc2f76d75d665681.zip |
Improve handling of ccache files
- save current ccache file to sysdb
- use the saved ccache file if the user has running processes
- create an empty ccache if offline
- return enviroment variables if offline
-rw-r--r-- | server/Makefile.am | 30 | ||||
-rw-r--r-- | server/db/sysdb.h | 1 | ||||
-rw-r--r-- | server/providers/krb5/krb5_auth.c | 546 | ||||
-rw-r--r-- | server/providers/krb5/krb5_auth.h | 3 | ||||
-rw-r--r-- | server/providers/krb5/krb5_child.c | 250 | ||||
-rw-r--r-- | server/tests/find_uid-tests.c | 124 | ||||
-rw-r--r-- | server/util/find_uid.c | 297 | ||||
-rw-r--r-- | server/util/find_uid.h | 36 |
8 files changed, 1081 insertions, 206 deletions
diff --git a/server/Makefile.am b/server/Makefile.am index d939f524..fc2ac94d 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -68,10 +68,11 @@ if HAVE_CHECK resolv-tests \ krb5-utils-tests \ check_and_open-tests \ - ipa_timerules-tests \ + ipa_timerules-tests \ files-tests \ refcount-tests \ - fail_over-tests + fail_over-tests \ + find_uid-tests endif check_PROGRAMS = \ @@ -133,7 +134,7 @@ INI_CFG_LIBS = \ DHASH_CFLAGS = \ -I$(srcdir)/../common/dhash DHASH_LIBS = \ - -L$(builddir)/../common/dhash/.libs/ \ + -L$(builddir)/../common/dhash/ \ -ldhash AM_CPPFLAGS = -Wall \ @@ -267,6 +268,7 @@ dist_noinst_HEADERS = \ util/sss_ldap.h \ util/sss_krb5.h \ util/refcount.h \ + util/find_uid.h \ config.h \ monitor/monitor.h \ monitor/monitor_interfaces.h \ @@ -518,6 +520,20 @@ ipa_timerules_tests_LDADD = \ $(TALLOC_LIBS) \ $(CHECK_LIBS) +find_uid_tests_SOURCES = \ + tests/find_uid-tests.c \ + util/find_uid.c \ + $(SSSD_DEBUG_OBJ) +find_uid_tests_CFLAGS = \ + $(AM_CFLAGS) \ + $(TALLOC_CFLAGS) \ + $(DHASH_CFLAGS) \ + $(CHECK_CFLAGS) +find_uid_tests_LDADD = \ + $(TALLOC_LIBS) \ + $(DHASH_LIBS) \ + $(CHECK_LIBS) + endif stress_tests_SOURCES = \ @@ -564,6 +580,7 @@ libsss_proxy_la_LDFLAGS = \ -module libsss_krb5_la_SOURCES = \ + util/find_uid.c \ providers/krb5/krb5_utils.c \ providers/krb5/krb5_become_user.c \ providers/krb5/krb5_auth.c \ @@ -571,7 +588,9 @@ libsss_krb5_la_SOURCES = \ providers/krb5/krb5_init.c libsss_krb5_la_CFLAGS = \ $(AM_CFLAGS) \ - $(KRB5_CFLAGS) + $(DHASH_CFLAGS) +libsss_krb5_la_LIBADD = \ + $(DHASH_LIBS) libsss_krb5_la_LDFLAGS = \ -version-info 1:0:0 \ -module @@ -592,6 +611,7 @@ libsss_ipa_la_SOURCES = \ providers/ldap/sdap.c \ util/sss_ldap.c \ util/sss_krb5.c \ + util/find_uid.c \ providers/krb5/krb5_utils.c \ providers/krb5/krb5_become_user.c \ providers/krb5/krb5_common.c \ @@ -599,9 +619,11 @@ libsss_ipa_la_SOURCES = \ libsss_ipa_la_CFLAGS = \ $(AM_CFLAGS) \ $(LDAP_CFLAGS) \ + $(DHASH_CFLAGS) \ $(KRB5_CFLAGS) libsss_ipa_la_LIBADD = \ $(OPENLDAP_LIBS) \ + $(DHASH_LIBS) \ $(KRB5_LIBS) libsss_ipa_la_LDFLAGS = \ -version-info 1:0:0 \ diff --git a/server/db/sysdb.h b/server/db/sysdb.h index a3299859..f94b43fd 100644 --- a/server/db/sysdb.h +++ b/server/db/sysdb.h @@ -72,6 +72,7 @@ #define SYSDB_UUID "uniqueID" #define SYSDB_UPN "userPrincipalName" +#define SYSDB_CCACHE_FILE "ccacheFile" #define SYSDB_ORIG_DN "originalDN" #define SYSDB_ORIG_MODSTAMP "originalModifyTimestamp" diff --git a/server/providers/krb5/krb5_auth.c b/server/providers/krb5/krb5_auth.c index d3e05e12..8068bce9 100644 --- a/server/providers/krb5/krb5_auth.c +++ b/server/providers/krb5/krb5_auth.c @@ -29,10 +29,12 @@ #include <sys/wait.h> #include <fcntl.h> #include <pwd.h> +#include <sys/stat.h> #include <security/pam_modules.h> #include "util/util.h" +#include "util/find_uid.h" #include "db/sysdb.h" #include "providers/krb5/krb5_auth.h" #include "providers/krb5/krb5_utils.h" @@ -48,6 +50,262 @@ struct io_buffer { size_t size; }; +static errno_t add_krb5_env(struct dp_option *opts, const char *ccname, + struct pam_data *pd) +{ + int ret; + const char *dummy; + char *env; + TALLOC_CTX *tmp_ctx = NULL; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(1, ("talloc_new failed.\n")); + return ENOMEM; + } + + if (ccname != NULL) { + env = talloc_asprintf(tmp_ctx, "%s=%s",CCACHE_ENV_NAME, ccname); + if (env == NULL) { + DEBUG(1, ("talloc_asprintf failed.\n")); + ret = ENOMEM; + goto done; + } + ret = pam_add_response(pd, PAM_ENV_ITEM, strlen(env)+1, + (uint8_t *) env); + if (ret != EOK) { + DEBUG(1, ("pam_add_response failed.\n")); + goto done; + } + } + + dummy = dp_opt_get_cstring(opts, KRB5_REALM); + if (dummy != NULL) { + env = talloc_asprintf(tmp_ctx, "%s=%s", SSSD_KRB5_REALM, dummy); + if (env == NULL) { + DEBUG(1, ("talloc_asprintf failed.\n")); + ret = ENOMEM; + goto done; + } + ret = pam_add_response(pd, PAM_ENV_ITEM, strlen(env)+1, + (uint8_t *) env); + if (ret != EOK) { + DEBUG(1, ("pam_add_response failed.\n")); + goto done; + } + } + + dummy = dp_opt_get_cstring(opts, KRB5_KDC); + if (dummy != NULL) { + env = talloc_asprintf(tmp_ctx, "%s=%s", SSSD_KRB5_KDC, dummy); + if (env == NULL) { + DEBUG(1, ("talloc_asprintf failed.\n")); + ret = ENOMEM; + goto done; + } + ret = pam_add_response(pd, PAM_ENV_ITEM, strlen(env)+1, + (uint8_t *) env); + if (ret != EOK) { + DEBUG(1, ("pam_add_response failed.\n")); + goto done; + } + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t check_if_ccache_file_is_used(uid_t uid, const char **ccname) +{ + int ret; + size_t offset = 0; + struct stat stat_buf; + const char *filename; + bool active; + + if (ccname == NULL || *ccname == NULL) { + return EINVAL; + } + + if (strncmp(*ccname, "FILE:", 5) == 0) { + offset = 5; + } + + filename = *ccname + offset; + + if (filename[0] != '/') { + DEBUG(1, ("Only absolute path names are allowed")); + return EINVAL; + } + + ret = lstat(filename, &stat_buf); + + if (ret == -1 && errno != ENOENT) { + DEBUG(1, ("stat failed [%d][%s].\n", errno, strerror(errno))); + return errno; + } else if (ret == -1 && errno == ENOENT) { + *ccname = NULL; + return EOK; + } + + if (stat_buf.st_uid != uid) { + DEBUG(1, ("Cache file [%s] exists, but is owned by [%d] instead of " + "[%d].\n", filename, stat_buf.st_uid, uid)); + return EINVAL; + } + + if (!S_ISREG(stat_buf.st_mode)) { + DEBUG(1, ("Cache file [%s] exists, but is not a regular file.\n", + filename)); + return EINVAL; + } + + ret = check_if_uid_is_active(uid, &active); + if (ret != EOK) { + DEBUG(1, ("check_if_uid_is_active failed.\n")); + return ret; + } + + if (!active) { + DEBUG(5, ("User [%d] is not active, deleting old ccache file [%s].\n", + uid, filename)); + ret = unlink(filename); + if (ret == -1) { + DEBUG(1, ("unlink failed [%d][%s].\n", errno, strerror(errno))); + return errno; + } + *ccname = NULL; + } else { + DEBUG(9, ("User [%d] is still active, reusing ccache file [%s].\n", + uid, filename)); + } + return EOK; +} + +struct krb5_save_ccname_state { + struct tevent_context *ev; + struct sysdb_ctx *sysdb; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + const char *name; + struct sysdb_attrs *attrs; +}; + +static void krb5_save_ccname_trans(struct tevent_req *subreq); +static void krb5_set_user_attr_done(struct tevent_req *subreq); + +static struct tevent_req *krb5_save_ccname_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *name, + const char *ccname) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct krb5_save_ccname_state *state; + int ret; + + if (name == NULL || ccname == NULL) { + DEBUG(1, ("Missing user or ccache name.\n")); + return NULL; + } + + req = tevent_req_create(mem_ctx, &state, struct krb5_save_ccname_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->sysdb = sysdb; + state->handle = NULL; + state->domain = domain; + state->name = name; + + state->attrs = sysdb_new_attrs(state); + ret = sysdb_attrs_add_string(state->attrs, SYSDB_CCACHE_FILE, ccname); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_add_string failed.\n")); + goto failed; + } + + subreq = sysdb_transaction_send(state, ev, sysdb); + if (subreq == NULL) { + goto failed; + } + tevent_req_set_callback(subreq, krb5_save_ccname_trans, req); + + return req; + +failed: + talloc_free(req); + return NULL; +} + +static void krb5_save_ccname_trans(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct krb5_save_ccname_state *state = tevent_req_data(req, + struct krb5_save_ccname_state); + int ret; + + ret = sysdb_transaction_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, + state->domain, state->name, + state->attrs, SYSDB_MOD_REP); + if (subreq == NULL) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, krb5_set_user_attr_done, req); +} + +static void krb5_set_user_attr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct krb5_save_ccname_state *state = tevent_req_data(req, + struct krb5_save_ccname_state); + int ret; + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_transaction_commit_send(state, state->ev, state->handle); + if (subreq == NULL) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_transaction_complete, req); + return; +} + +int krb5_save_ccname_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf) { struct io_buffer *buf; @@ -245,8 +503,7 @@ static int krb5_cleanup(void *ptr) return EOK; } -static errno_t krb5_setup(struct be_req *req, struct krb5child_req **krb5_req, - const char *homedir) +static errno_t krb5_setup(struct be_req *req, struct krb5child_req **krb5_req) { struct krb5child_req *kr = NULL; struct krb5_ctx *krb5_ctx; @@ -270,22 +527,12 @@ static errno_t krb5_setup(struct be_req *req, struct krb5child_req **krb5_req, } kr->read_from_child_fd = -1; kr->write_to_child_fd = -1; + kr->is_offline = false; talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup); kr->pd = pd; kr->req = req; kr->krb5_ctx = krb5_ctx; - kr->homedir = homedir; - - kr->ccname = expand_ccname_template(kr, kr, - dp_opt_get_cstring(krb5_ctx->opts, - KRB5_CCNAME_TMPL) - ); - if (kr->ccname == NULL) { - DEBUG(1, ("expand_ccname_template failed.\n")); - err = EINVAL; - goto failed; - } *krb5_req = kr; @@ -434,8 +681,11 @@ static errno_t fork_child(struct krb5child_req *kr) /* We need to keep the root privileges to read the keytab file if * validation is enabled, otherwise we can drop them here and run - * krb5_child with user privileges. */ - if (!dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_VALIDATE)) { + * krb5_child with user privileges. + * If authtok_size is zero we are offline and want to create an empty + * ccache file. In this case we can drop the privileges, too. */ + if (!dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_VALIDATE) || + kr->pd->authtok_size == 0) { ret = become_user(kr->pd->pw_uid, kr->pd->gr_gid); if (ret != EOK) { DEBUG(1, ("become_user failed.\n")); @@ -678,8 +928,9 @@ static int handle_child_recv(struct tevent_req *req, return EOK; } -static void get_user_upn_done(void *pvt, int err, struct ldb_result *res); -static void krb5_pam_handler_done(struct tevent_req *req); +static void get_user_attr_done(void *pvt, int err, struct ldb_result *res); +static void krb5_save_ccname_done(struct tevent_req *req); +static void krb5_child_done(struct tevent_req *req); static void krb5_pam_handler_cache_done(struct tevent_req *treq); void krb5_pam_handler(struct be_req *be_req) @@ -692,13 +943,6 @@ void krb5_pam_handler(struct be_req *be_req) pd = talloc_get_type(be_req->req_data, struct pam_data); - if (be_is_offline(be_req->be_ctx)) { - DEBUG(4, ("Backend is marked offline, retry later!\n")); - pam_status = PAM_AUTHINFO_UNAVAIL; - dp_err = DP_ERR_OFFLINE; - goto done; - } - if (pd->cmd != SSS_PAM_AUTHENTICATE && pd->cmd != SSS_PAM_CHAUTHTOK) { DEBUG(4, ("krb5 does not handles pam task %d.\n", pd->cmd)); pam_status = PAM_SUCCESS; @@ -706,18 +950,19 @@ void krb5_pam_handler(struct be_req *be_req) goto done; } - attrs = talloc_array(be_req, const char *, 3); + attrs = talloc_array(be_req, const char *, 4); if (attrs == NULL) { goto done; } attrs[0] = SYSDB_UPN; attrs[1] = SYSDB_HOMEDIR; - attrs[2] = NULL; + attrs[2] = SYSDB_CCACHE_FILE; + attrs[3] = NULL; ret = sysdb_get_user_attr(be_req, be_req->be_ctx->sysdb, be_req->be_ctx->domain, pd->user, attrs, - get_user_upn_done, be_req); + get_user_attr_done, be_req); if (ret) { goto done; @@ -731,7 +976,7 @@ done: krb_reply(be_req, dp_err, pd->pam_status); } -static void get_user_upn_done(void *pvt, int err, struct ldb_result *res) +static void get_user_attr_done(void *pvt, int err, struct ldb_result *res) { struct be_req *be_req = talloc_get_type(pvt, struct be_req); struct krb5_ctx *krb5_ctx; @@ -740,17 +985,19 @@ 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 *homedir = NULL; + int dp_err = DP_ERR_FATAL; + const char *ccache_file = NULL; const char *dummy; + bool active_ccache_present = false; - pd = talloc_get_type(be_req->req_data, struct pam_data); - krb5_ctx = get_krb5_ctx(be_req); - if (krb5_ctx == NULL) { - DEBUG(1, ("Kerberos context not available.\n")); - err = EINVAL; + ret = krb5_setup(be_req, &kr); + if (ret != EOK) { + DEBUG(1, ("krb5_setup failed.\n")); goto failed; } + pd = kr->pd; + krb5_ctx = kr->krb5_ctx; if (err != LDB_SUCCESS) { DEBUG(5, ("sysdb search for upn of user [%s] failed.\n", pd->user)); @@ -759,7 +1006,8 @@ static void get_user_upn_done(void *pvt, int err, struct ldb_result *res) switch (res->count) { case 0: - DEBUG(5, ("No upn for user [%s] found.\n", pd->user)); + DEBUG(5, ("No attributes for user [%s] found.\n", pd->user)); + goto failed; break; case 1: @@ -776,68 +1024,110 @@ static void get_user_upn_done(void *pvt, int err, struct ldb_result *res) } } - homedir = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_HOMEDIR, - NULL); - if (homedir == NULL) { + if (pd->upn == NULL) { + DEBUG(1, ("Cannot set UPN.\n")); + goto failed; + } + + kr->homedir = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_HOMEDIR, + NULL); + if (kr->homedir == NULL) { DEBUG(4, ("Home directory for user [%s] not known.\n", pd->user)); } + + ccache_file = ldb_msg_find_attr_as_string(res->msgs[0], + SYSDB_CCACHE_FILE, + NULL); + if (ccache_file != NULL) { + ret = check_if_ccache_file_is_used(pd->pw_uid, &ccache_file); + if (ret != EOK) { + DEBUG(1, ("check_if_ccache_file_is_used failed.\n")); + goto failed; + } + } + + if (ccache_file == NULL) { + active_ccache_present = false; + DEBUG(4, ("No active ccache file for user [%s] found.\n", + pd->user)); + ccache_file = expand_ccname_template(kr, kr, + dp_opt_get_cstring(kr->krb5_ctx->opts, + KRB5_CCNAME_TMPL) + ); + if (ccache_file == NULL) { + DEBUG(1, ("expand_ccname_template failed.\n")); + goto failed; + } + } else { + active_ccache_present = true; + } + DEBUG(9, ("Ccache_file is [%s] and %s.\n", ccache_file, + active_ccache_present ? "will be kept/renewed" : + "will be generated")); + kr->ccname = ccache_file; break; default: DEBUG(1, ("A user search by name (%s) returned > 1 results!\n", pd->user)); + goto failed; break; } - if (pd->upn == NULL) { - DEBUG(1, ("Cannot set UPN.\n")); - goto failed; - } - ret = krb5_setup(be_req, &kr, homedir); - if (ret != EOK) { - DEBUG(1, ("krb5_setup failed.\n")); - goto failed; + if (be_is_offline(be_req->be_ctx)) { + DEBUG(9, ("Preparing for offline operation.\n")); + kr->is_offline = true; + memset(pd->authtok, 0, pd->authtok_size); + pd->authtok_size = 0; + + if (active_ccache_present) { + req = krb5_save_ccname_send(kr, be_req->be_ctx->ev, + be_req->be_ctx->sysdb, + be_req->be_ctx->domain, pd->user, + kr->ccname); + if (req == NULL) { + DEBUG(1, ("krb5_save_ccname_send failed.\n")); + goto failed; + } + + tevent_req_set_callback(req, krb5_save_ccname_done, kr); + return; + } } - req = handle_child_send(be_req, be_req->be_ctx->ev, kr); + req = handle_child_send(kr, be_req->be_ctx->ev, kr); if (req == NULL) { DEBUG(1, ("handle_child_send failed.\n")); goto failed; } - tevent_req_set_callback(req, krb5_pam_handler_done, kr); + tevent_req_set_callback(req, krb5_child_done, kr); return; failed: talloc_free(kr); pd->pam_status = pam_status; - krb_reply(be_req, DP_ERR_FATAL, pd->pam_status); + krb_reply(be_req, dp_err, pd->pam_status); } -static void krb5_pam_handler_done(struct tevent_req *req) +static void krb5_child_done(struct tevent_req *req) { struct krb5child_req *kr = tevent_req_callback_data(req, struct krb5child_req); struct pam_data *pd = kr->pd; struct be_req *be_req = kr->req; - struct krb5_ctx *krb5_ctx = kr->krb5_ctx; int ret; uint8_t *buf; ssize_t len; + ssize_t pref_len; int p; int32_t *msg_status; int32_t *msg_type; int32_t *msg_len; - struct tevent_req *subreq = NULL; - char *password = NULL; - char *env = NULL; + int pam_status = PAM_SYSTEM_ERR; int dp_err = DP_ERR_FATAL; - const char *dummy; - - pd->pam_status = PAM_SYSTEM_ERR; - talloc_free(kr); ret = handle_child_recv(req, pd, &buf, &len); talloc_zfree(req); @@ -869,56 +1159,101 @@ static void krb5_pam_handler_done(struct tevent_req *req) goto done; } - ret = pam_add_response(pd, *msg_type, *msg_len, &buf[p]); - if (ret != EOK) { - DEBUG(1, ("pam_add_response failed.\n")); + if (*msg_status != PAM_SUCCESS && *msg_status != PAM_AUTHINFO_UNAVAIL) { + pam_status = *msg_status; + dp_err = DP_ERR_OK; + + ret = pam_add_response(pd, *msg_type, *msg_len, &buf[p]); + if (ret != EOK) { + DEBUG(1, ("pam_add_response failed.\n")); + } + + goto done; + } else { + pd->pam_status = *msg_status; + } + + pref_len = strlen(CCACHE_ENV_NAME)+1; + if (*msg_len > pref_len && + strncmp((const char *) &buf[p], CCACHE_ENV_NAME"=", pref_len) == 0) { + kr->ccname = talloc_strndup(kr, (char *) &buf[p+pref_len], + *msg_len-pref_len); + if (kr->ccname == NULL) { + DEBUG(1, ("talloc_strndup failed.\n")); + goto done; + } + } else { + DEBUG(1, ("Missing ccache name in child response [%.*s].\n", *msg_len, + &buf[p])); goto done; } if (*msg_status == PAM_AUTHINFO_UNAVAIL) { be_mark_offline(be_req->be_ctx); - pd->pam_status = *msg_status; - dp_err = DP_ERR_OFFLINE; + kr->is_offline = true; + } + + struct sysdb_attrs *attrs; + attrs = sysdb_new_attrs(kr); + ret = sysdb_attrs_add_string(attrs, SYSDB_CCACHE_FILE, kr->ccname); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_add_string failed.\n")); goto done; } - if (*msg_status == PAM_SUCCESS && pd->cmd == SSS_PAM_AUTHENTICATE) { - dummy = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM); - if (dummy != NULL) { - env = talloc_asprintf(pd, "%s=%s", SSSD_KRB5_REALM, dummy); - if (env == NULL) { - DEBUG(1, ("talloc_asprintf failed.\n")); - goto done; - } - ret = pam_add_response(pd, PAM_ENV_ITEM, strlen(env)+1, - (uint8_t *) env); - if (ret != EOK) { - DEBUG(1, ("pam_add_response failed.\n")); - goto done; - } - } + req = krb5_save_ccname_send(kr, be_req->be_ctx->ev, be_req->be_ctx->sysdb, + be_req->be_ctx->domain, pd->user, kr->ccname); + if (req == NULL) { + DEBUG(1, ("krb5_save_ccname_send failed.\n")); + goto done; + } - dummy = dp_opt_get_cstring(krb5_ctx->opts, KRB5_KDC); - if (dummy != NULL) { - env = talloc_asprintf(pd, "%s=%s", SSSD_KRB5_KDC, dummy); - if (env == NULL) { - DEBUG(1, ("talloc_asprintf failed.\n")); - goto done; - } - ret = pam_add_response(pd, PAM_ENV_ITEM, strlen(env)+1, - (uint8_t *) env); - if (ret != EOK) { - DEBUG(1, ("pam_add_response failed.\n")); - goto done; - } + tevent_req_set_callback(req, krb5_save_ccname_done, kr); + return; +done: + talloc_free(kr); + pd->pam_status = pam_status; + krb_reply(be_req, dp_err, pd->pam_status); +} + +static void krb5_save_ccname_done(struct tevent_req *req) +{ + struct krb5child_req *kr = tevent_req_callback_data(req, + struct krb5child_req); + struct pam_data *pd = kr->pd; + struct be_req *be_req = kr->req; + struct krb5_ctx *krb5_ctx = kr->krb5_ctx; + int pam_status=PAM_SYSTEM_ERR; + int dp_err = DP_ERR_FATAL; + int ret; + char *password = NULL; + + if (pd->cmd == SSS_PAM_AUTHENTICATE) { + ret = add_krb5_env(krb5_ctx->opts, kr->ccname, pd); + if (ret != EOK) { + DEBUG(1, ("add_krb5_env failed.\n")); + goto failed; } } - pd->pam_status = *msg_status; - dp_err = DP_ERR_OK; + ret = sysdb_set_user_attr_recv(req); + talloc_zfree(req); + if (ret != EOK) { + DEBUG(1, ("Saving ccache name failed.\n")); + goto failed; + } + + if (kr->is_offline) { + DEBUG(4, ("Backend is marked offline, retry later!\n")); + pam_status = PAM_AUTHINFO_UNAVAIL; + dp_err = DP_ERR_OFFLINE; + goto failed; + } + + if (be_req->be_ctx->domain->cache_credentials == TRUE) { - if (pd->pam_status == PAM_SUCCESS && - be_req->be_ctx->domain->cache_credentials == TRUE) { + /* password caching failures are not fatal errors */ + pd->pam_status = PAM_SUCCESS; switch(pd->cmd) { case SSS_PAM_AUTHENTICATE: @@ -941,24 +1276,27 @@ static void krb5_pam_handler_done(struct tevent_req *req) if (password == NULL) { DEBUG(0, ("password not available, offline auth may not work.\n")); - goto done; + goto failed; } talloc_set_destructor((TALLOC_CTX *)password, password_destructor); - subreq = sysdb_cache_password_send(be_req, be_req->be_ctx->ev, - be_req->be_ctx->sysdb, NULL, - be_req->be_ctx->domain, pd->user, - password); - if (subreq == NULL) { + req = sysdb_cache_password_send(be_req, be_req->be_ctx->ev, + be_req->be_ctx->sysdb, NULL, + be_req->be_ctx->domain, pd->user, + password); + if (req == NULL) { DEBUG(2, ("cache_password_send failed, offline auth may not work.\n")); - goto done; + goto failed; } - tevent_req_set_callback(subreq, krb5_pam_handler_cache_done, be_req); + tevent_req_set_callback(req, krb5_pam_handler_cache_done, be_req); return; } -done: +failed: + talloc_free(kr); + + pd->pam_status = pam_status; krb_reply(be_req, dp_err, pd->pam_status); } diff --git a/server/providers/krb5/krb5_auth.h b/server/providers/krb5/krb5_auth.h index 84eafec8..54ce2b8b 100644 --- a/server/providers/krb5/krb5_auth.h +++ b/server/providers/krb5/krb5_auth.h @@ -47,8 +47,9 @@ struct krb5child_req { struct tevent_timer *timeout_handler; - char *ccname; + const char *ccname; const char *homedir; + bool is_offline; }; struct krb5_ctx { diff --git a/server/providers/krb5/krb5_child.c b/server/providers/krb5/krb5_child.c index 66fccf89..5a1bf374 100644 --- a/server/providers/krb5/krb5_child.c +++ b/server/providers/krb5/krb5_child.c @@ -73,7 +73,6 @@ struct krb5_child_ctx { struct krb5_req { krb5_context ctx; - krb5_ccache cc; krb5_principal princ; char* name; krb5_creds *creds; @@ -106,6 +105,98 @@ struct response { uint8_t *buf; }; +static krb5_error_code create_ccache_file(struct krb5_req *kr, krb5_creds *creds) +{ + krb5_error_code kerr; + krb5_ccache tmp_cc = NULL; + char *cc_file_name; + int fd = -1; + size_t ccname_len; + char *dummy; + char *tmp_ccname; + + if (strncmp(kr->ccname, "FILE:", 5) == 0) { + cc_file_name = kr->ccname + 5; + } else { + cc_file_name = kr->ccname; + } + + if (cc_file_name[0] != '/') { + DEBUG(1, ("Ccache filename is not an absolute path.\n")); + return EINVAL; + } + + dummy = strrchr(cc_file_name, '/'); + tmp_ccname = talloc_strndup(kr, cc_file_name, (size_t) (dummy-cc_file_name)); + if (tmp_ccname == NULL) { + DEBUG(1, ("talloc_strdup failed.\n")); + return ENOMEM; + } + tmp_ccname = talloc_asprintf_append(tmp_ccname, "/.krb5cc_dummy_XXXXXX"); + + fd = mkstemp(tmp_ccname); + if (fd == -1) { + DEBUG(1, ("mkstemp failed [%d][%s].\n", errno, strerror(errno))); + return errno; + } + + kerr = krb5_cc_resolve(kr->ctx, tmp_ccname, &tmp_cc); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto done; + } + + kerr = krb5_cc_initialize(kr->ctx, tmp_cc, kr->princ); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto done; + } + if (fd != -1) { + close(fd); + fd = -1; + } + + if (creds != NULL) { + kerr = krb5_cc_store_cred(kr->ctx, tmp_cc, creds); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto done; + } + } + + kerr = krb5_cc_close(kr->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 ) { + fd = mkstemp(cc_file_name); + if (fd == -1) { + DEBUG(1, ("mkstemp failed [%d][%s].\n", errno, strerror(errno))); + kerr = errno; + goto done; + } + } + + kerr = rename(tmp_ccname, cc_file_name); + if (kerr == -1) { + DEBUG(1, ("rename failed [%d][%s].\n", errno, strerror(errno))); + } + +done: + if (fd != -1) { + close(fd); + fd = -1; + } + if (kerr != 0 && tmp_cc != NULL) { + krb5_cc_destroy(kr->ctx, tmp_cc); + } + return kerr; +} + static struct response *init_response(TALLOC_CTX *mem_ctx) { struct response *r; r = talloc(mem_ctx, struct response); @@ -163,7 +254,7 @@ static struct response *prepare_response_message(struct krb5_req *kr, } if (kerr == 0) { - if (kr->cc == NULL || kr->ccname == NULL) { + if (kr->ccname == NULL) { DEBUG(1, ("Error obtaining ccname.\n")); return NULL; } @@ -195,6 +286,27 @@ static struct response *prepare_response_message(struct krb5_req *kr, return resp; } +static errno_t sendresponse(int fd, krb5_error_code kerr, int pam_status, + struct krb5_req *kr) +{ + int ret; + struct response *resp; + + resp = prepare_response_message(kr, kerr, pam_status); + if (resp == NULL) { + DEBUG(1, ("prepare_response_message failed.\n")); + return ENOMEM; + } + + ret = write(fd, resp->buf, resp->size); + if (ret == -1) { + DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno))); + return errno; + } + + return EOK; +} + static krb5_error_code validate_tgt(struct krb5_req *kr) { krb5_error_code kerr; @@ -293,9 +405,6 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, { krb5_error_code kerr = 0; int ret; - 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, @@ -324,47 +433,12 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, DEBUG(9, ("TGT validation is disabled.\n")); } - if (kr->ccname[0] == '/' || strncmp(kr->ccname, "FILE:", 5) == 0) { - offset = 0; - if (kr->ccname[0] == 'F') { - offset = 5; - } - ccname_len = strlen(kr->ccname); - 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); + kerr = create_ccache_file(kr, kr->creds); if (kerr != 0) { KRB5_DEBUG(1, 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); - goto done; - } - - kerr = krb5_cc_store_cred(kr->ctx, kr->cc, kr->creds); - if (kerr != 0) { - KRB5_DEBUG(1, kerr); - krb5_cc_destroy(kr->ctx, kr->cc); - kr->cc = NULL; - goto done; - } - kerr = 0; done: @@ -378,10 +452,8 @@ static errno_t changepw_child(int fd, struct krb5_req *kr) { int ret; krb5_error_code kerr = 0; - errno_t err; char *pass_str = NULL; char *newpass_str = NULL; - struct response *resp = NULL; int pam_status = PAM_SYSTEM_ERR; int result_code = -1; krb5_data result_code_string; @@ -425,6 +497,8 @@ static errno_t changepw_child(int fd, struct krb5_req *kr) if (kerr != 0 || result_code != 0) { if (kerr != 0) { KRB5_DEBUG(1, kerr); + } else { + kerr = KRB5KRB_ERR_GENERIC; } if (result_code_string.length > 0) { @@ -455,43 +529,20 @@ static errno_t changepw_child(int fd, struct krb5_req *kr) } sendresponse: - resp = prepare_response_message(kr, kerr, pam_status); - if (resp == NULL) { - DEBUG(1, ("prepare_response_message failed.\n")); - if (kr->cc != NULL) { - krb5_cc_destroy(kr->ctx, kr->cc); - kr->cc = NULL; - } - return ENOMEM; - } - - ret = write(fd, resp->buf, resp->size); - if (ret == -1) { - err = errno; - DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno))); - if (kr->cc != NULL) { - krb5_cc_destroy(kr->ctx, kr->cc); - kr->cc = NULL; - } - return err; - } - - if (kr->cc != NULL) { - krb5_cc_close(kr->ctx, kr->cc); - kr->cc = NULL; + ret = sendresponse(fd, kerr, pam_status, kr); + if (ret != EOK) { + DEBUG(1, ("sendresponse failed.\n")); } - return EOK; + return ret; } static errno_t tgt_req_child(int fd, struct krb5_req *kr) { int ret; krb5_error_code kerr = 0; - errno_t err; char *pass_str = NULL; int pam_status = PAM_SYSTEM_ERR; - struct response *resp = NULL; pass_str = talloc_strndup(kr, (const char *) kr->pd->authtok, kr->pd->authtok_size); @@ -506,7 +557,7 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr) /* If the password is expired the KDC will always return KRB5KDC_ERR_KEY_EXP regardless if the supplied password is correct or not. In general the password can still be used to get a changepw ticket. - So we validate the password by trying to get a changepw ticket. */ + So we validate the password by trying to get a changepw ticket. */ if (kerr == KRB5KDC_ERR_KEY_EXP) { kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, pass_str, NULL, NULL, 0, @@ -537,36 +588,35 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr) } sendresponse: - resp = prepare_response_message(kr, kerr, pam_status); - if (resp == NULL) { - DEBUG(1, ("prepare_response_message failed.\n")); - if (kr->cc != NULL) { - krb5_cc_destroy(kr->ctx, kr->cc); - kr->cc = NULL; - } - return ENOMEM; + ret = sendresponse(fd, kerr, pam_status, kr); + if (ret != EOK) { + DEBUG(1, ("sendresponse failed.\n")); } - ret = write(fd, resp->buf, resp->size); - if (ret == -1) { - err = errno; - DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno))); - if (kr->cc != NULL) { - krb5_cc_destroy(kr->ctx, kr->cc); - kr->cc = NULL; - } - return err; + return ret; +} + +static errno_t create_empty_ccache(int fd, struct krb5_req *kr) +{ + int ret; + int pam_status = PAM_SUCCESS; + + ret = create_ccache_file(kr, NULL); + if (ret != 0) { + KRB5_DEBUG(1, ret); + pam_status = PAM_SYSTEM_ERR; } - if (kr->cc != NULL) { - krb5_cc_close(kr->ctx, kr->cc); - kr->cc = NULL; + ret = sendresponse(fd, ret, pam_status, kr); + if (ret != EOK) { + DEBUG(1, ("sendresponse failed.\n")); } - return EOK; + return ret; } -uint8_t *copy_buffer_and_add_zero(TALLOC_CTX *mem_ctx, const uint8_t *src, size_t len) +uint8_t *copy_buffer_and_add_zero(TALLOC_CTX *mem_ctx, const uint8_t *src, + size_t len) { uint8_t *str; @@ -680,11 +730,9 @@ static int krb5_cleanup(void *ptr) krb5_free_creds(kr->ctx, kr->creds); } if (kr->name != NULL) - krb5_free_unparsed_name(kr->ctx, kr->name); + sss_krb5_free_unparsed_name(kr->ctx, kr->name); if (kr->princ != NULL) krb5_free_principal(kr->ctx, kr->princ); - if (kr->cc != NULL) - krb5_cc_close(kr->ctx, kr->cc); if (kr->ctx != NULL) krb5_free_context(kr->ctx); @@ -735,7 +783,13 @@ static int krb5_setup(struct pam_data *pd, const char *user_princ_str, switch(pd->cmd) { case SSS_PAM_AUTHENTICATE: - kr->child_req = tgt_req_child; + /* if authtok_size is 1 we got an empty password, this means we + * are offline and need to create an empty ccache file */ + if (pd->authtok_size == 1) { + kr->child_req = create_empty_ccache; + } else { + kr->child_req = tgt_req_child; + } break; case SSS_PAM_CHAUTHTOK: kr->child_req = changepw_child; @@ -898,6 +952,7 @@ int main(int argc, const char *argv[]) ret = kr->child_req(STDOUT_FILENO, kr); if (ret != EOK) { DEBUG(1, ("Child request failed.\n")); + goto fail; } close(STDOUT_FILENO); @@ -906,6 +961,7 @@ int main(int argc, const char *argv[]) return 0; fail: + close(STDOUT_FILENO); talloc_free(pd); exit(-1); } diff --git a/server/tests/find_uid-tests.c b/server/tests/find_uid-tests.c new file mode 100644 index 00000000..bd213cd5 --- /dev/null +++ b/server/tests/find_uid-tests.c @@ -0,0 +1,124 @@ +/* + SSSD + + find_uid - Utilities tests + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2009 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> + +#include <check.h> + +#include "util/find_uid.h" + + +START_TEST(test_check_if_uid_is_active_success) +{ + uid_t uid; + bool result; + int ret; + + uid = getuid(); + + ret = check_if_uid_is_active(uid, &result); + fail_unless(ret == EOK, "check_if_uid_is_active failed."); + fail_unless(result, "check_if_uid_is_active did not found my uid [%d]", + uid); +} +END_TEST + +START_TEST(test_check_if_uid_is_active_fail) +{ + uid_t uid; + bool result; + int ret; + + uid = (uid_t) -4; + + ret = check_if_uid_is_active(uid, &result); + fail_unless(ret == EOK, "check_if_uid_is_active failed."); + fail_unless(!result, "check_if_uid_is_active found (hopefully not active) " + "uid [%d]", uid); +} +END_TEST + +START_TEST(test_get_uid_table) +{ + uid_t uid; + int ret; + TALLOC_CTX *tmp_ctx; + hash_table_t *table; + hash_key_t key; + hash_value_t value; + + tmp_ctx = talloc_new(NULL); + fail_unless(tmp_ctx != NULL, "talloc_new failed."); + + ret = get_uid_table(tmp_ctx, &table); + fail_unless(ret == EOK, "get_uid_table failed."); + + uid = getuid(); + key.type = HASH_KEY_ULONG; + key.ul = (unsigned long) uid; + + ret = hash_lookup(table, &key, &value); + + fail_unless(ret == HASH_SUCCESS, "Cannot find my uid [%d] in the table", uid); + + uid = (uid_t) -4; + key.type = HASH_KEY_ULONG; + key.ul = (unsigned long) uid; + + ret = hash_lookup(table, &key, &value); + + fail_unless(ret == HASH_ERROR_KEY_NOT_FOUND, "Found (hopefully not active) " + "uid [%d] in the table", uid); + + talloc_free(tmp_ctx); +} +END_TEST + +Suite *find_uid_suite (void) +{ + Suite *s = suite_create ("find_uid"); + + TCase *tc_find_uid = tcase_create ("find_uid"); + + tcase_add_test (tc_find_uid, test_check_if_uid_is_active_success); + tcase_add_test (tc_find_uid, test_check_if_uid_is_active_fail); + tcase_add_test (tc_find_uid, test_get_uid_table); + suite_add_tcase (s, tc_find_uid); + + return s; +} + +int main(void) +{ + debug_level = 255; + int number_failed; + Suite *s = find_uid_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; +} diff --git a/server/util/find_uid.c b/server/util/find_uid.c new file mode 100644 index 00000000..2fead50a --- /dev/null +++ b/server/util/find_uid.c @@ -0,0 +1,297 @@ +/* + SSSD + + Create uid table + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2009 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <talloc.h> +#include <ctype.h> +#include <sys/time.h> + +#include "dhash.h" +#include "util/util.h" + +#define INITIAL_TABLE_SIZE 64 +#define PATHLEN (NAME_MAX + 14) +#define BUFSIZE 4096 + +TALLOC_CTX *hash_talloc_ctx(TALLOC_CTX *mem_ctx) +{ + static TALLOC_CTX * saved_ctx = NULL; + + if (mem_ctx != NULL) { + saved_ctx = mem_ctx; + } + + return saved_ctx; +} + +void *hash_talloc(const size_t size) +{ + return talloc_size(hash_talloc_ctx(NULL), size); +} + +void hash_talloc_free(void *ptr) +{ + talloc_free(ptr); +} + +static errno_t get_uid_from_pid(const pid_t pid, uid_t *uid) +{ + int ret; + char path[PATHLEN]; + struct stat stat_buf; + int fd; + char buf[BUFSIZE]; + char *p; + char *e; + char *endptr; + long num=0; + + ret = snprintf(path, PATHLEN, "/proc/%d/status", pid); + if (ret < 0) { + DEBUG(1, ("snprintf failed")); + return EINVAL; + } else if (ret >= PATHLEN) { + DEBUG(1, ("path too long?!?!\n")); + return EINVAL; + } + + ret = lstat(path, &stat_buf); + if (ret == -1) { + DEBUG(1, ("lstat failed [%d][%s].\n", errno, strerror(errno))); + return errno; + } + + if (!S_ISREG(stat_buf.st_mode)) { + DEBUG(1, ("not a regular file\n")); + return EINVAL; + } + + fd = open(path, O_RDONLY); + if (fd == -1) { + DEBUG(1, ("open failed [%d][%s].\n", errno, strerror(errno))); + return errno; + } + ret = read(fd, buf, BUFSIZE); + if (ret == -1) { + DEBUG(1, ("read failed [%d][%s].\n", errno, strerror(errno))); + return errno; + } + + ret = close(fd); + if (ret == -1) { + DEBUG(1, ("close failed [%d][%s].\n", errno, strerror(errno))); + } + + p = strstr(buf, "\nUid:\t"); + if (p != NULL) { + p += 6; + e = strchr(p,'\t'); + if (e == NULL) { + DEBUG(1, ("missing delimiter.\n")); + return EINVAL; + } else { + *e = '\0'; + } + num = strtol(p, &endptr, 10); + if(errno == ERANGE) { + DEBUG(1, ("strtol failed [%s].\n", strerror(errno))); + return errno; + } + if (*endptr != '\0') { + DEBUG(1, ("uid contains extra characters\n")); + return EINVAL; + } + + if (num < 0 || num >= INT_MAX) { + DEBUG(1, ("uid out of range.\n")); + return ERANGE; + } + + } else { + DEBUG(1, ("format error\n")); + return EINVAL; + } + + *uid = num; + + return EOK; +} + +static errno_t name_to_pid(const char *name, pid_t *pid) +{ + long num; + char *endptr; + + errno = 0; + num = strtol(name, &endptr, 10); + if(errno == ERANGE) { + perror("strtol"); + return errno; + } + + if (*endptr != '\0') { + DEBUG(1, ("pid string contains extra characters.\n")); + return EINVAL; + } + + if (num <= 0 || num >= INT_MAX) { + DEBUG(1, ("pid out of range.\n")); + return ERANGE; + } + + *pid = num; + + return EOK; +} + +static int only_numbers(char *p) +{ + while(*p!='\0' && isdigit(*p)) ++p; + return *p; +} + +static errno_t get_active_uid_linux(hash_table_t *table, uid_t search_uid) +{ + DIR *proc_dir = NULL; + struct dirent *dirent; + int ret; + pid_t pid; + uid_t uid; + + hash_key_t key; + hash_value_t value; + + proc_dir = opendir("/proc"); + if (proc_dir == NULL) { + DEBUG(1, ("Cannot open proc dir.\n")); + ret = errno; + goto done; + }; + + errno = 0; + while ((dirent = readdir(proc_dir)) != NULL) { + if (only_numbers(dirent->d_name) != 0) continue; + ret = name_to_pid(dirent->d_name, &pid); + if (ret != EOK) { + DEBUG(1, ("name_to_pid failed.\n")); + goto done; + } + + ret = get_uid_from_pid(pid, &uid); + if (ret != EOK) { + DEBUG(1, ("get_uid_from_pid failed.\n")); + goto done; + } + + if (table != NULL) { + key.type = HASH_KEY_ULONG; + key.ul = (unsigned long) uid; + value.type = HASH_VALUE_ULONG; + value.ul = (unsigned long) uid; + + ret = hash_enter(table, &key, &value); + if (ret != HASH_SUCCESS) { + DEBUG(1, ("cannot add to table [%s]\n", hash_error_string(ret))); + ret = ENOMEM; + goto done; + } + } else { + if (uid == search_uid) { + ret = EOK; + goto done; + } + } + + + errno = 0; + } + if (errno != 0 && dirent == NULL) { + DEBUG(1 ,("readdir failed.\n")); + ret = errno; + goto done; + } + + ret = closedir(proc_dir); + proc_dir = NULL; + if (ret == -1) { + DEBUG(1 ,("closedir failed, watch out.\n")); + } + + if (table != NULL) { + ret = EOK; + } else { + ret = ENOENT; + } + +done: + if (proc_dir != NULL) closedir(proc_dir); + return ret; +} + +errno_t get_uid_table(TALLOC_CTX *mem_ctx, hash_table_t **table) +{ +#ifdef __linux__ + int ret; + + hash_talloc_ctx(mem_ctx); + ret = hash_create_ex(INITIAL_TABLE_SIZE, table, 0, 0, 0, 0, + hash_talloc, hash_talloc_free, NULL); + if (ret != HASH_SUCCESS) { + DEBUG(1, ("hash_create_ex failed [%s]\n", hash_error_string(ret))); + return ENOMEM; + } + + return get_active_uid_linux(*table, 0); +#else + return ENOSYS; +#endif +} + +errno_t check_if_uid_is_active(uid_t uid, bool *result) +{ + int ret; + + ret = get_active_uid_linux(NULL, uid); + if (ret != EOK && ret != ENOENT) { + DEBUG(1, ("get_uid_table failed.\n")); + return ret; + } + + if (ret == EOK) { + *result = true; + } else { + *result = false; + } + + return EOK; +} diff --git a/server/util/find_uid.h b/server/util/find_uid.h new file mode 100644 index 00000000..154a5f95 --- /dev/null +++ b/server/util/find_uid.h @@ -0,0 +1,36 @@ +/* + SSSD + + Create uid table + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2009 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef __FIND_UID_H__ +#define __FIND_UID_H__ + +#include <talloc.h> +#include <sys/types.h> + +#include "dhash.h" +#include "util/util.h" + +errno_t get_uid_table(TALLOC_CTX *mem_ctx, hash_table_t **table); +errno_t check_if_uid_is_active(uid_t uid, bool *result); + +#endif /* __FIND_UID_H__ */ |