summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2013-07-22 09:55:28 +0200
committerJakub Hrozek <jhrozek@redhat.com>2013-08-02 09:06:35 +0200
commit97263498f47e0225c37b3f98476f03dcd34d9b86 (patch)
tree395d8f14a8333358cbe48c6d3cdf70a4c1cdfe3a
parent31c7e4f601e0085dd9feb98a885e4fdcfff50fe3 (diff)
downloadsssd-97263498f47e0225c37b3f98476f03dcd34d9b86.tar.gz
sssd-97263498f47e0225c37b3f98476f03dcd34d9b86.tar.xz
sssd-97263498f47e0225c37b3f98476f03dcd34d9b86.zip
NSS responder: interface to query custom attributes
-rw-r--r--src/responder/nss/nsssrv_cmd.c354
-rw-r--r--src/responder/nss/nsssrv_private.h1
2 files changed, 339 insertions, 16 deletions
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 62a564a41..2879de613 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -491,6 +491,59 @@ static int nss_cmd_getpw_send_reply(struct nss_dom_ctx *dctx, bool filter)
return EOK;
}
+static int get_user_attr_parse(uint8_t *body, size_t blen, const char **_rawname,
+ size_t *_namelen, uint8_t **_pvt)
+{
+ const char *rawname = NULL;
+ uint8_t *pvt = NULL;
+ size_t bp;
+ uint32_t len, namelen, pvtlen;
+
+ /* The packet contains:
+ * total packet length (32 bytes)
+ * length of the username (32 bytes)
+ * username (zero terminated)
+ * length of the attribute name (32 bytes)
+ * attribute name (zero terminated)
+ */
+ bp = 0;
+ SAFEALIGN_BUF2VAR_UINT32(&len, body+bp, &bp);
+ if (len > blen) {
+ return EIO;
+ }
+
+ SAFEALIGN_BUF2VAR_UINT32(&namelen, body+bp, &bp);
+ if (namelen > len - bp) {
+ return EIO;
+ }
+
+ if (namelen == 0) {
+ /* No name? Punt. */
+ *_rawname = NULL;
+ return EINVAL;
+ }
+
+ if (body[namelen-1] != '\0') {
+ return EINVAL;
+ }
+
+ rawname = (const char *) body + bp;
+ bp += namelen;
+
+ SAFEALIGN_BUF2VAR_UINT32(&pvtlen, body+bp, &bp);
+ /* Sanity check if pvt data is within the boundaries */
+ if (pvtlen > len - bp) {
+ return EIO;
+ }
+
+ pvt = body + bp;
+
+ *_rawname = rawname;
+ *_namelen = namelen;
+ *_pvt = pvt;
+ return EOK;
+}
+
static void nsssrv_dp_send_acct_req_done(struct tevent_req *req);
/* FIXME: do not check res->count, but get in a msgs and check in parent */
@@ -820,6 +873,211 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
return ENOENT;
}
+static errno_t fill_attr(struct sss_packet *packet,
+ const char *attrname,
+ struct ldb_message *msg)
+{
+ const char *attr_val_str;
+ struct sized_string attr_val;
+ uint8_t *body;
+ size_t blen, bp;
+ errno_t ret;
+
+ attr_val_str = ldb_msg_find_attr_as_string(msg, attrname, NULL);
+ if (attr_val_str == NULL) {
+ return ENOENT;
+ }
+
+ to_sized_string(&attr_val, attr_val_str);
+
+ ret = sss_packet_grow(packet, attr_val.len + 3 * sizeof(uint32_t));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, ("sss_packet_grow failed.\n"));
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &blen);
+
+ bp = 0;
+ SAFEALIGN_VAR2BUF_UINT32(body+bp, 1, &bp); /* num results */
+ SAFEALIGN_VAR2BUF_UINT32(body+bp, 0, &bp); /* reserved */
+ SAFEALIGN_VAR2BUF_UINT32(body+bp, SSS_ID_TYPE_ATTR, &bp); /* id type */
+ memcpy(body + bp, attr_val.str, attr_val.len);
+ return EOK;
+}
+
+static errno_t nss_cmd_getattr_send_reply(struct nss_dom_ctx *dctx)
+{
+ struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
+ struct cli_ctx *cctx = cmdctx->cctx;
+ const char *attrname = (const char *) dctx->pvt;
+ errno_t ret;
+
+ if (dctx->res->count > 1) {
+ return EINVAL;
+ } else if (dctx->res->count == 0) {
+ return ENOENT;
+ }
+
+ ret = sss_packet_new(cctx->creq, 0,
+ sss_packet_get_cmd(cctx->creq->in),
+ &cctx->creq->out);
+ if (ret != EOK) {
+ return EFAULT;
+ }
+
+ ret = fill_attr(cctx->creq->out, attrname, dctx->res->msgs[0]);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ sss_packet_set_error(cctx->creq->out, EOK);
+ sss_cmd_done(cctx, cmdctx);
+ return EOK;
+}
+
+static int nss_cmd_getattr_search(struct nss_dom_ctx *dctx)
+{
+ struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
+ struct sss_domain_info *dom = dctx->domain;
+ struct cli_ctx *cctx = cmdctx->cctx;
+ char *name = NULL;
+ struct sysdb_ctx *sysdb;
+ struct nss_ctx *nctx;
+ int ret;
+ struct ldb_message *msg;
+ const char *attrs[] = { SYSDB_NAME,
+ (const char *) dctx->pvt,
+ NULL};
+
+ 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) {
+ dom = get_next_domain(dom, false);
+ }
+
+ if (!dom) break;
+
+ if (dom != dctx->domain) {
+ /* make sure we reset the check_provider flag when we check
+ * a new domain */
+ dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
+ }
+
+ /* make sure to update the dctx if we changed domain */
+ dctx->domain = dom;
+
+ talloc_free(name);
+ name = sss_get_cased_name(cmdctx, cmdctx->name, dom->case_sensitive);
+ if (!name) return ENOMEM;
+
+ /* verify this user has not yet been negatively cached,
+ * or has been permanently filtered */
+ ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
+ dom, name);
+
+ /* if neg cached, return we didn't find it */
+ if (ret == EEXIST) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ ("User [%s] does not exist in [%s]! (negative cache)\n",
+ name, dom->name));
+ /* if a multidomain search, try with next */
+ if (cmdctx->check_next) {
+ dom = get_next_domain(dom, false);
+ continue;
+ }
+ /* There are no further domains or this was a
+ * fully-qualified user request.
+ */
+ return ENOENT;
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA,
+ ("Requesting info for [%s@%s]\n", name, dom->name));
+
+ sysdb = dom->sysdb;
+ if (sysdb == NULL) {
+ DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
+ return EIO;
+ }
+
+ ret = sysdb_search_user_by_name(cmdctx, sysdb, dom, name, attrs, &msg);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to make request to our cache!\n"));
+ return EIO;
+ }
+
+ dctx->res = talloc_zero(cmdctx, struct ldb_result);
+ if (dctx->res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n"));
+ return ENOMEM;
+ }
+
+ if (ret == EOK) {
+ 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, msg);
+ }
+
+ if (dctx->res->count == 0 && !dctx->check_provider) {
+ /* set negative cache only if not result of cache check */
+ ret = sss_ncache_set_user(nctx->ncache, false, dom, name);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* if a multidomain search, try with next */
+ if (cmdctx->check_next) {
+ dom = get_next_domain(dom, false);
+ if (dom) continue;
+ }
+
+ DEBUG(2, ("No results for getpwnam call\n"));
+
+ /* User not found in ldb -> delete user from memory cache. */
+ ret = delete_entry_from_memcache(dctx->domain, name,
+ nctx->pwd_mc_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Deleting user from memcache failed.\n"));
+ }
+
+ return ENOENT;
+ }
+
+ /* if this is a caching provider (or if we haven't checked the cache
+ * yet) then verify that the cache is uptodate */
+ if (dctx->check_provider) {
+ ret = check_cache(dctx, nctx, dctx->res,
+ SSS_DP_USER, name, 0,
+ nss_cmd_getby_dp_callback,
+ dctx);
+ if (ret != EOK) {
+ /* Anything but EOK means we should reenter the mainloop
+ * because we may be refreshing the cache
+ */
+ return ret;
+ }
+ }
+
+ /* One result found */
+ DEBUG(6, ("Returning info for user [%s@%s]\n", name, dom->name));
+
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("No matching domain found for [%s], fail!\n", cmdctx->name));
+ return ENOENT;
+}
+
static int nss_cmd_getgrnam_search(struct nss_dom_ctx *dctx);
static int nss_cmd_getgr_send_reply(struct nss_dom_ctx *dctx, bool filter);
static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx);
@@ -870,6 +1128,9 @@ static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min,
case SSS_NSS_GETSIDBYID:
ret = nss_cmd_getbysid_send_reply(dctx);
break;
+ case SSS_NSS_GET_USER_ATTR:
+ ret = nss_cmd_getattr_send_reply(dctx);
+ break;
default:
DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command [%d].\n",
dctx->cmdctx->cmd));
@@ -960,6 +1221,12 @@ static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min,
ret = nss_cmd_getbysid_send_reply(dctx);
}
break;
+ case SSS_NSS_GET_USER_ATTR:
+ ret = nss_cmd_getattr_search(dctx);
+ if (ret == EOK) {
+ ret = nss_cmd_getattr_send_reply(dctx);
+ }
+ break;
default:
DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command [%d].\n",
dctx->cmdctx->cmd));
@@ -974,6 +1241,8 @@ done:
}
static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx);
+static int nss_cmd_getbynam_parse_inp(enum sss_cli_command cmd, struct cli_ctx *cctx,
+ const char **_rawname, uint8_t **_pvt);
static void nss_cmd_getbynam_done(struct tevent_req *req);
static int nss_cmd_getpwnam(struct cli_ctx *cctx)
{
@@ -987,9 +1256,8 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
const char *rawname;
+ uint8_t *pvt;
char *domname;
- uint8_t *body;
- size_t blen;
int ret;
switch(cmd) {
@@ -997,6 +1265,7 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
case SSS_NSS_GETGRNAM:
case SSS_NSS_INITGR:
case SSS_NSS_GETSIDBYNAME:
+ case SSS_NSS_GET_USER_ATTR:
break;
default:
DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command type [%d].\n", cmd));
@@ -1017,23 +1286,12 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
}
dctx->cmdctx = cmdctx;
- /* get user name to query */
- sss_packet_get_body(cctx->creq->in, &body, &blen);
-
- /* if not terminated fail */
- if (body[blen -1] != '\0') {
- ret = EINVAL;
- goto done;
- }
-
- /* If the body isn't valid UTF-8, fail */
- if (!sss_utf8_check(body, blen -1)) {
- ret = EINVAL;
+ ret = nss_cmd_getbynam_parse_inp(cmd, cctx, &rawname, &pvt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, ("Cannot parse input\n"));
goto done;
}
- rawname = (const char *)body;
-
DEBUG(SSSDBG_TRACE_FUNC, ("Running command [%d] with input [%s].\n",
dctx->cmdctx->cmd, rawname));
@@ -1047,6 +1305,7 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
ret = ENOMEM;
} else {
dctx->rawname = rawname;
+ dctx->pvt = pvt;
tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx);
ret = EAGAIN;
}
@@ -1069,6 +1328,7 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
} else {
/* this is a multidomain search */
dctx->rawname = rawname;
+ dctx->pvt = pvt;
dctx->domain = cctx->rctx->domains;
cmdctx->check_next = true;
if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
@@ -1114,6 +1374,12 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
ret = nss_cmd_getbysid_send_reply(dctx);
}
break;
+ case SSS_NSS_GET_USER_ATTR:
+ ret = nss_cmd_getattr_search(dctx);
+ if (ret == EOK) {
+ ret = nss_cmd_getattr_send_reply(dctx);
+ }
+ break;
default:
DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command [%d].\n",
dctx->cmdctx->cmd));
@@ -1124,6 +1390,56 @@ done:
return nss_cmd_done(cmdctx, ret);
}
+static int nss_cmd_getbynam_parse_inp(enum sss_cli_command cmd, struct cli_ctx *cctx,
+ const char **_rawname, uint8_t **_pvt)
+{
+ uint8_t *body;
+ size_t blen;
+ size_t namelen;
+ const char *rawname;
+ uint8_t *pvt = NULL;
+ errno_t ret;
+
+ /* get user name to query */
+ sss_packet_get_body(cctx->creq->in, &body, &blen);
+
+ /* if not terminated fail */
+ if (body[blen -1] != '\0') {
+ return EINVAL;
+ }
+
+ switch(cmd) {
+ case SSS_NSS_GETPWNAM:
+ case SSS_NSS_GETGRNAM:
+ case SSS_NSS_INITGR:
+ case SSS_NSS_GETSIDBYNAME:
+ rawname = (const char *)body;
+ namelen = blen;
+ pvt = NULL;
+ break;
+ case SSS_NSS_GET_USER_ATTR:
+ ret = get_user_attr_parse(body, blen, &rawname, &namelen, &pvt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("Maloformed packet for command %d\n", cmd));
+ return EINVAL;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command type [%d].\n", cmd));
+ return EINVAL;
+ }
+
+ /* If the name isn't valid UTF-8, fail */
+ if (!sss_utf8_check((const uint8_t *) rawname, namelen -1)) {
+ return EINVAL;
+ }
+
+ *_pvt = pvt;
+ *_rawname = rawname;
+ return EOK;
+}
+
static void nss_cmd_getbynam_done(struct tevent_req *req)
{
struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
@@ -4255,6 +4571,11 @@ static int nss_cmd_getidbysid(struct cli_ctx *cctx)
return nss_cmd_getbysid(SSS_NSS_GETIDBYSID, cctx);
}
+static int nss_cmd_get_user_attr(struct cli_ctx *cctx)
+{
+ return nss_cmd_getbynam(SSS_NSS_GET_USER_ATTR, cctx);
+}
+
struct cli_protocol_version *register_cli_protocol_version(void)
{
static struct cli_protocol_version nss_cli_protocol_version[] = {
@@ -4290,6 +4611,7 @@ static struct sss_cmd_table nss_cmds[] = {
{SSS_NSS_GETSIDBYID, nss_cmd_getsidbyid},
{SSS_NSS_GETNAMEBYSID, nss_cmd_getnamebysid},
{SSS_NSS_GETIDBYSID, nss_cmd_getidbysid},
+ {SSS_NSS_GET_USER_ATTR, nss_cmd_get_user_attr},
{SSS_CLI_NULL, NULL}
};
diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h
index 04f1330c6..772e31d3e 100644
--- a/src/responder/nss/nsssrv_private.h
+++ b/src/responder/nss/nsssrv_private.h
@@ -67,6 +67,7 @@ struct nss_dom_ctx {
/* For a case when we are discovering subdomains */
const char *rawname;
+ const uint8_t *pvt;
bool check_provider;