summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2014-06-24 18:30:01 +0200
committerJakub Hrozek <jhrozek@redhat.com>2014-09-01 13:56:57 +0200
commit899d1bdc048cd74518170d7d9535d76d3f46d4af (patch)
tree289feefa466ed4d844afacdc94b9446aba480cad /src
parent7d2437adc312d3322d36043ff458fafdb4b7f2cf (diff)
downloadsssd-899d1bdc048cd74518170d7d9535d76d3f46d4af.tar.gz
sssd-899d1bdc048cd74518170d7d9535d76d3f46d4af.tar.xz
sssd-899d1bdc048cd74518170d7d9535d76d3f46d4af.zip
PAM, NSS: allow UPN login names
With this patch the NSS and PAM responders can handle user principal names besides the fully qualified user names. User principal names are build from a user name and a domain suffix separated by an '@' sign. But the domain suffix does not necessarily has to be the same as the configured domain name in sssd.conf of the dynamically discovered DNS domain name of a domain. The typical use case is an Active Directory forest with lots of different domains. To not force the users to remember the name of the individual domain they belong to the AD administrator can set a common domain suffix for all users from all domains in the forest. This is typically the domain name used for emails to make it even more easy to the users to remember it. Since SSSD splits name and domain part at the '@' sign and the common domain suffix might not be resolvable by DNS or the given user is not a member of that domain (e.g. in the case where the forest root is used as common domain suffix) SSSD might fail to look up the user. With this patch the NSS and PAM responder will do an extra lookup for a UPN if the domain part of the given name is not known or the user was not found and the login name contained the '@' sign. Resolves https://fedorahosted.org/sssd/ticket/1749
Diffstat (limited to 'src')
-rw-r--r--src/providers/data_provider.h1
-rw-r--r--src/responder/nss/nsssrv_cmd.c142
-rw-r--r--src/responder/nss/nsssrv_private.h1
-rw-r--r--src/responder/pam/pamsrv_cmd.c47
4 files changed, 168 insertions, 23 deletions
diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h
index 742fcca57..b1279852e 100644
--- a/src/providers/data_provider.h
+++ b/src/providers/data_provider.h
@@ -176,6 +176,7 @@ struct pam_data {
struct sss_auth_token *newauthtok;
uint32_t cli_pid;
char *logon_name;
+ bool name_is_upn;
int pam_status;
int response_delay;
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 04ff2d654..bf578f394 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -27,6 +27,7 @@
#include "responder/nss/nsssrv_services.h"
#include "responder/nss/nsssrv_mmap_cache.h"
#include "responder/common/negcache.h"
+#include "providers/data_provider.h"
#include "confdb/confdb.h"
#include "db/sysdb.h"
#include "sss_client/idmap/sss_nss_idmap.h"
@@ -728,13 +729,16 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
char *name = NULL;
struct nss_ctx *nctx;
int ret;
+ static const char *user_attrs[] = SYSDB_PW_ATTRS;
+ struct ldb_message *msg;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
while (dom) {
/* if it is a domainless search, skip domains that require fully
* qualified names instead */
- while (dom && cmdctx->check_next && dom->fqnames) {
+ while (dom && cmdctx->check_next && dom->fqnames
+ && !cmdctx->name_is_upn) {
dom = get_next_domain(dom, false);
}
@@ -791,7 +795,37 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
return EIO;
}
- ret = sysdb_getpwnam(cmdctx, dom, name, &dctx->res);
+ if (cmdctx->name_is_upn) {
+ ret = sysdb_search_user_by_upn(cmdctx, dom, name, user_attrs, &msg);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_upn failed.\n");
+ return ret;
+ }
+
+ dctx->res = talloc_zero(cmdctx, struct ldb_result);
+ if (dctx->res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ return ENOMEM;
+ }
+
+ if (ret == ENOENT) {
+ dctx->res->count = 0;
+ dctx->res->msgs = NULL;
+ ret = EOK;
+ } else {
+ dctx->res->count = 1;
+ dctx->res->msgs = talloc_array(dctx->res,
+ struct ldb_message *, 1);
+ if (dctx->res->msgs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+ return ENOMEM;
+ }
+
+ dctx->res->msgs[0] = talloc_steal(dctx->res->msgs, msg);
+ }
+ } else {
+ ret = sysdb_getpwnam(cmdctx, dom, name, &dctx->res);
+ }
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to make request to our cache!\n");
@@ -835,7 +869,8 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
* yet) then verify that the cache is uptodate */
if (dctx->check_provider) {
ret = check_cache(dctx, nctx, dctx->res,
- SSS_DP_USER, name, 0, NULL,
+ SSS_DP_USER, name, 0,
+ cmdctx->name_is_upn ? EXTRA_NAME_IS_UPN : NULL,
nss_cmd_getby_dp_callback,
dctx);
if (ret != EOK) {
@@ -868,6 +903,44 @@ static errno_t nss_cmd_getbysid_search(struct nss_dom_ctx *dctx);
static errno_t nss_cmd_getbysid_send_reply(struct nss_dom_ctx *dctx);
static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx *dctx);
+static int nss_cmd_assume_upn(struct nss_dom_ctx *dctx)
+{
+ int ret;
+
+ if (dctx->domain == NULL) {
+ dctx->domain = dctx->cmdctx->cctx->rctx->domains;
+ dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
+ dctx->cmdctx->check_next = true;
+ dctx->cmdctx->name = talloc_strdup(dctx->cmdctx, dctx->rawname);
+ dctx->cmdctx->name_is_upn = true;
+ if (dctx->cmdctx->name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ return ENOMEM;
+ }
+ }
+
+ switch (dctx->cmdctx->cmd) {
+ case SSS_NSS_GETPWNAM:
+ ret = nss_cmd_getpwnam_search(dctx);
+ if (ret == EOK) {
+ ret = nss_cmd_getpw_send_reply(dctx, false);
+ }
+ break;
+ case SSS_NSS_INITGR:
+ ret = nss_cmd_initgroups_search(dctx);
+ if (ret == EOK) {
+ ret = nss_cmd_initgr_send_reply(dctx);
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d].\n",
+ dctx->cmdctx->cmd);
+ ret = EINVAL;
+ }
+
+ return ret;
+}
+
static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr)
{
@@ -1006,6 +1079,14 @@ static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min,
}
done:
+ if (ret == ENOENT
+ && (dctx->cmdctx->cmd == SSS_NSS_GETPWNAM
+ || dctx->cmdctx->cmd == SSS_NSS_INITGR)
+ && dctx->rawname != NULL && strchr(dctx->rawname, '@') != NULL) {
+ /* assume Kerberos principal */
+ ret = nss_cmd_assume_upn(dctx);
+ }
+
ret = nss_cmd_done(cmdctx, ret);
if (ret) {
NSS_CMD_FATAL_ERROR(cctx);
@@ -1211,6 +1292,11 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getpw_send_reply(dctx, false);
+ } else if (ret == ENOENT
+ && dctx->rawname != NULL
+ && strchr(dctx->rawname, '@') != NULL) {
+ /* assume Kerberos principal */
+ ret = nss_cmd_assume_upn(dctx);
}
break;
case SSS_NSS_GETGRNAM:
@@ -1225,6 +1311,11 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_initgr_send_reply(dctx);
+ } else if (ret == ENOENT
+ && dctx->rawname != NULL
+ && strchr(dctx->rawname, '@') != NULL) {
+ /* assume Kerberos principal */
+ ret = nss_cmd_assume_upn(dctx);
}
break;
case SSS_NSS_GETSIDBYNAME:
@@ -1261,7 +1352,12 @@ static void nss_cmd_getbynam_done(struct tevent_req *req)
ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains,
cctx->rctx->default_domain, rawname,
&domname, &cmdctx->name);
- if (ret != EOK) {
+ if (ret == EAGAIN && (dctx->cmdctx->cmd == SSS_NSS_GETPWNAM
+ || dctx->cmdctx->cmd == SSS_NSS_INITGR)) {
+ /* assume Kerberos principal */
+ ret = nss_cmd_assume_upn(dctx);
+ goto done;
+ } else if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", rawname);
ret = ENOENT;
goto done;
@@ -1291,6 +1387,11 @@ static void nss_cmd_getbynam_done(struct tevent_req *req)
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getpw_send_reply(dctx, false);
+ } else if (ret == ENOENT
+ && dctx->rawname != NULL
+ && strchr(dctx->rawname, '@') != NULL) {
+ /* assume Kerberos principal */
+ ret = nss_cmd_assume_upn(dctx);
}
break;
case SSS_NSS_GETGRNAM:
@@ -1305,6 +1406,11 @@ static void nss_cmd_getbynam_done(struct tevent_req *req)
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_initgr_send_reply(dctx);
+ } else if (ret == ENOENT
+ && dctx->rawname != NULL
+ && strchr(dctx->rawname, '@') != NULL) {
+ /* assume Kerberos principal */
+ ret = nss_cmd_assume_upn(dctx);
}
break;
case SSS_NSS_GETSIDBYNAME:
@@ -3734,13 +3840,17 @@ static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx)
char *name = NULL;
struct nss_ctx *nctx;
int ret;
+ static const char *user_attrs[] = SYSDB_PW_ATTRS;
+ struct ldb_message *msg;
+ const char *sysdb_name;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
while (dom) {
/* if it is a domainless search, skip domains that require fully
* qualified names instead */
- while (dom && cmdctx->check_next && dom->fqnames) {
+ while (dom && cmdctx->check_next && dom->fqnames
+ && !cmdctx->name_is_upn) {
dom = get_next_domain(dom, false);
}
@@ -3797,7 +3907,24 @@ static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx)
return EIO;
}
- ret = sysdb_initgroups(cmdctx, dom, name, &dctx->res);
+ if (cmdctx->name_is_upn) {
+ ret = sysdb_search_user_by_upn(cmdctx, dom, name, user_attrs, &msg);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_upn failed.\n");
+ return ret;
+ }
+
+ sysdb_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (sysdb_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Sysdb entry does not have a name.\n");
+ return EINVAL;
+ }
+
+ ret = sysdb_initgroups(cmdctx, dom, sysdb_name, &dctx->res);
+ } else {
+ ret = sysdb_initgroups(cmdctx, dom, name, &dctx->res);
+ }
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to make request to our cache! [%d][%s]\n",
@@ -3828,7 +3955,8 @@ static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx)
* yet) then verify that the cache is uptodate */
if (dctx->check_provider) {
ret = check_cache(dctx, nctx, dctx->res,
- SSS_DP_INITGROUPS, name, 0, NULL,
+ SSS_DP_INITGROUPS, name, 0,
+ cmdctx->name_is_upn ? EXTRA_NAME_IS_UPN : NULL,
nss_cmd_getby_dp_callback,
dctx);
if (ret != EOK) {
diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h
index 23bb6a8d3..e5f3bde50 100644
--- a/src/responder/nss/nsssrv_private.h
+++ b/src/responder/nss/nsssrv_private.h
@@ -31,6 +31,7 @@ struct nss_cmd_ctx {
struct cli_ctx *cctx;
enum sss_cli_command cmd;
char *name;
+ bool name_is_upn;
uint32_t id;
char *secid;
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index eae17341a..561bd3d96 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -882,7 +882,22 @@ static void pam_forwarder_cb(struct tevent_req *req)
pd = preq->pd;
ret = pam_forwarder_parse_data(cctx, pd);
- if (ret != EOK) {
+ if (ret == EAGAIN) {
+ if (strchr(preq->pd->logon_name, '@') == NULL) {
+ goto done;
+ }
+ /* Assuming Kerberos principal */
+ preq->domain = preq->cctx->rctx->domains;
+ preq->check_provider = NEED_CHECK_PROVIDER(preq->domain->provider);
+ preq->pd->user = talloc_strdup(preq->pd, preq->pd->logon_name);
+ if (preq->pd->user == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ preq->pd->name_is_upn = true;
+ preq->pd->domain = NULL;
+ } else if (ret != EOK) {
ret = EINVAL;
goto done;
}
@@ -916,12 +931,14 @@ static int pam_check_user_search(struct pam_auth_req *preq)
struct dp_callback_ctx *cb_ctx;
struct pam_ctx *pctx =
talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
- struct ldb_result *res;
+ static const char *user_attrs[] = SYSDB_PW_ATTRS;
+ struct ldb_message *msg;
while (dom) {
/* if it is a domainless search, skip domains that require fully
* qualified names instead */
- while (dom && !preq->pd->domain && dom->fqnames) {
+ while (dom && !preq->pd->domain && !preq->pd->name_is_upn
+ && dom->fqnames) {
dom = get_next_domain(dom, false);
}
@@ -979,20 +996,18 @@ static int pam_check_user_search(struct pam_auth_req *preq)
return EFAULT;
}
- ret = sysdb_getpwnam(preq, dom, name, &res);
- if (ret != EOK) {
+ if (preq->pd->name_is_upn) {
+ ret = sysdb_search_user_by_upn(preq, dom, name, user_attrs, &msg);
+ } else {
+ ret = sysdb_search_user_by_name(preq, dom, name, user_attrs, &msg);
+ }
+ if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to make request to our cache!\n");
return EIO;
}
- if (res->count > 1) {
- DEBUG(SSSDBG_FATAL_FAILURE,
- "getpwnam call returned more than one result !?!\n");
- return ENOENT;
- }
-
- if (res->count == 0) {
+ if (ret == ENOENT) {
if (preq->check_provider == false) {
/* set negative cache only if not result of cache check */
ret = sss_ncache_set_user(pctx->ncache, false, dom, name);
@@ -1021,7 +1036,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
/* if we need to check the remote account go on */
if (preq->check_provider) {
- cacheExpire = ldb_msg_find_attr_as_uint64(res->msgs[0],
+ cacheExpire = ldb_msg_find_attr_as_uint64(msg,
SYSDB_CACHE_EXPIRE, 0);
if (cacheExpire < time(NULL)) {
break;
@@ -1032,7 +1047,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
"Returning info for user [%s@%s]\n", name, dom->name);
/* We might have searched by alias. Pass on the primary name */
- ret = pd_set_primary_name(res->msgs[0], preq->pd);
+ ret = pd_set_primary_name(msg, preq->pd);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Could not canonicalize username\n");
return ret;
@@ -1054,8 +1069,8 @@ static int pam_check_user_search(struct pam_auth_req *preq)
preq->check_provider = false;
dpreq = sss_dp_get_account_send(preq, preq->cctx->rctx,
- dom, false, SSS_DP_INITGROUPS,
- name, 0, NULL);
+ dom, false, SSS_DP_INITGROUPS, name, 0,
+ preq->pd->name_is_upn ? EXTRA_NAME_IS_UPN : NULL);
if (!dpreq) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Out of memory sending data provider request\n");