diff options
Diffstat (limited to 'src/responder/nss/nss_protocol_sid.c')
-rw-r--r-- | src/responder/nss/nss_protocol_sid.c | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/src/responder/nss/nss_protocol_sid.c b/src/responder/nss/nss_protocol_sid.c new file mode 100644 index 0000000..69d61bb --- /dev/null +++ b/src/responder/nss/nss_protocol_sid.c @@ -0,0 +1,704 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 "util/crypto/sss_crypto.h" +#include "responder/nss/nss_protocol.h" + +static errno_t +find_sss_id_type(struct ldb_message *msg, + bool mpg, + enum sss_id_type *id_type) +{ + size_t c; + struct ldb_message_element *el; + struct ldb_val *val = NULL; + + el = ldb_msg_find_element(msg, SYSDB_OBJECTCATEGORY); + if (el == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Objectcategory attribute not found.\n"); + return EINVAL; + } + + for (c = 0; c < el->num_values; c++) { + val = &(el->values[c]); + if (strncasecmp(SYSDB_USER_CLASS, + (char *)val->data, val->length) == 0) { + break; + } + } + + if (c == el->num_values) { + *id_type = SSS_ID_TYPE_GID; + } else { + if (mpg) { + *id_type = SSS_ID_TYPE_BOTH; + } else { + *id_type = SSS_ID_TYPE_UID; + } + } + + return EOK; +} + +static errno_t +sss_nss_get_id_type(struct sss_nss_cmd_ctx *cmd_ctx, + struct cache_req_result *result, + enum sss_id_type *_type) +{ + errno_t ret; + bool mpg; + + /* Well known objects are always groups. */ + if (result->well_known_object) { + *_type = SSS_ID_TYPE_GID; + return EOK; + } + + mpg = sss_domain_is_mpg(result->domain) || sss_domain_is_hybrid(result->domain); + ret = find_sss_id_type(result->msgs[0], mpg, _type); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Unable to find ID type [%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + + return EOK; +} + +static errno_t +sss_nss_get_sid_id_type(struct sss_nss_cmd_ctx *cmd_ctx, + struct cache_req_result *result, + const char **_sid, + uint64_t *_id, + enum sss_id_type *_type) +{ + errno_t ret; + size_t c; + const char *tmp; + const char *user_sid = NULL; + const char *group_sid = NULL; + uint64_t user_uid = 0; + uint64_t user_gid = 0; + uint64_t group_gid = 0; + enum sss_id_type ltype; + + if (result->count == 1) { + *_sid = ldb_msg_find_attr_as_string(result->msgs[0], + SYSDB_SID_STR, NULL); + if (*_sid == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing SID.\n"); + return EINVAL; + } + ret = sss_nss_get_id_type(cmd_ctx, result, _type); + if (ret == EOK ) { + if (*_type == SSS_ID_TYPE_GID) { + *_id = ldb_msg_find_attr_as_uint64(result->msgs[0], + SYSDB_GIDNUM, 0); + } else { + *_id = ldb_msg_find_attr_as_uint64(result->msgs[0], + SYSDB_UIDNUM, 0); + } + } + return ret; + } + + for (c = 0; c < result->count; c++) { + ret = find_sss_id_type(result->msgs[c], + false /* we are only interested in the type + * of the object, so mpg setting can + * be ignored */, + <ype); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Unable to find ID type, ignored [%d][%s].\n", + ret, sss_strerror(ret)); + continue; + } + + tmp = ldb_msg_find_attr_as_string(result->msgs[c], + SYSDB_SID_STR, NULL); + if (tmp == NULL) { + continue; + } + + if (ltype == SSS_ID_TYPE_GID) { + if (tmp != NULL && group_sid != NULL) { + DEBUG(SSSDBG_OP_FAILURE, + "Search for SID found multiple groups with SIDs: %s, %s;" + " request failed.\n", tmp, group_sid); + return EINVAL; + } + group_sid = tmp; + group_gid = ldb_msg_find_attr_as_uint64(result->msgs[c], + SYSDB_GIDNUM, 0); + } else { + if (tmp != NULL && user_sid != NULL) { + DEBUG(SSSDBG_OP_FAILURE, + "Search for SID found multiple users with SIDs: %s, %s; " + "request failed.\n", tmp, user_sid); + return EINVAL; + } + user_sid = tmp; + user_uid = ldb_msg_find_attr_as_uint64(result->msgs[c], + SYSDB_UIDNUM, 0); + user_gid = ldb_msg_find_attr_as_uint64(result->msgs[c], + SYSDB_GIDNUM, 0); + } + } + + if (user_sid == NULL && group_sid == NULL) { + /* No SID in the results */ + return ENOENT; + } else if (user_sid != NULL && group_sid == NULL) { + /* There is only one user with a SID in the results */ + *_sid = user_sid; + *_id = user_uid; + *_type = SSS_ID_TYPE_UID; + } else if (user_sid == NULL && group_sid != NULL) { + /* There is only one group with a SID in the results */ + *_sid = group_sid; + *_id = group_gid; + *_type = SSS_ID_TYPE_GID; + } else if (user_sid != NULL && group_sid != NULL && user_uid != 0 + && user_uid == user_gid && user_gid == group_gid) { + /* Manually created user-private-group */ + *_sid = user_sid; + *_id = user_uid; + *_type = SSS_ID_TYPE_UID; + } else { + DEBUG(SSSDBG_OP_FAILURE, + "Found user with SID [%s] and group with SID [%s] during a " + "single request, cannot handle this case.\n", + user_sid, group_sid); + /* Unrelated user and group both with SIDs are returned, we cannot + * handle this case. */ + return EINVAL; + } + + return EOK; +} + +errno_t +sss_nss_protocol_fill_sid(struct sss_nss_ctx *nss_ctx, + struct sss_nss_cmd_ctx *cmd_ctx, + struct sss_packet *packet, + struct cache_req_result *result) +{ + struct sized_string sz_sid; + enum sss_id_type id_type; + const char *sid; + uint64_t id; + size_t rp = 0; + size_t body_len; + uint8_t *body; + errno_t ret; + + ret = sss_nss_get_sid_id_type(cmd_ctx, result, &sid, &id, &id_type); + if (ret != EOK) { + return ret; + } + + to_sized_string(&sz_sid, sid); + + ret = sss_packet_grow(packet, sz_sid.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, &body_len); + + SAFEALIGN_SET_UINT32(&body[rp], 1, &rp); /* Num results. */ + SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */ + SAFEALIGN_SET_UINT32(&body[rp], id_type, &rp); + SAFEALIGN_SET_STRING(&body[rp], sz_sid.str, sz_sid.len, &rp); + + if (nss_ctx->sid_mc_ctx != NULL) { + /* no need to check for SSS_NSS_EX_FLAG_INVALIDATE_CACHE since + * SID related requests don't support 'flags' + */ + if (id == 0 || id >= UINT32_MAX) { + DEBUG(SSSDBG_OP_FAILURE, "Invalid POSIX ID %lu\n", id); + return EOK; + } + ret = sss_mmap_cache_sid_store(&nss_ctx->sid_mc_ctx, &sz_sid, + (uint32_t)id, id_type, + cmd_ctx->type != CACHE_REQ_OBJECT_BY_ID); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to store SID='%s' / ID=%lu in mmap cache [%d]: %s!\n", + sz_sid.str, id, ret, sss_strerror(ret)); + } + } + + return EOK; +} + +static errno_t process_attr_list(TALLOC_CTX *mem_ctx, struct ldb_message *msg, + const char **attr_list, + struct sized_string **_keys, + struct sized_string **_vals, + size_t *array_size, size_t *sum, + size_t *found) +{ + size_t c; + size_t d; + struct sized_string *keys; + struct sized_string *vals; + struct ldb_val val; + struct ldb_message_element *el; + bool use_base64; + + keys = *_keys; + vals = *_vals; + + for (c = 0; attr_list[c] != NULL; c++) { + el = ldb_msg_find_element(msg, attr_list[c]); + if (el != NULL && el->num_values > 0) { + if (el->num_values > 1) { + *array_size += el->num_values; + keys = talloc_realloc(mem_ctx, keys, struct sized_string, + *array_size); + vals = talloc_realloc(mem_ctx, vals, struct sized_string, + *array_size); + if (keys == NULL || vals == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n"); + return ENOMEM; + } + } + + use_base64 = false; + if (strcmp(attr_list[c], SYSDB_USER_CERT) == 0) { + use_base64 = true; + } + + for (d = 0; d < el->num_values; d++) { + to_sized_string(&keys[*found], attr_list[c]); + *sum += keys[*found].len; + if (use_base64) { + val.data = (uint8_t *)sss_base64_encode(vals, + el->values[d].data, + el->values[d].length); + if (val.data != NULL) { + val.length = strlen((char *)val.data); + } + } else { + val = el->values[d]; + } + + if (val.data == NULL || val.data[val.length] != '\0') { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unexpected attribute value found for [%s].\n", + attr_list[c]); + return EINVAL; + } + to_sized_string(&vals[*found], (const char *)val.data); + *sum += vals[*found].len; + + (*found)++; + } + } + } + + *_keys = keys; + *_vals = vals; + + return EOK; +} + +errno_t +sss_nss_protocol_fill_orig(struct sss_nss_ctx *nss_ctx, + struct sss_nss_cmd_ctx *cmd_ctx, + struct sss_packet *packet, + struct cache_req_result *result) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_message *msg = result->msgs[0]; + const char **full_attrs = NULL; + enum sss_id_type id_type; + struct sized_string *keys; + struct sized_string *vals; + size_t full_attrs_count = 0; + size_t array_size; + size_t sum; + size_t found; + size_t i; + size_t rp = 0; + size_t body_len; + uint8_t *body; + errno_t ret; + + if (result->count != 1) { + DEBUG(SSSDBG_OP_FAILURE, + "Unexpected number of results [%u], expected [1].\n", + result->count); + return EINVAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sss_nss_get_id_type(cmd_ctx, result, &id_type); + if (ret != EOK) { + return ret; + } + + if (nss_ctx->full_attribute_list != NULL) { + full_attrs = nss_ctx->full_attribute_list; + for (full_attrs_count = 0; + full_attrs[full_attrs_count] != NULL; + full_attrs_count++); + } + + array_size = full_attrs_count; + keys = talloc_array(tmp_ctx, struct sized_string, array_size); + vals = talloc_array(tmp_ctx, struct sized_string, array_size); + if (keys == NULL || vals == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n"); + ret = ENOMEM; + goto done; + } + + sum = 0; + found = 0; + + if (full_attrs_count != 0) { + ret = process_attr_list(tmp_ctx, msg, full_attrs, &keys, &vals, + &array_size, &sum, &found); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "process_attr_list failed.\n"); + goto done; + } + } + + ret = sss_packet_grow(packet, sum + 3 * sizeof(uint32_t)); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n"); + goto done; + } + + sss_packet_get_body(packet, &body, &body_len); + SAFEALIGN_SETMEM_UINT32(&body[rp], 1, &rp); /* Num results */ + SAFEALIGN_SETMEM_UINT32(&body[rp], 0, &rp); /* reserved */ + SAFEALIGN_COPY_UINT32(&body[rp], &id_type, &rp); + for (i = 0; i < found; i++) { + SAFEALIGN_SET_STRING(&body[rp], keys[i].str, keys[i].len, &rp); + SAFEALIGN_SET_STRING(&body[rp], vals[i].str, vals[i].len, &rp); + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +sss_nss_get_well_known_name(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct cache_req_result *result, + struct sized_string **_sz_name) +{ + struct sized_string *sz_name; + const char *fq_name = NULL; + const char *domname; + const char *name; + + name = result->lookup_name; + if (name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing name.\n"); + return EINVAL; + } + + sz_name = talloc_zero(mem_ctx, struct sized_string); + if (sz_name == NULL) { + return ENOMEM; + } + + domname = result->domain != NULL + ? result->domain->name + : result->well_known_domain; + + if (domname != NULL) { + fq_name = sss_tc_fqname2(sz_name, rctx->global_names, + domname, domname, name); + if (fq_name == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Conversion to fqname failed.\n"); + talloc_free(sz_name); + return ENOMEM; + } + + name = fq_name; + } + + to_sized_string(sz_name, name); + + *_sz_name = sz_name; + + return EOK; +} + +static errno_t +sss_nss_get_ad_name(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct cache_req_result *result, + struct sized_string **_sz_name) +{ + struct ldb_message *msg = result->msgs[0]; + const char *name; + errno_t ret; + + if (result->well_known_object) { + return sss_nss_get_well_known_name(mem_ctx, rctx, result, _sz_name); + } + + name = ldb_msg_find_attr_as_string(msg, ORIGINALAD_PREFIX SYSDB_NAME, + NULL); + if (name == NULL) { + name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + } + + if (name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing name.\n"); + return EINVAL; + } + + ret = sized_output_name(mem_ctx, rctx, name, result->domain, _sz_name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Unable to create sized name [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + return EOK; +} + +errno_t +sss_nss_protocol_fill_single_name(struct sss_nss_ctx *nss_ctx, + struct sss_nss_cmd_ctx *cmd_ctx, + struct sss_packet *packet, + struct cache_req_result *result) +{ + if (result->ldb_result->count > 1) { + DEBUG(SSSDBG_TRACE_FUNC, "Lookup returned more than one result " + "but only one was expected.\n"); + return EEXIST; + } + + return sss_nss_protocol_fill_name(nss_ctx, cmd_ctx, packet, result); +} + +errno_t +sss_nss_protocol_fill_name(struct sss_nss_ctx *nss_ctx, + struct sss_nss_cmd_ctx *cmd_ctx, + struct sss_packet *packet, + struct cache_req_result *result) +{ + struct sized_string *sz_name; + enum sss_id_type id_type; + size_t rp = 0; + size_t body_len; + uint8_t *body; + errno_t ret; + + ret = sss_nss_get_id_type(cmd_ctx, result, &id_type); + if (ret != EOK) { + return ret; + } + + ret = sss_nss_get_ad_name(cmd_ctx, nss_ctx->rctx, result, &sz_name); + if (ret != EOK) { + return ret; + } + + ret = sss_packet_grow(packet, sz_name->len + 3 * sizeof(uint32_t)); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n"); + talloc_free(sz_name); + return ret; + } + + sss_packet_get_body(packet, &body, &body_len); + + SAFEALIGN_SET_UINT32(&body[rp], 1, &rp); /* Num results. */ + SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */ + SAFEALIGN_SET_UINT32(&body[rp], id_type, &rp); + SAFEALIGN_SET_STRING(&body[rp], sz_name->str, sz_name->len, &rp); + + talloc_free(sz_name); + + return EOK; +} + +errno_t +sss_nss_protocol_fill_id(struct sss_nss_ctx *nss_ctx, + struct sss_nss_cmd_ctx *cmd_ctx, + struct sss_packet *packet, + struct cache_req_result *result) +{ + struct ldb_message *msg = result->msgs[0]; + enum sss_id_type id_type; + uint64_t id64; + uint32_t id; + const char *sid = NULL; + struct sized_string sid_key; + size_t rp = 0; + size_t body_len; + uint8_t *body; + errno_t ret; + + if (result->ldb_result == NULL) { + /* This was a well known SID. This is currently unsupported with id. */ + return EINVAL; + } + + ret = sss_nss_get_id_type(cmd_ctx, result, &id_type); + if (ret != EOK) { + return ret; + } + + if (id_type == SSS_ID_TYPE_GID) { + id64 = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0); + } else { + id64 = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0); + } + + if (id64 == 0 || id64 >= UINT32_MAX) { + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid POSIX ID.\n"); + return EINVAL; + } + + id = (uint32_t)id64; + + ret = sss_packet_grow(packet, 4 * sizeof(uint32_t)); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n"); + return ret; + } + + sss_packet_get_body(packet, &body, &body_len); + + SAFEALIGN_SET_UINT32(&body[rp], 1, &rp); /* Num results. */ + SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */ + SAFEALIGN_SET_UINT32(&body[rp], id_type, &rp); + SAFEALIGN_SET_UINT32(&body[rp], id, &rp); + + if (nss_ctx->sid_mc_ctx != NULL) { + /* no need to check for SSS_NSS_EX_FLAG_INVALIDATE_CACHE since + * SID related requests don't support 'flags' + */ + sid = ldb_msg_find_attr_as_string(msg, SYSDB_SID_STR, NULL); + if (!sid) { + DEBUG(SSSDBG_OP_FAILURE, "Missing SID?!\n"); + return EOK; + } + to_sized_string(&sid_key, sid); + ret = sss_mmap_cache_sid_store(&nss_ctx->sid_mc_ctx, &sid_key, id, id_type, + cmd_ctx->type != CACHE_REQ_OBJECT_BY_ID); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to store SID='%s' / ID=%d in mmap cache [%d]: %s!\n", + sid, id, ret, sss_strerror(ret)); + } + } + + return EOK; +} + +errno_t +sss_nss_protocol_fill_name_list_all_domains(struct sss_nss_ctx *nss_ctx, + struct sss_nss_cmd_ctx *cmd_ctx, + struct sss_packet *packet, + struct cache_req_result **results) +{ + enum sss_id_type *id_types; + size_t rp = 0; + size_t body_len; + uint8_t *body; + errno_t ret; + struct sized_string *sz_names; + size_t len; + size_t c; + const char *tmp_str; + size_t d; + size_t total = 0; + size_t iter = 0; + + if (results == NULL) { + return EINVAL; + } + + for (d = 0; results[d] != NULL; d++) { + total += results[d]->count; + } + + sz_names = talloc_array(cmd_ctx, struct sized_string, total); + if (sz_names == NULL) { + return ENOMEM; + } + + id_types = talloc_array(cmd_ctx, enum sss_id_type, total); + if (id_types == NULL) { + return ENOMEM; + } + + len = 0; + for (d = 0; results[d] != NULL; d++) { + for (c = 0; c < results[d]->count; c++) { + ret = sss_nss_get_id_type(cmd_ctx, results[d], &(id_types[iter])); + if (ret != EOK) { + return ret; + } + + tmp_str = sss_get_name_from_msg(results[d]->domain, + results[d]->msgs[c]); + if (tmp_str == NULL) { + return EINVAL; + } + to_sized_string(&(sz_names[iter]), tmp_str); + + len += sz_names[iter].len; + iter++; + } + } + + len += (2 + total) * sizeof(uint32_t); + + ret = sss_packet_grow(packet, len); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n"); + return ret; + } + + sss_packet_get_body(packet, &body, &body_len); + + SAFEALIGN_SET_UINT32(&body[rp], total, &rp); /* Num results. */ + SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */ + for (c = 0; c < total; c++) { + SAFEALIGN_SET_UINT32(&body[rp], id_types[c], &rp); + SAFEALIGN_SET_STRING(&body[rp], sz_names[c].str, sz_names[c].len, + &rp); + } + + return EOK; +} |