summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2009-11-09 21:54:06 +0100
committerStephen Gallagher <sgallagh@redhat.com>2009-11-20 11:18:51 -0500
commitb9fda1a01873eab1887c53bdbc2f76d75d665681 (patch)
treed5bed1a691abda57eff8bd8bc92c6ce0a07bb557
parent55ab3a9b2dcbe809dece953605ab359c5e12a139 (diff)
downloadsssd-b9fda1a01873eab1887c53bdbc2f76d75d665681.tar.gz
sssd-b9fda1a01873eab1887c53bdbc2f76d75d665681.tar.xz
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.am30
-rw-r--r--server/db/sysdb.h1
-rw-r--r--server/providers/krb5/krb5_auth.c546
-rw-r--r--server/providers/krb5/krb5_auth.h3
-rw-r--r--server/providers/krb5/krb5_child.c250
-rw-r--r--server/tests/find_uid-tests.c124
-rw-r--r--server/util/find_uid.c297
-rw-r--r--server/util/find_uid.h36
8 files changed, 1081 insertions, 206 deletions
diff --git a/server/Makefile.am b/server/Makefile.am
index d939f524b..fc2ac94d5 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 a32998590..f94b43fdb 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 d3e05e120..8068bce96 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 84eafec81..54ce2b8b8 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 66fccf89f..5a1bf374e 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 000000000..bd213cd5a
--- /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 000000000..2fead50a3
--- /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 000000000..154a5f955
--- /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__ */