diff options
Diffstat (limited to 'source4/winbind')
37 files changed, 7548 insertions, 0 deletions
diff --git a/source4/winbind/config.mk b/source4/winbind/config.mk new file mode 100644 index 0000000000..b5eb2c23f0 --- /dev/null +++ b/source4/winbind/config.mk @@ -0,0 +1,74 @@ +# server subsystem + +####################### +# Start SUBSYSTEM WINBIND +[MODULE::WINBIND] +INIT_FUNCTION = server_service_winbind_init +SUBSYSTEM = smbd +PRIVATE_DEPENDENCIES = \ + WB_HELPER \ + IDMAP \ + NDR_WINBIND \ + process_model \ + RPC_NDR_LSA \ + dcerpc_samr \ + PAM_ERRORS \ + LIBCLI_LDAP +# End SUBSYSTEM WINBIND +####################### + +WINBIND_OBJ_FILES = $(addprefix $(winbindsrcdir)/, \ + wb_server.o \ + wb_irpc.o \ + wb_samba3_protocol.o \ + wb_samba3_cmd.o \ + wb_init_domain.o \ + wb_dom_info.o \ + wb_dom_info_trusted.o \ + wb_sid2domain.o \ + wb_name2domain.o \ + wb_sids2xids.o \ + wb_xids2sids.o \ + wb_gid2sid.o \ + wb_sid2uid.o \ + wb_sid2gid.o \ + wb_uid2sid.o \ + wb_connect_lsa.o \ + wb_connect_sam.o \ + wb_cmd_lookupname.o \ + wb_cmd_lookupsid.o \ + wb_cmd_getdcname.o \ + wb_cmd_getpwnam.o \ + wb_cmd_getpwuid.o \ + wb_cmd_userdomgroups.o \ + wb_cmd_usersids.o \ + wb_cmd_list_trustdom.o \ + wb_cmd_list_users.o \ + wb_cmd_setpwent.o \ + wb_cmd_getpwent.o \ + wb_pam_auth.o \ + wb_sam_logon.o) + +$(eval $(call proto_header_template,$(winbindsrcdir)/wb_proto.h,$(WINBIND_OBJ_FILES:.o=.c))) + +################################################ +# Start SUBYSTEM WB_HELPER +[SUBSYSTEM::WB_HELPER] +PUBLIC_DEPENDENCIES = RPC_NDR_LSA dcerpc_samr +# End SUBSYSTEM WB_HELPER +################################################ + +WB_HELPER_OBJ_FILES = $(addprefix $(winbindsrcdir)/, wb_async_helpers.o wb_utils.o) + +$(eval $(call proto_header_template,$(winbindsrcdir)/wb_helper.h,$(WB_HELPER_OBJ_FILES:.o=.c))) + +################################################ +# Start SUBYSTEM IDMAP +[SUBSYSTEM::IDMAP] +PUBLIC_DEPENDENCIES = SAMDB_COMMON +# End SUBSYSTEM IDMAP +################################################ + +IDMAP_OBJ_FILES = $(winbindsrcdir)/idmap.o + +$(eval $(call proto_header_template,$(winbindsrcdir)/idmap_proto.h,$(IDMAP_OBJ_FILES:.o=.c))) diff --git a/source4/winbind/idmap.c b/source4/winbind/idmap.c new file mode 100644 index 0000000000..333a86445a --- /dev/null +++ b/source4/winbind/idmap.c @@ -0,0 +1,718 @@ +/* + Unix SMB/CIFS implementation. + + Map SIDs to unixids and back + + Copyright (C) Kai Blin 2008 + + 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 "includes.h" +#include "auth/auth.h" +#include "librpc/gen_ndr/lsa.h" +#include "librpc/gen_ndr/samr.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "lib/ldb/include/ldb.h" +#include "lib/ldb/include/ldb_errors.h" +#include "lib/ldb_wrap.h" +#include "param/param.h" +#include "winbind/idmap.h" +#include "libcli/security/proto.h" +#include "libcli/ldap/ldap_ndr.h" + +/** + * Get uid/gid bounds from idmap database + * + * \param idmap_ctx idmap context to use + * \param low lower uid/gid bound is stored here + * \param high upper uid/gid bound is stored here + * \return 0 on success, nonzero on failure + */ +static int idmap_get_bounds(struct idmap_context *idmap_ctx, uint32_t *low, + uint32_t *high) +{ + int ret = -1; + struct ldb_context *ldb = idmap_ctx->ldb_ctx; + struct ldb_dn *dn; + struct ldb_result *res = NULL; + TALLOC_CTX *tmp_ctx = talloc_new(idmap_ctx); + uint32_t lower_bound = (uint32_t) -1; + uint32_t upper_bound = (uint32_t) -1; + + dn = ldb_dn_new(tmp_ctx, ldb, "CN=CONFIG"); + if (dn == NULL) goto failed; + + ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &res); + if (ret != LDB_SUCCESS) goto failed; + + talloc_steal(tmp_ctx, res); + + if (res->count != 1) { + ret = -1; + goto failed; + } + + lower_bound = ldb_msg_find_attr_as_uint(res->msgs[0], "lowerBound", -1); + if (lower_bound != (uint32_t) -1) { + ret = LDB_SUCCESS; + } else { + ret = -1; + goto failed; + } + + upper_bound = ldb_msg_find_attr_as_uint(res->msgs[0], "upperBound", -1); + if (upper_bound != (uint32_t) -1) { + ret = LDB_SUCCESS; + } else { + ret = -1; + } + +failed: + talloc_free(tmp_ctx); + *low = lower_bound; + *high = upper_bound; + return ret; +} + +/** + * Add a dom_sid structure to a ldb_message + * \param idmap_ctx idmap context to use + * \param mem_ctx talloc context to use + * \param ldb_message ldb message to add dom_sid to + * \param attr_name name of the attribute to store the dom_sid in + * \param sid dom_sid to store + * \return 0 on success, an ldb error code on failure. + */ +static int idmap_msg_add_dom_sid(struct idmap_context *idmap_ctx, + TALLOC_CTX *mem_ctx, struct ldb_message *msg, + const char *attr_name, const struct dom_sid *sid) +{ + struct ldb_val val; + enum ndr_err_code ndr_err; + + ndr_err = ndr_push_struct_blob(&val, mem_ctx, + lp_iconv_convenience(idmap_ctx->lp_ctx), + sid, + (ndr_push_flags_fn_t)ndr_push_dom_sid); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return -1; + } + + return ldb_msg_add_value(msg, attr_name, &val, NULL); +} + +/** + * Get a dom_sid structure from a ldb message. + * + * \param mem_ctx talloc context to allocate dom_sid memory in + * \param msg ldb_message to get dom_sid from + * \param attr_name key that has the dom_sid as data + * \return dom_sid structure on success, NULL on failure + */ +static struct dom_sid *idmap_msg_get_dom_sid(TALLOC_CTX *mem_ctx, + struct ldb_message *msg, const char *attr_name) +{ + struct dom_sid *sid; + const struct ldb_val *val; + enum ndr_err_code ndr_err; + + val = ldb_msg_find_ldb_val(msg, attr_name); + if (val == NULL) { + return NULL; + } + + sid = talloc(mem_ctx, struct dom_sid); + if (sid == NULL) { + return NULL; + } + + ndr_err = ndr_pull_struct_blob(val, sid, NULL, sid, + (ndr_pull_flags_fn_t)ndr_pull_dom_sid); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(sid); + return NULL; + } + + return sid; +} + +/** + * Initialize idmap context + * + * talloc_free to close. + * + * \param mem_ctx talloc context to use. + * \return allocated idmap_context on success, NULL on error + */ +struct idmap_context *idmap_init(TALLOC_CTX *mem_ctx, + struct event_context *ev_ctx, + struct loadparm_context *lp_ctx) +{ + struct idmap_context *idmap_ctx; + + idmap_ctx = talloc(mem_ctx, struct idmap_context); + if (idmap_ctx == NULL) { + return NULL; + } + + idmap_ctx->lp_ctx = lp_ctx; + + idmap_ctx->ldb_ctx = ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx, + lp_idmap_url(lp_ctx), + system_session(mem_ctx, lp_ctx), + NULL, 0, NULL); + if (idmap_ctx->ldb_ctx == NULL) { + return NULL; + } + + idmap_ctx->unix_groups_sid = dom_sid_parse_talloc(mem_ctx, "S-1-22-2"); + if (idmap_ctx->unix_groups_sid == NULL) { + return NULL; + } + + idmap_ctx->unix_users_sid = dom_sid_parse_talloc(mem_ctx, "S-1-22-1"); + if (idmap_ctx->unix_users_sid == NULL) { + return NULL; + } + + return idmap_ctx; +} + +/** + * Convert an unixid to the corresponding SID + * + * \param idmap_ctx idmap context to use + * \param mem_ctx talloc context the memory for the struct dom_sid is allocated + * from. + * \param unixid pointer to a unixid struct to convert + * \param sid pointer that will take the struct dom_sid pointer if the mapping + * succeeds. + * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping not + * possible or some other NTSTATUS that is more descriptive on failure. + */ + +NTSTATUS idmap_xid_to_sid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx, + const struct unixid *unixid, struct dom_sid **sid) +{ + int ret; + NTSTATUS status = NT_STATUS_NONE_MAPPED; + struct ldb_context *ldb = idmap_ctx->ldb_ctx; + struct ldb_result *res = NULL; + struct dom_sid *unix_sid, *new_sid; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + const char *id_type; + + switch (unixid->type) { + case ID_TYPE_UID: + id_type = "ID_TYPE_UID"; + break; + case ID_TYPE_GID: + id_type = "ID_TYPE_GID"; + break; + default: + DEBUG(1, ("unixid->type must be type gid or uid\n")); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, + NULL, "(&(|(type=ID_TYPE_BOTH)(type=%s))" + "(xidNumber=%u))", id_type, unixid->id); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb))); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + if (res->count == 1) { + *sid = idmap_msg_get_dom_sid(mem_ctx, res->msgs[0], + "objectSid"); + if (*sid == NULL) { + DEBUG(1, ("Failed to get sid from db: %u\n", ret)); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + talloc_free(tmp_ctx); + return NT_STATUS_OK; + } + + DEBUG(6, ("xid not found in idmap db, create S-1-22- SID.\n")); + + /* For local users/groups , we just create a rid = uid/gid */ + if (unixid->type == ID_TYPE_UID) { + unix_sid = dom_sid_parse_talloc(tmp_ctx, "S-1-22-1"); + } else { + unix_sid = dom_sid_parse_talloc(tmp_ctx, "S-1-22-2"); + } + if (unix_sid == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + new_sid = dom_sid_add_rid(mem_ctx, unix_sid, unixid->id); + if (new_sid == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + *sid = new_sid; + talloc_free(tmp_ctx); + return NT_STATUS_OK; + +failed: + talloc_free(tmp_ctx); + return status; +} + + +/** + * Map a SID to an unixid struct. + * + * If no mapping exists, a new mapping will be created. + * + * \todo Check if SIDs can be resolved if lp_idmap_trusted_only() == true + * \todo Fix backwards compatibility for Samba3 + * + * \param idmap_ctx idmap context to use + * \param mem_ctx talloc context to use + * \param sid SID to map to an unixid struct + * \param unixid pointer to a unixid struct pointer + * \return NT_STATUS_OK on success, NT_STATUS_INVALID_SID if the sid is not from + * a trusted domain and idmap trusted only = true, NT_STATUS_NONE_MAPPED if the + * mapping failed. + */ +NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx, + const struct dom_sid *sid, struct unixid **unixid) +{ + int ret; + NTSTATUS status = NT_STATUS_NONE_MAPPED; + struct ldb_context *ldb = idmap_ctx->ldb_ctx; + struct ldb_dn *dn; + struct ldb_message *hwm_msg, *map_msg; + struct ldb_result *res = NULL; + int trans; + uint32_t low, high, hwm, new_xid; + char *sid_string, *unixid_string, *hwm_string; + bool hwm_entry_exists; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + + if (dom_sid_in_domain(idmap_ctx->unix_users_sid, sid)) { + uint32_t rid; + DEBUG(6, ("This is a local unix uid, just calculate that.\n")); + status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid); + if (!NT_STATUS_IS_OK(status)) goto failed; + + *unixid = talloc(mem_ctx, struct unixid); + if (*unixid == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + (*unixid)->id = rid; + (*unixid)->type = ID_TYPE_UID; + + talloc_free(tmp_ctx); + return NT_STATUS_OK; + } + + if (dom_sid_in_domain(idmap_ctx->unix_groups_sid, sid)) { + uint32_t rid; + DEBUG(6, ("This is a local unix gid, just calculate that.\n")); + status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid); + if (!NT_STATUS_IS_OK(status)) goto failed; + + *unixid = talloc(mem_ctx, struct unixid); + if (*unixid == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + (*unixid)->id = rid; + (*unixid)->type = ID_TYPE_GID; + + talloc_free(tmp_ctx); + return NT_STATUS_OK; + } + + ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, + NULL, "(&(objectClass=sidMap)(objectSid=%s))", + ldap_encode_ndr_dom_sid(tmp_ctx, sid)); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb))); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + if (res->count == 1) { + const char *type = ldb_msg_find_attr_as_string(res->msgs[0], + "type", NULL); + new_xid = ldb_msg_find_attr_as_uint(res->msgs[0], "xidNumber", + -1); + if (new_xid == (uint32_t) -1) { + DEBUG(1, ("Invalid xid mapping.\n")); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + if (type == NULL) { + DEBUG(1, ("Invalid type for mapping entry.\n")); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + *unixid = talloc(mem_ctx, struct unixid); + if (*unixid == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + (*unixid)->id = new_xid; + + if (strcmp(type, "ID_TYPE_BOTH") == 0) { + (*unixid)->type = ID_TYPE_BOTH; + } else if (strcmp(type, "ID_TYPE_UID") == 0) { + (*unixid)->type = ID_TYPE_UID; + } else { + (*unixid)->type = ID_TYPE_GID; + } + + talloc_free(tmp_ctx); + return NT_STATUS_OK; + } + + DEBUG(6, ("No existing mapping found, attempting to create one.\n")); + + trans = ldb_transaction_start(ldb); + if (trans != LDB_SUCCESS) { + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + /* Redo the search to make sure noone changed the mapping while we + * weren't looking */ + ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, + NULL, "(&(objectClass=sidMap)(objectSid=%s))", + ldap_encode_ndr_dom_sid(tmp_ctx, sid)); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb))); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + if (res->count > 0) { + DEBUG(1, ("Database changed while trying to add a sidmap.\n")); + status = NT_STATUS_RETRY; + goto failed; + } + + /*FIXME: if lp_idmap_trusted_only() == true, check if SID can be + * resolved here. */ + + ret = idmap_get_bounds(idmap_ctx, &low, &high); + if (ret != LDB_SUCCESS) { + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + dn = ldb_dn_new(tmp_ctx, ldb, "CN=CONFIG"); + if (dn == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &res); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb))); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + talloc_steal(tmp_ctx, res); + + if (res->count != 1) { + DEBUG(1, ("No CN=CONFIG record, idmap database is broken.\n")); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + hwm = ldb_msg_find_attr_as_uint(res->msgs[0], "xidNumber", -1); + if (hwm == (uint32_t)-1) { + hwm = low; + hwm_entry_exists = false; + } else { + hwm_entry_exists = true; + } + + if (hwm > high) { + DEBUG(1, ("Out of xids to allocate.\n")); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + hwm_msg = ldb_msg_new(tmp_ctx); + if (hwm_msg == NULL) { + DEBUG(1, ("Out of memory when creating ldb_message\n")); + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + hwm_msg->dn = dn; + + new_xid = hwm; + hwm++; + + hwm_string = talloc_asprintf(tmp_ctx, "%u", hwm); + if (hwm_string == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + sid_string = dom_sid_string(tmp_ctx, sid); + if (sid_string == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + unixid_string = talloc_asprintf(tmp_ctx, "%u", new_xid); + if (unixid_string == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + if (hwm_entry_exists) { + struct ldb_message_element *els; + struct ldb_val *vals; + + /* We're modifying the entry, not just adding a new one. */ + els = talloc_array(tmp_ctx, struct ldb_message_element, 2); + if (els == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + vals = talloc_array(tmp_ctx, struct ldb_val, 2); + if (els == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + hwm_msg->num_elements = 2; + hwm_msg->elements = els; + + els[0].num_values = 1; + els[0].values = &vals[0]; + els[0].flags = LDB_FLAG_MOD_DELETE; + els[0].name = talloc_strdup(tmp_ctx, "xidNumber"); + if (els[0].name == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + els[1].num_values = 1; + els[1].values = &vals[1]; + els[1].flags = LDB_FLAG_MOD_ADD; + els[1].name = els[0].name; + + vals[0].data = (uint8_t *)unixid_string; + vals[0].length = strlen(unixid_string); + vals[1].data = (uint8_t *)hwm_string; + vals[1].length = strlen(hwm_string); + } else { + ret = ldb_msg_add_empty(hwm_msg, "xidNumber", LDB_FLAG_MOD_ADD, + NULL); + if (ret != LDB_SUCCESS) { + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + ret = ldb_msg_add_string(hwm_msg, "xidNumber", hwm_string); + if (ret != LDB_SUCCESS) + { + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + } + + ret = ldb_modify(ldb, hwm_msg); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Updating the xid high water mark failed: %s\n", + ldb_errstring(ldb))); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + map_msg = ldb_msg_new(tmp_ctx); + if (map_msg == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + map_msg->dn = ldb_dn_new_fmt(tmp_ctx, ldb, "CN=%s", sid_string); + if (map_msg->dn == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + ret = ldb_msg_add_string(map_msg, "xidNumber", unixid_string); + if (ret != LDB_SUCCESS) { + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + ret = idmap_msg_add_dom_sid(idmap_ctx, tmp_ctx, map_msg, "objectSid", + sid); + if (ret != LDB_SUCCESS) { + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + ret = ldb_msg_add_string(map_msg, "objectClass", "sidMap"); + if (ret != LDB_SUCCESS) { + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + ret = ldb_msg_add_string(map_msg, "type", "ID_TYPE_BOTH"); + if (ret != LDB_SUCCESS) { + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + ret = ldb_msg_add_string(map_msg, "cn", sid_string); + if (ret != LDB_SUCCESS) { + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + ret = ldb_add(ldb, map_msg); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Adding a sidmap failed: %s\n", ldb_errstring(ldb))); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + trans = ldb_transaction_commit(ldb); + if (trans != LDB_SUCCESS) { + DEBUG(1, ("Transaction failed: %s\n", ldb_errstring(ldb))); + status = NT_STATUS_NONE_MAPPED; + goto failed; + } + + *unixid = talloc(mem_ctx, struct unixid); + if (*unixid == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + (*unixid)->id = new_xid; + (*unixid)->type = ID_TYPE_BOTH; + talloc_free(tmp_ctx); + return NT_STATUS_OK; + +failed: + if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb); + talloc_free(tmp_ctx); + return status; +} + +/** + * Convert an array of unixids to the corresponding array of SIDs + * + * \param idmap_ctx idmap context to use + * \param mem_ctx talloc context the memory for the dom_sids is allocated + * from. + * \param count length of id_mapping array. + * \param id array of id_mappings. + * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping is not + * possible at all, NT_STATUS_SOME_UNMAPPED if some mappings worked and some + * did not. + */ + +NTSTATUS idmap_xids_to_sids(struct idmap_context *idmap_ctx, + TALLOC_CTX *mem_ctx, int count, + struct id_mapping *id) +{ + int i; + int error_count = 0; + + for (i = 0; i < count; ++i) { + id[i].status = idmap_xid_to_sid(idmap_ctx, mem_ctx, + id[i].unixid, &id[i].sid); + if (NT_STATUS_EQUAL(id[i].status, NT_STATUS_RETRY)) { + id[i].status = idmap_xid_to_sid(idmap_ctx, mem_ctx, + id[i].unixid, + &id[i].sid); + } + if (!NT_STATUS_IS_OK(id[i].status)) { + DEBUG(1, ("idmapping xid_to_sid failed for id[%d]\n", i)); + error_count++; + } + } + + if (error_count == count) { + /* Mapping did not work at all. */ + return NT_STATUS_NONE_MAPPED; + } else if (error_count > 0) { + /* Some mappings worked, some did not. */ + return STATUS_SOME_UNMAPPED; + } else { + return NT_STATUS_OK; + } +} + +/** + * Convert an array of SIDs to the corresponding array of unixids + * + * \param idmap_ctx idmap context to use + * \param mem_ctx talloc context the memory for the unixids is allocated + * from. + * \param count length of id_mapping array. + * \param id array of id_mappings. + * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping is not + * possible at all, NT_STATUS_SOME_UNMAPPED if some mappings worked and some + * did not. + */ + +NTSTATUS idmap_sids_to_xids(struct idmap_context *idmap_ctx, + TALLOC_CTX *mem_ctx, int count, + struct id_mapping *id) +{ + int i; + int error_count = 0; + + for (i = 0; i < count; ++i) { + id[i].status = idmap_sid_to_xid(idmap_ctx, mem_ctx, + id[i].sid, &id[i].unixid); + if (NT_STATUS_EQUAL(id[i].status, NT_STATUS_RETRY)) { + id[i].status = idmap_sid_to_xid(idmap_ctx, mem_ctx, + id[i].sid, + &id[i].unixid); + } + if (!NT_STATUS_IS_OK(id[i].status)) { + DEBUG(1, ("idmapping sid_to_xid failed for id[%d]\n", i)); + error_count++; + } + } + + if (error_count == count) { + /* Mapping did not work at all. */ + return NT_STATUS_NONE_MAPPED; + } else if (error_count > 0) { + /* Some mappings worked, some did not. */ + return STATUS_SOME_UNMAPPED; + } else { + return NT_STATUS_OK; + } +} + diff --git a/source4/winbind/idmap.h b/source4/winbind/idmap.h new file mode 100644 index 0000000000..13dbe0b921 --- /dev/null +++ b/source4/winbind/idmap.h @@ -0,0 +1,39 @@ +/* + Unix SMB/CIFS implementation. + + Map SIDs to uids/gids and back + + Copyright (C) Kai Blin 2008 + + 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 _IDMAP_H_ +#define _IDMAP_H_ + +#include "librpc/gen_ndr/winbind.h" + +struct idmap_context { + struct loadparm_context *lp_ctx; + struct ldb_context *ldb_ctx; + struct dom_sid *unix_groups_sid; + struct dom_sid *unix_users_sid; +}; + +struct event_context; + +#include "winbind/idmap_proto.h" + +#endif + diff --git a/source4/winbind/wb_async_helpers.c b/source4/winbind/wb_async_helpers.c new file mode 100644 index 0000000000..25d52a16b5 --- /dev/null +++ b/source4/winbind/wb_async_helpers.c @@ -0,0 +1,442 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Volker Lendecke 2005 + + 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/>. +*/ +/* + a composite API for finding a DC and its name +*/ + +#include "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_async_helpers.h" + +#include "lib/messaging/irpc.h" +#include "librpc/gen_ndr/irpc.h" +#include "auth/credentials/credentials.h" +#include "libcli/security/security.h" +#include "libcli/auth/libcli_auth.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" + +#include "winbind/wb_helper.h" + +struct lsa_lookupsids_state { + struct composite_context *ctx; + int num_sids; + struct lsa_LookupSids r; + struct lsa_SidArray sids; + struct lsa_TransNameArray names; + uint32_t count; + struct wb_sid_object **result; +}; + +static void lsa_lookupsids_recv_names(struct rpc_request *req); + +struct composite_context *wb_lsa_lookupsids_send(TALLOC_CTX *mem_ctx, + struct dcerpc_pipe *lsa_pipe, + struct policy_handle *handle, + int num_sids, + const struct dom_sid **sids) +{ + struct composite_context *result; + struct rpc_request *req; + struct lsa_lookupsids_state *state; + int i; + + result = composite_create(mem_ctx, lsa_pipe->conn->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct lsa_lookupsids_state); + if (state == NULL) goto failed; + result->private_data = state; + state->ctx = result; + + state->sids.num_sids = num_sids; + state->sids.sids = talloc_array(state, struct lsa_SidPtr, num_sids); + if (state->sids.sids == NULL) goto failed; + + for (i=0; i<num_sids; i++) { + state->sids.sids[i].sid = dom_sid_dup(state->sids.sids, + sids[i]); + if (state->sids.sids[i].sid == NULL) goto failed; + } + + state->count = 0; + state->num_sids = num_sids; + state->names.count = 0; + state->names.names = NULL; + + state->r.in.handle = handle; + state->r.in.sids = &state->sids; + state->r.in.names = &state->names; + state->r.in.level = 1; + state->r.in.count = &state->count; + state->r.out.names = &state->names; + state->r.out.count = &state->count; + + req = dcerpc_lsa_LookupSids_send(lsa_pipe, state, &state->r); + if (req == NULL) goto failed; + + req->async.callback = lsa_lookupsids_recv_names; + req->async.private_data = state; + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void lsa_lookupsids_recv_names(struct rpc_request *req) +{ + struct lsa_lookupsids_state *state = + talloc_get_type(req->async.private_data, + struct lsa_lookupsids_state); + int i; + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->r.out.result; + if (!NT_STATUS_IS_OK(state->ctx->status) && + !NT_STATUS_EQUAL(state->ctx->status, STATUS_SOME_UNMAPPED)) { + composite_error(state->ctx, state->ctx->status); + return; + } + + state->result = talloc_array(state, struct wb_sid_object *, + state->num_sids); + if (composite_nomem(state->result, state->ctx)) return; + + for (i=0; i<state->num_sids; i++) { + struct lsa_TranslatedName *name = + &state->r.out.names->names[i]; + struct lsa_DomainInfo *dom; + + state->result[i] = talloc_zero(state->result, + struct wb_sid_object); + if (composite_nomem(state->result[i], state->ctx)) return; + + state->result[i]->type = name->sid_type; + if (state->result[i]->type == SID_NAME_UNKNOWN) { + continue; + } + + if (name->sid_index >= state->r.out.domains->count) { + composite_error(state->ctx, + NT_STATUS_INVALID_PARAMETER); + return; + } + + dom = &state->r.out.domains->domains[name->sid_index]; + state->result[i]->domain = talloc_reference(state->result[i], + dom->name.string); + if ((name->sid_type == SID_NAME_DOMAIN) || + (name->name.string == NULL)) { + state->result[i]->name = + talloc_strdup(state->result[i], ""); + } else { + state->result[i]->name = + talloc_steal(state->result[i], + name->name.string); + } + + if (composite_nomem(state->result[i]->name, state->ctx)) { + return; + } + } + + composite_done(state->ctx); +} + +NTSTATUS wb_lsa_lookupsids_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct wb_sid_object ***names) +{ + NTSTATUS status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + struct lsa_lookupsids_state *state = + talloc_get_type(c->private_data, + struct lsa_lookupsids_state); + *names = talloc_steal(mem_ctx, state->result); + } + talloc_free(c); + return status; +} + + +struct lsa_lookupnames_state { + struct composite_context *ctx; + uint32_t num_names; + struct lsa_LookupNames r; + struct lsa_TransSidArray sids; + uint32_t count; + struct wb_sid_object **result; +}; + +static void lsa_lookupnames_recv_sids(struct rpc_request *req); + +struct composite_context *wb_lsa_lookupnames_send(TALLOC_CTX *mem_ctx, + struct dcerpc_pipe *lsa_pipe, + struct policy_handle *handle, + int num_names, + const char **names) +{ + struct composite_context *result; + struct rpc_request *req; + struct lsa_lookupnames_state *state; + + struct lsa_String *lsa_names; + int i; + + result = composite_create(mem_ctx, lsa_pipe->conn->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct lsa_lookupnames_state); + if (state == NULL) goto failed; + result->private_data = state; + state->ctx = result; + + state->sids.count = 0; + state->sids.sids = NULL; + state->num_names = num_names; + state->count = 0; + + lsa_names = talloc_array(state, struct lsa_String, num_names); + if (lsa_names == NULL) goto failed; + + for (i=0; i<num_names; i++) { + lsa_names[i].string = names[i]; + } + + state->r.in.handle = handle; + state->r.in.num_names = num_names; + state->r.in.names = lsa_names; + state->r.in.sids = &state->sids; + state->r.in.level = 1; + state->r.in.count = &state->count; + state->r.out.count = &state->count; + state->r.out.sids = &state->sids; + + req = dcerpc_lsa_LookupNames_send(lsa_pipe, state, &state->r); + if (req == NULL) goto failed; + + req->async.callback = lsa_lookupnames_recv_sids; + req->async.private_data = state; + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void lsa_lookupnames_recv_sids(struct rpc_request *req) +{ + struct lsa_lookupnames_state *state = + talloc_get_type(req->async.private_data, + struct lsa_lookupnames_state); + int i; + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->r.out.result; + if (!NT_STATUS_IS_OK(state->ctx->status) && + !NT_STATUS_EQUAL(state->ctx->status, STATUS_SOME_UNMAPPED)) { + composite_error(state->ctx, state->ctx->status); + return; + } + + state->result = talloc_array(state, struct wb_sid_object *, + state->num_names); + if (composite_nomem(state->result, state->ctx)) return; + + for (i=0; i<state->num_names; i++) { + struct lsa_TranslatedSid *sid = &state->r.out.sids->sids[i]; + struct lsa_DomainInfo *dom; + + state->result[i] = talloc_zero(state->result, + struct wb_sid_object); + if (composite_nomem(state->result[i], state->ctx)) return; + + state->result[i]->type = sid->sid_type; + if (state->result[i]->type == SID_NAME_UNKNOWN) { + continue; + } + + if (sid->sid_index >= state->r.out.domains->count) { + composite_error(state->ctx, + NT_STATUS_INVALID_PARAMETER); + return; + } + + dom = &state->r.out.domains->domains[sid->sid_index]; + + state->result[i]->sid = dom_sid_add_rid(state->result[i], + dom->sid, sid->rid); + } + + composite_done(state->ctx); +} + +NTSTATUS wb_lsa_lookupnames_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct wb_sid_object ***sids) +{ + NTSTATUS status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + struct lsa_lookupnames_state *state = + talloc_get_type(c->private_data, + struct lsa_lookupnames_state); + *sids = talloc_steal(mem_ctx, state->result); + } + talloc_free(c); + return status; +} +struct samr_getuserdomgroups_state { + struct composite_context *ctx; + struct dcerpc_pipe *samr_pipe; + + int num_rids; + uint32_t *rids; + + struct policy_handle *user_handle; + struct samr_OpenUser o; + struct samr_GetGroupsForUser g; + struct samr_Close c; +}; + +static void samr_usergroups_recv_open(struct rpc_request *req); +static void samr_usergroups_recv_groups(struct rpc_request *req); +static void samr_usergroups_recv_close(struct rpc_request *req); + +struct composite_context *wb_samr_userdomgroups_send(TALLOC_CTX *mem_ctx, + struct dcerpc_pipe *samr_pipe, + struct policy_handle *domain_handle, + uint32_t rid) +{ + struct composite_context *result; + struct rpc_request *req; + struct samr_getuserdomgroups_state *state; + + result = composite_create(mem_ctx, samr_pipe->conn->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct samr_getuserdomgroups_state); + if (state == NULL) goto failed; + result->private_data = state; + state->ctx = result; + + state->samr_pipe = samr_pipe; + + state->user_handle = talloc(state, struct policy_handle); + if (state->user_handle == NULL) goto failed; + + state->o.in.domain_handle = domain_handle; + state->o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + state->o.in.rid = rid; + state->o.out.user_handle = state->user_handle; + + req = dcerpc_samr_OpenUser_send(state->samr_pipe, state, &state->o); + if (req == NULL) goto failed; + + req->async.callback = samr_usergroups_recv_open; + req->async.private_data = state; + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void samr_usergroups_recv_open(struct rpc_request *req) +{ + struct samr_getuserdomgroups_state *state = + talloc_get_type(req->async.private_data, + struct samr_getuserdomgroups_state); + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->o.out.result; + if (!composite_is_ok(state->ctx)) return; + + state->g.in.user_handle = state->user_handle; + + req = dcerpc_samr_GetGroupsForUser_send(state->samr_pipe, state, + &state->g); + composite_continue_rpc(state->ctx, req, samr_usergroups_recv_groups, + state); +} + +static void samr_usergroups_recv_groups(struct rpc_request *req) +{ + struct samr_getuserdomgroups_state *state = + talloc_get_type(req->async.private_data, + struct samr_getuserdomgroups_state); + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->g.out.result; + if (!composite_is_ok(state->ctx)) return; + + state->c.in.handle = state->user_handle; + state->c.out.handle = state->user_handle; + + req = dcerpc_samr_Close_send(state->samr_pipe, state, &state->c); + composite_continue_rpc(state->ctx, req, samr_usergroups_recv_close, + state); +} + +static void samr_usergroups_recv_close(struct rpc_request *req) +{ + struct samr_getuserdomgroups_state *state = + talloc_get_type(req->async.private_data, + struct samr_getuserdomgroups_state); + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->c.out.result; + if (!composite_is_ok(state->ctx)) return; + + composite_done(state->ctx); +} + +NTSTATUS wb_samr_userdomgroups_recv(struct composite_context *ctx, + TALLOC_CTX *mem_ctx, + int *num_rids, uint32_t **rids) +{ + struct samr_getuserdomgroups_state *state = + talloc_get_type(ctx->private_data, + struct samr_getuserdomgroups_state); + + int i; + NTSTATUS status = composite_wait(ctx); + if (!NT_STATUS_IS_OK(status)) goto done; + + *num_rids = state->g.out.rids->count; + *rids = talloc_array(mem_ctx, uint32_t, *num_rids); + if (*rids == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0; i<*num_rids; i++) { + (*rids)[i] = state->g.out.rids->rids[i].rid; + } + + done: + talloc_free(ctx); + return status; +} diff --git a/source4/winbind/wb_async_helpers.h b/source4/winbind/wb_async_helpers.h new file mode 100644 index 0000000000..0dc2d4ff10 --- /dev/null +++ b/source4/winbind/wb_async_helpers.h @@ -0,0 +1,34 @@ +/* + Unix SMB/CIFS implementation. + + SMB composite request interfaces + + Copyright (C) Volker Lendecke 2005 + + 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 __WB_ASYNC_HELPERS_H__ +#define __WB_ASYNC_HELPERS_H__ + +#include "librpc/gen_ndr/lsa.h" + +struct wb_sid_object { + enum lsa_SidType type; + struct dom_sid *sid; + const char *domain; + const char *name; +}; + +#endif /* __WB_ASYNC_HELPERS_H__ */ diff --git a/source4/winbind/wb_cmd_getdcname.c b/source4/winbind/wb_cmd_getdcname.c new file mode 100644 index 0000000000..46d7f0d82f --- /dev/null +++ b/source4/winbind/wb_cmd_getdcname.c @@ -0,0 +1,124 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for wbinfo --getdcname + + Copyright (C) Volker Lendecke 2005 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" + +#include "librpc/gen_ndr/ndr_netlogon_c.h" + +struct cmd_getdcname_state { + struct composite_context *ctx; + const char *domain_name; + + struct netr_GetAnyDCName g; +}; + +static void getdcname_recv_domain(struct composite_context *ctx); +static void getdcname_recv_dcname(struct rpc_request *req); + +struct composite_context *wb_cmd_getdcname_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const char *domain_name) +{ + struct composite_context *result, *ctx; + struct cmd_getdcname_state *state; + + result = composite_create(mem_ctx, service->task->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct cmd_getdcname_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->domain_name = talloc_strdup(state, domain_name); + if (state->domain_name == NULL) goto failed; + + ctx = wb_sid2domain_send(state, service, service->primary_sid); + if (ctx == NULL) goto failed; + + ctx->async.fn = getdcname_recv_domain; + ctx->async.private_data = state; + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void getdcname_recv_domain(struct composite_context *ctx) +{ + struct cmd_getdcname_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_getdcname_state); + struct wbsrv_domain *domain; + struct rpc_request *req; + + state->ctx->status = wb_sid2domain_recv(ctx, &domain); + if (!composite_is_ok(state->ctx)) return; + + state->g.in.logon_server = talloc_asprintf( + state, "\\\\%s", + dcerpc_server_name(domain->netlogon_pipe)); + state->g.in.domainname = state->domain_name; + + req = dcerpc_netr_GetAnyDCName_send(domain->netlogon_pipe, state, + &state->g); + if (composite_nomem(req, state->ctx)) return; + + composite_continue_rpc(state->ctx, req, getdcname_recv_dcname, state); +} + +static void getdcname_recv_dcname(struct rpc_request *req) +{ + struct cmd_getdcname_state *state = + talloc_get_type(req->async.private_data, + struct cmd_getdcname_state); + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = werror_to_ntstatus(state->g.out.result); + if (!composite_is_ok(state->ctx)) return; + + composite_done(state->ctx); +} + +NTSTATUS wb_cmd_getdcname_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + const char **dcname) +{ + struct cmd_getdcname_state *state = + talloc_get_type(c->private_data, struct cmd_getdcname_state); + NTSTATUS status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + const char *p = state->g.out.dcname; + if (*p == '\\') p += 1; + if (*p == '\\') p += 1; + *dcname = talloc_strdup(mem_ctx, p); + if (*dcname == NULL) { + status = NT_STATUS_NO_MEMORY; + } + } + talloc_free(state); + return status; +} diff --git a/source4/winbind/wb_cmd_getpwent.c b/source4/winbind/wb_cmd_getpwent.c new file mode 100644 index 0000000000..980a8a4d92 --- /dev/null +++ b/source4/winbind/wb_cmd_getpwent.c @@ -0,0 +1,128 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for getpwent + + Copyright (C) Kai Blin 2007 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "winbind/wb_async_helpers.h" +#include "winbind/wb_helper.h" +#include "smbd/service_task.h" +#include "libnet/libnet_proto.h" + +struct cmd_getpwent_state { + struct composite_context *ctx; + struct wbsrv_service *service; + + struct wbsrv_pwent *pwent; + uint32_t max_users; + + uint32_t num_users; + struct winbindd_pw *result; +}; + +static void cmd_getpwent_recv_pwnam(struct composite_context *ctx); +#if 0 /*FIXME: implement this*/ +static void cmd_getpwent_recv_user_list(struct composite_context *ctx); +#endif + +struct composite_context *wb_cmd_getpwent_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, struct wbsrv_pwent *pwent, + uint32_t max_users) +{ + struct composite_context *ctx, *result; + struct cmd_getpwent_state *state; + + DEBUG(5, ("wb_cmd_getpwent_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(mem_ctx, struct cmd_getpwent_state); + if (composite_nomem(state, result)) return result; + + state->ctx = result; + result->private_data = state; + state->service = service; + state->pwent = pwent; + state->max_users = max_users; + state->num_users = 0; + + /* If there are users left in the libnet_UserList and we're below the + * maximum number of users to get per winbind getpwent call, use + * getpwnam to get the winbindd_pw struct */ + if (pwent->page_index < pwent->user_list->out.count) { + int idx = pwent->page_index; + char *username = talloc_strdup(state, + pwent->user_list->out.users[idx].username); + + pwent->page_index++; + ctx = wb_cmd_getpwnam_send(state, service, username); + if (composite_nomem(ctx, state->ctx)) return result; + + composite_continue(state->ctx, ctx, cmd_getpwent_recv_pwnam, + state); + } else { + /* If there is no valid user left, call libnet_UserList to get a new + * list of users. */ + composite_error(state->ctx, NT_STATUS_NO_MORE_ENTRIES); + } + return result; +} + +static void cmd_getpwent_recv_pwnam(struct composite_context *ctx) +{ + struct cmd_getpwent_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_getpwent_state); + struct winbindd_pw *pw; + + DEBUG(5, ("cmd_getpwent_recv_pwnam called\n")); + + state->ctx->status = wb_cmd_getpwnam_recv(ctx, state, &pw); + if (!composite_is_ok(state->ctx)) return; + + /*FIXME: Cheat for now and only get one user per call */ + state->result = pw; + + composite_done(state->ctx); +} + +NTSTATUS wb_cmd_getpwent_recv(struct composite_context *ctx, + TALLOC_CTX *mem_ctx, struct winbindd_pw **pw, + uint32_t *num_users) +{ + NTSTATUS status = composite_wait(ctx); + + DEBUG(5, ("wb_cmd_getpwent_recv called\n")); + + if (NT_STATUS_IS_OK(status)) { + struct cmd_getpwent_state *state = + talloc_get_type(ctx->private_data, + struct cmd_getpwent_state); + *pw = talloc_steal(mem_ctx, state->result); + /*FIXME: Cheat and only get oner user */ + *num_users = 1; + } + + talloc_free(ctx); + return status; +} + diff --git a/source4/winbind/wb_cmd_getpwnam.c b/source4/winbind/wb_cmd_getpwnam.c new file mode 100644 index 0000000000..7d821537f0 --- /dev/null +++ b/source4/winbind/wb_cmd_getpwnam.c @@ -0,0 +1,196 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for wbinfo -i + + Copyright (C) Kai Blin 2007 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "winbind/wb_async_helpers.h" +#include "param/param.h" +#include "winbind/wb_helper.h" +#include "smbd/service_task.h" +#include "libnet/libnet_proto.h" +#include "libcli/security/proto.h" + +struct cmd_getpwnam_state { + struct composite_context *ctx; + struct wbsrv_service *service; + char *name; + char *workgroup_name; + struct dom_sid *group_sid; + + struct winbindd_pw *result; +}; + +static void cmd_getpwnam_recv_domain(struct composite_context *ctx); +static void cmd_getpwnam_recv_user_info(struct composite_context *ctx); +static void cmd_getpwnam_recv_uid(struct composite_context *ctx); +static void cmd_getpwnam_recv_gid(struct composite_context *ctx); + +struct composite_context *wb_cmd_getpwnam_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const char *name) +{ + struct composite_context *result, *ctx; + struct cmd_getpwnam_state *state; + + DEBUG(5, ("wb_cmd_getpwnam_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(result, struct cmd_getpwnam_state); + if (composite_nomem(state, result)) return result; + state->ctx = result; + result->private_data = state; + state->service = service; + state->name = talloc_strdup(state, name); + if(composite_nomem(state->name, result)) return result; + + ctx = wb_name2domain_send(state, service, name); + if (composite_nomem(ctx, result)) return result; + + composite_continue(result, ctx, cmd_getpwnam_recv_domain, state); + return result; +} + +static void cmd_getpwnam_recv_domain(struct composite_context *ctx) +{ + struct cmd_getpwnam_state *state = talloc_get_type( + ctx->async.private_data, struct cmd_getpwnam_state); + struct wbsrv_domain *domain; + struct libnet_UserInfo *user_info; + char *user_dom, *user_name; + bool ok; + + state->ctx->status = wb_name2domain_recv(ctx, &domain); + if(!composite_is_ok(state->ctx)) return; + + user_info = talloc(state, struct libnet_UserInfo); + if (composite_nomem(user_info, state->ctx)) return; + + ok= wb_samba3_split_username(state, state->service->task->lp_ctx, state->name, &user_dom, &user_name); + if(!ok){ + composite_error(state->ctx, NT_STATUS_OBJECT_NAME_INVALID); + return; + } + + user_info->in.level = USER_INFO_BY_NAME; + user_info->in.data.user_name = user_name; + user_info->in.domain_name = domain->libnet_ctx->samr.name; + state->workgroup_name = talloc_strdup(state, + domain->libnet_ctx->samr.name); + if(composite_nomem(state->workgroup_name, state->ctx)) return; + + ctx = libnet_UserInfo_send(domain->libnet_ctx, state, user_info, NULL); + + composite_continue(state->ctx, ctx, cmd_getpwnam_recv_user_info, state); +} + +static void cmd_getpwnam_recv_user_info(struct composite_context *ctx) +{ + struct cmd_getpwnam_state *state = talloc_get_type( + ctx->async.private_data, struct cmd_getpwnam_state); + struct libnet_UserInfo *user_info; + struct winbindd_pw *pw; + + DEBUG(5, ("cmd_getpwnam_recv_user_info called\n")); + + user_info = talloc(state, struct libnet_UserInfo); + if(composite_nomem(user_info, state->ctx)) return; + + pw = talloc(state, struct winbindd_pw); + if(composite_nomem(pw, state->ctx)) return; + + state->ctx->status = libnet_UserInfo_recv(ctx, state, user_info); + if(!composite_is_ok(state->ctx)) return; + + WBSRV_SAMBA3_SET_STRING(pw->pw_name, user_info->out.account_name); + WBSRV_SAMBA3_SET_STRING(pw->pw_passwd, "*"); + WBSRV_SAMBA3_SET_STRING(pw->pw_gecos, user_info->out.full_name); + WBSRV_SAMBA3_SET_STRING(pw->pw_dir, + lp_template_homedir(state->service->task->lp_ctx)); + all_string_sub(pw->pw_dir, "%WORKGROUP%", state->workgroup_name, + sizeof(fstring) - 1); + all_string_sub(pw->pw_dir, "%ACCOUNTNAME%", user_info->out.account_name, + sizeof(fstring) - 1); + WBSRV_SAMBA3_SET_STRING(pw->pw_shell, + lp_template_shell(state->service->task->lp_ctx)); + + state->group_sid = dom_sid_dup(state, user_info->out.primary_group_sid); + if(composite_nomem(state->group_sid, state->ctx)) return; + + state->result = pw; + + ctx = wb_sid2uid_send(state, state->service, user_info->out.user_sid); + composite_continue(state->ctx, ctx, cmd_getpwnam_recv_uid, state); +} + +static void cmd_getpwnam_recv_uid(struct composite_context *ctx) +{ + struct cmd_getpwnam_state *state = talloc_get_type( + ctx->async.private_data, struct cmd_getpwnam_state); + uid_t uid; + + DEBUG(5, ("cmd_getpwnam_recv_uid called\n")); + + state->ctx->status = wb_sid2uid_recv(ctx, &uid); + if(!composite_is_ok(state->ctx)) return; + + state->result->pw_uid = uid; + + ctx = wb_sid2gid_send(state, state->service, state->group_sid); + composite_continue(state->ctx, ctx, cmd_getpwnam_recv_gid, state); +} + +static void cmd_getpwnam_recv_gid(struct composite_context *ctx) +{ + struct cmd_getpwnam_state *state = talloc_get_type( + ctx->async.private_data, struct cmd_getpwnam_state); + gid_t gid; + + DEBUG(5, ("cmd_getpwnam_recv_gid called\n")); + + state->ctx->status = wb_sid2gid_recv(ctx, &gid); + if(!composite_is_ok(state->ctx)) return; + + state->result->pw_gid = gid; + + composite_done(state->ctx); +} + +NTSTATUS wb_cmd_getpwnam_recv(struct composite_context *ctx, + TALLOC_CTX *mem_ctx, struct winbindd_pw **pw) +{ + NTSTATUS status = composite_wait(ctx); + + DEBUG(5, ("wb_cmd_getpwnam_recv called\n")); + + if (NT_STATUS_IS_OK(status)) { + struct cmd_getpwnam_state *state = + talloc_get_type(ctx->private_data, + struct cmd_getpwnam_state); + *pw = talloc_steal(mem_ctx, state->result); + } + talloc_free(ctx); + return status; + +} + diff --git a/source4/winbind/wb_cmd_getpwuid.c b/source4/winbind/wb_cmd_getpwuid.c new file mode 100644 index 0000000000..15cc592cf6 --- /dev/null +++ b/source4/winbind/wb_cmd_getpwuid.c @@ -0,0 +1,205 @@ +/* + Unix SMB/CIFS implementation. + + Backend for getpwuid + + Copyright (C) Kai Blin 2007 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "winbind/wb_async_helpers.h" +#include "winbind/wb_helper.h" +#include "smbd/service_task.h" +#include "libnet/libnet_proto.h" +#include "param/param.h" +#include "libcli/security/proto.h" +#include "auth/credentials/credentials.h" + +struct cmd_getpwuid_state { + struct composite_context *ctx; + struct wbsrv_service *service; + uid_t uid; + struct dom_sid *sid; + char *workgroup; + struct wbsrv_domain *domain; + + struct winbindd_pw *result; +}; + +static void cmd_getpwuid_recv_sid(struct composite_context *ctx); +static void cmd_getpwuid_recv_domain(struct composite_context *ctx); +static void cmd_getpwuid_recv_user_info(struct composite_context *ctx); +static void cmd_getpwuid_recv_gid(struct composite_context *ctx); + +/* Get the SID using the uid */ + +struct composite_context *wb_cmd_getpwuid_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + uid_t uid) +{ + struct composite_context *ctx, *result; + struct cmd_getpwuid_state *state; + + DEBUG(5, ("wb_cmd_getpwnam_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(result, struct cmd_getpwuid_state); + if (composite_nomem(state, result)) return result; + state->ctx = result; + result->private_data = state; + state->service = service; + state->uid = uid; + + ctx = wb_uid2sid_send(state, service, uid); + if (composite_nomem(ctx, state->ctx)) return result; + + composite_continue(result, ctx, cmd_getpwuid_recv_sid, state); + return result; +} + + +/* Receive the sid and get the domain structure with it */ + +static void cmd_getpwuid_recv_sid(struct composite_context *ctx) +{ + struct cmd_getpwuid_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_getpwuid_state); + + DEBUG(5, ("cmd_getpwuid_recv_sid called %p\n", ctx->private_data)); + + state->ctx->status = wb_uid2sid_recv(ctx, state, &state->sid); + if (!composite_is_ok(state->ctx)) return; + + ctx = wb_sid2domain_send(state, state->service, state->sid); + + composite_continue(state->ctx, ctx, cmd_getpwuid_recv_domain, state); +} + +/* Receive the domain struct and call libnet to get the user info struct */ + +static void cmd_getpwuid_recv_domain(struct composite_context *ctx) +{ + struct cmd_getpwuid_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_getpwuid_state); + struct libnet_UserInfo *user_info; + + DEBUG(5, ("cmd_getpwuid_recv_domain called\n")); + + state->ctx->status = wb_sid2domain_recv(ctx, &state->domain); + if (!composite_is_ok(state->ctx)) return; + + user_info = talloc(state, struct libnet_UserInfo); + if (composite_nomem(user_info, state->ctx)) return; + + user_info->in.level = USER_INFO_BY_SID; + user_info->in.data.user_sid = state->sid; + user_info->in.domain_name = state->domain->libnet_ctx->samr.name; + + /* We need the workgroup later, so copy it */ + state->workgroup = talloc_strdup(state, + state->domain->libnet_ctx->samr.name); + if (composite_nomem(state->workgroup, state->ctx)) return; + + ctx = libnet_UserInfo_send(state->domain->libnet_ctx, state, user_info, + NULL); + + composite_continue(state->ctx, ctx, cmd_getpwuid_recv_user_info, state); +} + +/* Receive the user info struct and get the gid for the user */ + +static void cmd_getpwuid_recv_user_info(struct composite_context *ctx) +{ + struct cmd_getpwuid_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_getpwuid_state); + struct libnet_UserInfo *user_info; + struct winbindd_pw *pw; + + DEBUG(5, ("cmd_getpwuid_recv_user_info called\n")); + + pw = talloc(state, struct winbindd_pw); + if (composite_nomem(pw, state->ctx)) return; + + user_info = talloc(state, struct libnet_UserInfo); + if(composite_nomem(user_info, state->ctx)) return; + + state->ctx->status = libnet_UserInfo_recv(ctx, state, user_info); + if (!composite_is_ok(state->ctx)) return; + + WBSRV_SAMBA3_SET_STRING(pw->pw_name, user_info->out.account_name); + WBSRV_SAMBA3_SET_STRING(pw->pw_passwd, "*"); + WBSRV_SAMBA3_SET_STRING(pw->pw_gecos, user_info->out.full_name); + WBSRV_SAMBA3_SET_STRING(pw->pw_dir, + lp_template_homedir(state->service->task->lp_ctx)); + all_string_sub(pw->pw_dir, "%WORKGROUP%", state->workgroup, + sizeof(fstring) - 1); + all_string_sub(pw->pw_dir, "%ACCOUNTNAME%", user_info->out.account_name, + sizeof(fstring) - 1); + WBSRV_SAMBA3_SET_STRING(pw->pw_shell, + lp_template_shell(state->service->task->lp_ctx)); + + pw->pw_uid = state->uid; + + state->result = pw; + + ctx = wb_sid2gid_send(state, state->service, + user_info->out.primary_group_sid); + + composite_continue(state->ctx, ctx, cmd_getpwuid_recv_gid, state); +} + +static void cmd_getpwuid_recv_gid(struct composite_context *ctx) +{ + struct cmd_getpwuid_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_getpwuid_state); + gid_t gid; + + DEBUG(5, ("cmd_getpwuid_recv_gid called\n")); + + state->ctx->status = wb_sid2gid_recv(ctx, &gid); + if (!composite_is_ok(state->ctx)) return; + + state->result->pw_gid = gid; + + composite_done(state->ctx); +} + +NTSTATUS wb_cmd_getpwuid_recv(struct composite_context *ctx, + TALLOC_CTX *mem_ctx, struct winbindd_pw **pw) +{ + NTSTATUS status = composite_wait(ctx); + + DEBUG(5, ("wb_cmd_getpwnam_recv called\n")); + + if (NT_STATUS_IS_OK(status)) { + struct cmd_getpwuid_state *state = + talloc_get_type(ctx->private_data, + struct cmd_getpwuid_state); + *pw = talloc_steal(mem_ctx, state->result); + } + talloc_free(ctx); + return status; + +} + diff --git a/source4/winbind/wb_cmd_list_trustdom.c b/source4/winbind/wb_cmd_list_trustdom.c new file mode 100644 index 0000000000..fe98ce2f6a --- /dev/null +++ b/source4/winbind/wb_cmd_list_trustdom.c @@ -0,0 +1,196 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for wbinfo -m + + Copyright (C) Volker Lendecke 2005 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" + +/* List trusted domains. To avoid the trouble with having to wait for other + * conflicting requests waiting for the lsa pipe we're opening our own lsa + * pipe here. */ + +struct cmd_list_trustdom_state { + struct composite_context *ctx; + struct dcerpc_pipe *lsa_pipe; + struct policy_handle *lsa_policy; + int num_domains; + struct wb_dom_info **domains; + + uint32_t resume_handle; + struct lsa_DomainList domainlist; + struct lsa_EnumTrustDom r; +}; + +static void cmd_list_trustdoms_recv_domain(struct composite_context *ctx); +static void cmd_list_trustdoms_recv_lsa(struct composite_context *ctx); +static void cmd_list_trustdoms_recv_doms(struct rpc_request *req); + +struct composite_context *wb_cmd_list_trustdoms_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service) +{ + struct composite_context *result, *ctx; + struct cmd_list_trustdom_state *state; + + result = composite_create(mem_ctx, service->task->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct cmd_list_trustdom_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + ctx = wb_sid2domain_send(state, service, service->primary_sid); + if (ctx == NULL) goto failed; + ctx->async.fn = cmd_list_trustdoms_recv_domain; + ctx->async.private_data = state; + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void cmd_list_trustdoms_recv_domain(struct composite_context *ctx) +{ + struct cmd_list_trustdom_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_list_trustdom_state); + struct wbsrv_domain *domain; + struct smbcli_tree *tree; + + state->ctx->status = wb_sid2domain_recv(ctx, &domain); + if (!composite_is_ok(state->ctx)) return; + + tree = dcerpc_smb_tree(domain->libnet_ctx->lsa.pipe->conn); + if (composite_nomem(tree, state->ctx)) return; + + ctx = wb_init_lsa_send(state, domain); + composite_continue(state->ctx, ctx, cmd_list_trustdoms_recv_lsa, + state); +} + +static void cmd_list_trustdoms_recv_lsa(struct composite_context *ctx) +{ + struct cmd_list_trustdom_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_list_trustdom_state); + struct rpc_request *req; + + state->ctx->status = wb_init_lsa_recv(ctx, state, + &state->lsa_pipe, + &state->lsa_policy); + if (!composite_is_ok(state->ctx)) return; + + state->num_domains = 0; + state->domains = NULL; + + state->domainlist.count = 0; + state->domainlist.domains = NULL; + + state->resume_handle = 0; + state->r.in.handle = state->lsa_policy; + state->r.in.resume_handle = &state->resume_handle; + state->r.in.max_size = 1000; + state->r.out.resume_handle = &state->resume_handle; + state->r.out.domains = &state->domainlist; + + req = dcerpc_lsa_EnumTrustDom_send(state->lsa_pipe, state, &state->r); + composite_continue_rpc(state->ctx, req, cmd_list_trustdoms_recv_doms, + state); +} + +static void cmd_list_trustdoms_recv_doms(struct rpc_request *req) +{ + struct cmd_list_trustdom_state *state = + talloc_get_type(req->async.private_data, + struct cmd_list_trustdom_state); + int i, old_num_domains; + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->r.out.result; + + if (!NT_STATUS_IS_OK(state->ctx->status) && + !NT_STATUS_EQUAL(state->ctx->status, NT_STATUS_NO_MORE_ENTRIES) && + !NT_STATUS_EQUAL(state->ctx->status, STATUS_MORE_ENTRIES)) { + composite_error(state->ctx, state->ctx->status); + return; + } + + old_num_domains = state->num_domains; + + state->num_domains += state->r.out.domains->count; + state->domains = talloc_realloc(state, state->domains, + struct wb_dom_info *, + state->num_domains); + if (state->num_domains && + composite_nomem(state->domains, state->ctx)) return; + + for (i=0; i<state->r.out.domains->count; i++) { + int j = i+old_num_domains; + state->domains[j] = talloc(state->domains, + struct wb_dom_info); + if (composite_nomem(state->domains[i], state->ctx)) return; + state->domains[j]->name = talloc_steal( + state->domains[j], + state->r.out.domains->domains[i].name.string); + state->domains[j]->sid = talloc_steal( + state->domains[j], + state->r.out.domains->domains[i].sid); + } + + if (NT_STATUS_IS_OK(state->ctx->status) || NT_STATUS_EQUAL(state->ctx->status, NT_STATUS_NO_MORE_ENTRIES)) { + state->ctx->status = NT_STATUS_OK; + composite_done(state->ctx); + return; + } + + state->domainlist.count = 0; + state->domainlist.domains = NULL; + state->r.in.handle = state->lsa_policy; + state->r.in.resume_handle = &state->resume_handle; + state->r.in.max_size = 1000; + state->r.out.resume_handle = &state->resume_handle; + state->r.out.domains = &state->domainlist; + + req = dcerpc_lsa_EnumTrustDom_send(state->lsa_pipe, state, &state->r); + composite_continue_rpc(state->ctx, req, cmd_list_trustdoms_recv_doms, + state); +} + +NTSTATUS wb_cmd_list_trustdoms_recv(struct composite_context *ctx, + TALLOC_CTX *mem_ctx, + int *num_domains, + struct wb_dom_info ***domains) +{ + NTSTATUS status = composite_wait(ctx); + if (NT_STATUS_IS_OK(status)) { + struct cmd_list_trustdom_state *state = + talloc_get_type(ctx->private_data, + struct cmd_list_trustdom_state); + *num_domains = state->num_domains; + *domains = talloc_steal(mem_ctx, state->domains); + } + talloc_free(ctx); + return status; +} diff --git a/source4/winbind/wb_cmd_list_users.c b/source4/winbind/wb_cmd_list_users.c new file mode 100644 index 0000000000..f67f133488 --- /dev/null +++ b/source4/winbind/wb_cmd_list_users.c @@ -0,0 +1,198 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for wbinfo -u + + Copyright (C) Kai Blin 2007 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "winbind/wb_async_helpers.h" +#include "winbind/wb_helper.h" +#include "smbd/service_task.h" +#include "libnet/libnet_proto.h" + +struct cmd_list_users_state { + struct composite_context *ctx; + struct wbsrv_service *service; + + struct wbsrv_domain *domain; + char *domain_name; + uint resume_index; + char *result; +}; + +static void cmd_list_users_recv_domain(struct composite_context *ctx); +static void cmd_list_users_recv_user_list(struct composite_context *ctx); + +struct composite_context *wb_cmd_list_users_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, const char *domain_name) +{ + struct composite_context *ctx, *result; + struct cmd_list_users_state *state; + + DEBUG(5, ("wb_cmd_list_users_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(result, struct cmd_list_users_state); + if (composite_nomem(state, result)) return result; + + state->ctx = result; + result->private_data = state; + state->service = service; + state->resume_index = 0; + state->result = talloc_strdup(state, ""); + if (composite_nomem(state->result, state->ctx)) return result; + + /*FIXME: We should look up the domain in the winbind request if it is + * set, not just take the primary domain. However, I want to get the + * libnet logic to work first. */ + + if (domain_name && *domain_name != '\0') { + state->domain_name = talloc_strdup(state, domain_name); + if (composite_nomem(state->domain_name, state->ctx)) + return result; + } else { + state->domain_name = NULL; + } + + ctx = wb_sid2domain_send(state, service, service->primary_sid); + if (composite_nomem(ctx, state->ctx)) return result; + + composite_continue(state->ctx, ctx, cmd_list_users_recv_domain, state); + return result; +} + +static void cmd_list_users_recv_domain(struct composite_context *ctx) +{ + struct cmd_list_users_state *state = talloc_get_type( + ctx->async.private_data, struct cmd_list_users_state); + struct wbsrv_domain *domain; + struct libnet_UserList *user_list; + + DEBUG(5, ("cmd_list_users_recv_domain called\n")); + + state->ctx->status = wb_sid2domain_recv(ctx, &domain); + if (!composite_is_ok(state->ctx)) return; + + state->domain = domain; + + /* If this is non-null, we've looked up the domain given in the winbind + * request, otherwise we'll just use the default name.*/ + if (state->domain_name == NULL) { + state->domain_name = talloc_strdup(state, + domain->libnet_ctx->samr.name); + if (composite_nomem(state->domain_name, state->ctx)) return; + } + + user_list = talloc(state, struct libnet_UserList); + if (composite_nomem(user_list, state->ctx)) return; + + user_list->in.domain_name = state->domain_name; + + /* Rafal suggested that 128 is a good number here. I don't like magic + * numbers too much, but for now it'll have to do. + */ + user_list->in.page_size = 128; + user_list->in.resume_index = state->resume_index; + + ctx = libnet_UserList_send(domain->libnet_ctx, state, user_list, NULL); + + composite_continue(state->ctx, ctx, cmd_list_users_recv_user_list, + state); +} + +static void cmd_list_users_recv_user_list(struct composite_context *ctx) +{ + struct cmd_list_users_state *state = talloc_get_type( + ctx->async.private_data, struct cmd_list_users_state); + struct libnet_UserList *user_list; + NTSTATUS status; + int i; + + DEBUG(5, ("cmd_list_users_recv_user_list called\n")); + + user_list = talloc(state, struct libnet_UserList); + if (composite_nomem(user_list, state->ctx)) return; + + status = libnet_UserList_recv(ctx, state, user_list); + + /* If NTSTATUS is neither OK nor MORE_ENTRIES, something broke */ + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + composite_error(state->ctx, status); + return; + } + + for (i = 0; i < user_list->out.count; ++i) { + DEBUG(5, ("Appending user '%s'\n", user_list->out.users[i].username)); + state->result = talloc_asprintf_append_buffer(state->result, "%s,", + user_list->out.users[i].username); + } + + /* If the status is OK, we're finished, there's no more users. + * So we'll trim off the trailing ',' and are done.*/ + if (NT_STATUS_IS_OK(status)) { + int str_len = strlen(state->result); + DEBUG(5, ("list_UserList_recv returned NT_STATUS_OK\n")); + state->result[str_len - 1] = '\0'; + composite_done(state->ctx); + return; + } + + DEBUG(5, ("list_UserList_recv returned NT_STATUS_MORE_ENTRIES\n")); + + /* Otherwise there's more users to get, so call out to libnet and + * continue on this function here. */ + + user_list->in.domain_name = state->domain_name; + /* See comment above about the page size. 128 seems like a good default. + */ + user_list->in.page_size = 128; + user_list->in.resume_index = user_list->out.resume_index; + + ctx = libnet_UserList_send(state->domain->libnet_ctx, state, user_list, + NULL); + + composite_continue(state->ctx, ctx, cmd_list_users_recv_user_list, + state); +} + +NTSTATUS wb_cmd_list_users_recv(struct composite_context *ctx, + TALLOC_CTX *mem_ctx, uint32_t *extra_data_len, + char **extra_data) +{ + NTSTATUS status = composite_wait(ctx); + + DEBUG(5, ("wb_cmd_list_users_recv called\n")); + + if (NT_STATUS_IS_OK(status)) { + struct cmd_list_users_state *state = talloc_get_type( + ctx->private_data, struct cmd_list_users_state); + + *extra_data_len = strlen(state->result); + *extra_data = talloc_steal(mem_ctx, state->result); + } + + talloc_free(ctx); + return status; +} + + diff --git a/source4/winbind/wb_cmd_lookupname.c b/source4/winbind/wb_cmd_lookupname.c new file mode 100644 index 0000000000..9839e2cd80 --- /dev/null +++ b/source4/winbind/wb_cmd_lookupname.c @@ -0,0 +1,121 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for wbinfo -n + + Copyright (C) Volker Lendecke 2005 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "winbind/wb_async_helpers.h" +#include "winbind/wb_helper.h" +#include "smbd/service_task.h" + +struct cmd_lookupname_state { + struct composite_context *ctx; + const char *name; + struct wb_sid_object *result; +}; + +static void lookupname_recv_domain(struct composite_context *ctx); +static void lookupname_recv_sids(struct composite_context *ctx); + +struct composite_context *wb_cmd_lookupname_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const char *dom_name, + const char *name) +{ + struct composite_context *result, *ctx; + struct cmd_lookupname_state *state; + + result = composite_create(mem_ctx, service->task->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct cmd_lookupname_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->name = talloc_asprintf(state, "%s\\%s", dom_name, name); + if (state->name == NULL) goto failed; + + ctx = wb_sid2domain_send(state, service, service->primary_sid); + if (ctx == NULL) goto failed; + + ctx->async.fn = lookupname_recv_domain; + ctx->async.private_data = state; + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void lookupname_recv_domain(struct composite_context *ctx) +{ + struct cmd_lookupname_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_lookupname_state); + struct wbsrv_domain *domain; + + state->ctx->status = wb_sid2domain_recv(ctx, &domain); + if (!composite_is_ok(state->ctx)) return; + + ctx = wb_lsa_lookupnames_send(state, domain->libnet_ctx->lsa.pipe, + &domain->libnet_ctx->lsa.handle, 1, &state->name); + composite_continue(state->ctx, ctx, lookupname_recv_sids, state); +} + +static void lookupname_recv_sids(struct composite_context *ctx) +{ + struct cmd_lookupname_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_lookupname_state); + struct wb_sid_object **sids; + + state->ctx->status = wb_lsa_lookupnames_recv(ctx, state, &sids); + if (!composite_is_ok(state->ctx)) return; + + state->result = sids[0]; + composite_done(state->ctx); +} + +NTSTATUS wb_cmd_lookupname_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct wb_sid_object **sid) +{ + struct cmd_lookupname_state *state = + talloc_get_type(c->private_data, struct cmd_lookupname_state); + NTSTATUS status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + *sid = talloc_steal(mem_ctx, state->result); + } + talloc_free(state); + return status; +} + +NTSTATUS wb_cmd_lookupname(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const char *dom_name, + const char *name, + struct wb_sid_object **sid) +{ + struct composite_context *c = + wb_cmd_lookupname_send(mem_ctx, service, dom_name, name); + return wb_cmd_lookupname_recv(c, mem_ctx, sid); +} diff --git a/source4/winbind/wb_cmd_lookupsid.c b/source4/winbind/wb_cmd_lookupsid.c new file mode 100644 index 0000000000..2d72ae2072 --- /dev/null +++ b/source4/winbind/wb_cmd_lookupsid.c @@ -0,0 +1,119 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for wbinfo -s + + Copyright (C) Volker Lendecke 2005 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "winbind/wb_helper.h" +#include "smbd/service_task.h" +#include "libcli/security/security.h" + +struct cmd_lookupsid_state { + struct composite_context *ctx; + const struct dom_sid *sid; + struct wb_sid_object *result; +}; + +static void lookupsid_recv_domain(struct composite_context *ctx); +static void lookupsid_recv_names(struct composite_context *ctx); + +struct composite_context *wb_cmd_lookupsid_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const struct dom_sid *sid) +{ + struct composite_context *result, *ctx; + struct cmd_lookupsid_state *state; + + + result = composite_create(mem_ctx, service->task->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct cmd_lookupsid_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->sid = dom_sid_dup(state, sid); + if (state->sid == NULL) goto failed; + + ctx = wb_sid2domain_send(state, service, service->primary_sid); + if (ctx == NULL) goto failed; + + ctx->async.fn = lookupsid_recv_domain; + ctx->async.private_data = state; + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void lookupsid_recv_domain(struct composite_context *ctx) +{ + struct cmd_lookupsid_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_lookupsid_state); + struct wbsrv_domain *domain; + + state->ctx->status = wb_sid2domain_recv(ctx, &domain); + if (!composite_is_ok(state->ctx)) return; + + ctx = wb_lsa_lookupsids_send(state, domain->libnet_ctx->lsa.pipe, + &domain->libnet_ctx->lsa.handle, 1, &state->sid); + composite_continue(state->ctx, ctx, lookupsid_recv_names, state); +} + +static void lookupsid_recv_names(struct composite_context *ctx) +{ + struct cmd_lookupsid_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_lookupsid_state); + struct wb_sid_object **names; + + state->ctx->status = wb_lsa_lookupsids_recv(ctx, state, &names); + if (!composite_is_ok(state->ctx)) return; + + state->result = names[0]; + composite_done(state->ctx); +} + +NTSTATUS wb_cmd_lookupsid_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct wb_sid_object **sid) +{ + struct cmd_lookupsid_state *state = + talloc_get_type(c->private_data, struct cmd_lookupsid_state); + NTSTATUS status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + *sid = talloc_steal(mem_ctx, state->result); + } + talloc_free(state); + return status; +} + +NTSTATUS wb_cmd_lookupsid(TALLOC_CTX *mem_ctx, struct wbsrv_service *service, + const struct dom_sid *sid, + struct wb_sid_object **name) +{ + struct composite_context *c = + wb_cmd_lookupsid_send(mem_ctx, service, sid); + return wb_cmd_lookupsid_recv(c, mem_ctx, name); +} diff --git a/source4/winbind/wb_cmd_setpwent.c b/source4/winbind/wb_cmd_setpwent.c new file mode 100644 index 0000000000..50ab2fdff6 --- /dev/null +++ b/source4/winbind/wb_cmd_setpwent.c @@ -0,0 +1,142 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for setpwent + + Copyright (C) Kai Blin 2007 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "winbind/wb_async_helpers.h" +#include "winbind/wb_helper.h" +#include "smbd/service_task.h" +#include "libnet/libnet_proto.h" + +struct cmd_setpwent_state { + struct composite_context *ctx; + struct wbsrv_service *service; + struct libnet_context *libnet_ctx; + + struct wbsrv_pwent *result; +}; + +static void cmd_setpwent_recv_domain(struct composite_context *ctx); +static void cmd_setpwent_recv_user_list(struct composite_context *ctx); + +struct composite_context *wb_cmd_setpwent_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service) +{ + struct composite_context *ctx, *result; + struct cmd_setpwent_state *state; + + DEBUG(5, ("wb_cmd_setpwent_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(mem_ctx, struct cmd_setpwent_state); + if (composite_nomem(state, result)) return result; + + state->ctx = result; + result->private_data = state; + state->service = service; + + state->result = talloc(state, struct wbsrv_pwent); + if (composite_nomem(state->result, state->ctx)) return result; + + ctx = wb_sid2domain_send(state, service, service->primary_sid); + if (composite_nomem(ctx, state->ctx)) return result; + + composite_continue(state->ctx, ctx, cmd_setpwent_recv_domain, state); + return result; +} + +static void cmd_setpwent_recv_domain(struct composite_context *ctx) +{ + struct cmd_setpwent_state *state = talloc_get_type( + ctx->async.private_data, struct cmd_setpwent_state); + struct wbsrv_domain *domain; + struct libnet_UserList *user_list; + + DEBUG(5, ("cmd_setpwent_recv_domain called\n")); + + state->ctx->status = wb_sid2domain_recv(ctx, &domain); + if (!composite_is_ok(state->ctx)) return; + + state->libnet_ctx = domain->libnet_ctx; + + user_list = talloc(state->result, struct libnet_UserList); + if (composite_nomem(user_list, state->ctx)) return; + + user_list->in.domain_name = talloc_strdup(state, + domain->libnet_ctx->samr.name); + if (composite_nomem(user_list->in.domain_name, state->ctx)) return; + + /* Page size recommended by Rafal */ + user_list->in.page_size = 128; + + /* Always get the start of the list */ + user_list->in.resume_index = 0; + + ctx = libnet_UserList_send(domain->libnet_ctx, state->result, user_list, + NULL); + + composite_continue(state->ctx, ctx, cmd_setpwent_recv_user_list, state); +} + +static void cmd_setpwent_recv_user_list(struct composite_context *ctx) +{ + struct cmd_setpwent_state *state = talloc_get_type( + ctx->async.private_data, struct cmd_setpwent_state); + struct libnet_UserList *user_list; + + DEBUG(5, ("cmd_setpwent_recv_user_list called\n")); + + user_list = talloc(state->result, struct libnet_UserList); + if (composite_nomem(user_list, state->ctx)) return; + + state->ctx->status = libnet_UserList_recv(ctx, state->result, + user_list); + if (!composite_is_ok(state->ctx)) return; + + state->result->user_list = user_list; + state->result->page_index = 0; + state->result->libnet_ctx = state->libnet_ctx; + + composite_done(state->ctx); +} + +NTSTATUS wb_cmd_setpwent_recv(struct composite_context *ctx, + TALLOC_CTX *mem_ctx, struct wbsrv_pwent **pwent) +{ + NTSTATUS status = composite_wait(ctx); + + DEBUG(5, ("wb_cmd_setpwent_recv called\n")); + + if (NT_STATUS_IS_OK(status)) { + struct cmd_setpwent_state *state = + talloc_get_type(ctx->private_data, + struct cmd_setpwent_state); + + *pwent = talloc_steal(mem_ctx, state->result); + } + + talloc_free(ctx); + return status; +} + diff --git a/source4/winbind/wb_cmd_userdomgroups.c b/source4/winbind/wb_cmd_userdomgroups.c new file mode 100644 index 0000000000..649fe489cc --- /dev/null +++ b/source4/winbind/wb_cmd_userdomgroups.c @@ -0,0 +1,147 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for wbinfo --user-domgroups + + Copyright (C) Volker Lendecke 2005 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "libcli/security/security.h" +#include "winbind/wb_server.h" +#include "winbind/wb_helper.h" +#include "smbd/service_task.h" + +struct cmd_userdomgroups_state { + struct composite_context *ctx; + struct dom_sid *dom_sid; + uint32_t user_rid; + int num_rids; + uint32_t *rids; +}; + +static void userdomgroups_recv_domain(struct composite_context *ctx); +static void userdomgroups_recv_rids(struct composite_context *ctx); + +struct composite_context *wb_cmd_userdomgroups_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const struct dom_sid *sid) +{ + struct composite_context *result, *ctx; + struct cmd_userdomgroups_state *state; + + result = composite_create(mem_ctx, service->task->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct cmd_userdomgroups_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->dom_sid = dom_sid_dup(state, sid); + if (state->dom_sid == NULL) goto failed; + state->dom_sid->num_auths -= 1; + + state->user_rid = sid->sub_auths[sid->num_auths-1]; + + ctx = wb_sid2domain_send(state, service, sid); + + composite_continue(state->ctx, ctx, userdomgroups_recv_domain, state); + + if (ctx) { + return result; + } + + failed: + talloc_free(result); + return NULL; +} + +static void userdomgroups_recv_domain(struct composite_context *ctx) +{ + struct cmd_userdomgroups_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_userdomgroups_state); + struct wbsrv_domain *domain; + + state->ctx->status = wb_sid2domain_recv(ctx, &domain); + if (!composite_is_ok(state->ctx)) return; + + ctx = wb_samr_userdomgroups_send(state, domain->libnet_ctx->samr.pipe, + &domain->libnet_ctx->samr.handle, + state->user_rid); + composite_continue(state->ctx, ctx, userdomgroups_recv_rids, state); + +} + +static void userdomgroups_recv_rids(struct composite_context *ctx) +{ + struct cmd_userdomgroups_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_userdomgroups_state); + + state->ctx->status = wb_samr_userdomgroups_recv(ctx, state, + &state->num_rids, + &state->rids); + if (!composite_is_ok(state->ctx)) return; + + composite_done(state->ctx); +} + +NTSTATUS wb_cmd_userdomgroups_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + int *num_sids, struct dom_sid ***sids) +{ + struct cmd_userdomgroups_state *state = + talloc_get_type(c->private_data, + struct cmd_userdomgroups_state); + int i; + NTSTATUS status; + + status = composite_wait(c); + if (!NT_STATUS_IS_OK(status)) goto done; + + *num_sids = state->num_rids; + *sids = talloc_array(mem_ctx, struct dom_sid *, state->num_rids); + if (*sids == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0; i<state->num_rids; i++) { + (*sids)[i] = dom_sid_add_rid((*sids), state->dom_sid, + state->rids[i]); + if ((*sids)[i] == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + } + +done: + talloc_free(c); + return status; +} + +NTSTATUS wb_cmd_userdomgroups(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const struct dom_sid *sid, + int *num_sids, struct dom_sid ***sids) +{ + struct composite_context *c = + wb_cmd_userdomgroups_send(mem_ctx, service, sid); + return wb_cmd_userdomgroups_recv(c, mem_ctx, num_sids, sids); +} diff --git a/source4/winbind/wb_cmd_usersids.c b/source4/winbind/wb_cmd_usersids.c new file mode 100644 index 0000000000..b414cf6313 --- /dev/null +++ b/source4/winbind/wb_cmd_usersids.c @@ -0,0 +1,193 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for wbinfo --user-sids + + Copyright (C) Volker Lendecke 2005 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "libcli/security/security.h" + +/* Calculate the token in two steps: Go the user's originating domain, ask for + * the user's domain groups. Then with the resulting list of sids go to our + * own domain to expand the aliases aka domain local groups. */ + +struct cmd_usersids_state { + struct composite_context *ctx; + struct wbsrv_service *service; + struct dom_sid *user_sid; + int num_domgroups; + struct dom_sid **domgroups; + + struct lsa_SidArray lsa_sids; + struct samr_Ids rids; + struct samr_GetAliasMembership r; + + int num_sids; + struct dom_sid **sids; +}; + +static void usersids_recv_domgroups(struct composite_context *ctx); +static void usersids_recv_domain(struct composite_context *ctx); +static void usersids_recv_aliases(struct rpc_request *req); + +struct composite_context *wb_cmd_usersids_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const struct dom_sid *sid) +{ + struct composite_context *result, *ctx; + struct cmd_usersids_state *state; + + + result = composite_create(mem_ctx, service->task->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct cmd_usersids_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->service = service; + state->user_sid = dom_sid_dup(state, sid); + if (state->user_sid == NULL) goto failed; + + ctx = wb_cmd_userdomgroups_send(state, service, sid); + if (ctx == NULL) goto failed; + + ctx->async.fn = usersids_recv_domgroups; + ctx->async.private_data = state; + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void usersids_recv_domgroups(struct composite_context *ctx) +{ + struct cmd_usersids_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_usersids_state); + + state->ctx->status = wb_cmd_userdomgroups_recv(ctx, state, + &state->num_domgroups, + &state->domgroups); + if (!composite_is_ok(state->ctx)) return; + + ctx = wb_sid2domain_send(state, state->service, + state->service->primary_sid); + composite_continue(state->ctx, ctx, usersids_recv_domain, state); +} + +static void usersids_recv_domain(struct composite_context *ctx) +{ + struct cmd_usersids_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_usersids_state); + struct rpc_request *req; + struct wbsrv_domain *domain; + int i; + + state->ctx->status = wb_sid2domain_recv(ctx, &domain); + if (!composite_is_ok(state->ctx)) return; + + state->lsa_sids.num_sids = state->num_domgroups+1; + state->lsa_sids.sids = talloc_array(state, struct lsa_SidPtr, + state->lsa_sids.num_sids); + if (composite_nomem(state->lsa_sids.sids, state->ctx)) return; + + state->lsa_sids.sids[0].sid = state->user_sid; + for (i=0; i<state->num_domgroups; i++) { + state->lsa_sids.sids[i+1].sid = state->domgroups[i]; + } + + state->rids.count = 0; + state->rids.ids = NULL; + + state->r.in.domain_handle = &domain->libnet_ctx->samr.handle; + state->r.in.sids = &state->lsa_sids; + state->r.out.rids = &state->rids; + + req = dcerpc_samr_GetAliasMembership_send(domain->libnet_ctx->samr.pipe, state, + &state->r); + composite_continue_rpc(state->ctx, req, usersids_recv_aliases, state); +} + +static void usersids_recv_aliases(struct rpc_request *req) +{ + struct cmd_usersids_state *state = + talloc_get_type(req->async.private_data, + struct cmd_usersids_state); + int i; + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->r.out.result; + if (!composite_is_ok(state->ctx)) return; + + state->num_sids = 1 + state->num_domgroups + state->r.out.rids->count; + state->sids = talloc_array(state, struct dom_sid *, state->num_sids); + if (composite_nomem(state->sids, state->ctx)) return; + + state->sids[0] = talloc_steal(state->sids, state->user_sid); + + for (i=0; i<state->num_domgroups; i++) { + state->sids[1+i] = + talloc_steal(state->sids, state->domgroups[i]); + } + + for (i=0; i<state->r.out.rids->count; i++) { + state->sids[1+state->num_domgroups+i] = dom_sid_add_rid( + state->sids, state->service->primary_sid, + state->r.out.rids->ids[i]); + + if (composite_nomem(state->sids[1+state->num_domgroups+i], + state->ctx)) return; + } + + composite_done(state->ctx); +} + +NTSTATUS wb_cmd_usersids_recv(struct composite_context *ctx, + TALLOC_CTX *mem_ctx, + int *num_sids, struct dom_sid ***sids) +{ + NTSTATUS status = composite_wait(ctx); + if (NT_STATUS_IS_OK(status)) { + struct cmd_usersids_state *state = + talloc_get_type(ctx->private_data, + struct cmd_usersids_state); + *num_sids = state->num_sids; + *sids = talloc_steal(mem_ctx, state->sids); + } + talloc_free(ctx); + return status; +} + +NTSTATUS wb_cmd_usersids(TALLOC_CTX *mem_ctx, struct wbsrv_service *service, + const struct dom_sid *sid, + int *num_sids, struct dom_sid ***sids) +{ + struct composite_context *c = + wb_cmd_usersids_send(mem_ctx, service, sid); + return wb_cmd_usersids_recv(c, mem_ctx, num_sids, sids); +} + diff --git a/source4/winbind/wb_connect_lsa.c b/source4/winbind/wb_connect_lsa.c new file mode 100644 index 0000000000..a728f8abe4 --- /dev/null +++ b/source4/winbind/wb_connect_lsa.c @@ -0,0 +1,135 @@ +/* + Unix SMB/CIFS implementation. + + Connect to the LSA pipe, given an smbcli_tree and possibly some + credentials. Try ntlmssp, schannel and anon in that order. + + Copyright (C) Volker Lendecke 2005 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007 + + 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 "includes.h" +#include "libcli/composite/composite.h" + +#include "libcli/raw/libcliraw.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "winbind/wb_server.h" + +/* Helper to initialize LSA with a specific auth methods. Verify by opening + * the LSA policy. */ + +struct init_lsa_state { + struct composite_context *ctx; + struct dcerpc_pipe *lsa_pipe; + + uint8_t auth_type; + struct cli_credentials *creds; + + struct lsa_ObjectAttribute objectattr; + struct lsa_OpenPolicy2 openpolicy; + struct policy_handle *handle; +}; + +static void init_lsa_recv_pipe(struct composite_context *ctx); +static void init_lsa_recv_openpol(struct rpc_request *req); + +struct composite_context *wb_init_lsa_send(TALLOC_CTX *mem_ctx, + struct wbsrv_domain *domain) +{ + struct composite_context *result, *ctx; + struct init_lsa_state *state; + + result = composite_create(mem_ctx, domain->netlogon_pipe->conn->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct init_lsa_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + /* this will make the secondary connection on the same IPC$ share, + secured with SPNEGO or NTLMSSP */ + ctx = dcerpc_secondary_auth_connection_send(domain->netlogon_pipe, + domain->lsa_binding, + &ndr_table_lsarpc, + domain->libnet_ctx->cred, + domain->libnet_ctx->lp_ctx); + composite_continue(state->ctx, ctx, init_lsa_recv_pipe, state); + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void init_lsa_recv_pipe(struct composite_context *ctx) +{ + struct rpc_request *req; + struct init_lsa_state *state = + talloc_get_type(ctx->async.private_data, + struct init_lsa_state); + + state->ctx->status = dcerpc_secondary_auth_connection_recv(ctx, state, + &state->lsa_pipe); + if (!composite_is_ok(state->ctx)) return; + + state->handle = talloc(state, struct policy_handle); + if (composite_nomem(state->handle, state->ctx)) return; + + state->openpolicy.in.system_name = + talloc_asprintf(state, "\\\\%s", + dcerpc_server_name(state->lsa_pipe)); + ZERO_STRUCT(state->objectattr); + state->openpolicy.in.attr = &state->objectattr; + state->openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + state->openpolicy.out.handle = state->handle; + + req = dcerpc_lsa_OpenPolicy2_send(state->lsa_pipe, state, + &state->openpolicy); + composite_continue_rpc(state->ctx, req, init_lsa_recv_openpol, state); +} + +static void init_lsa_recv_openpol(struct rpc_request *req) +{ + struct init_lsa_state *state = + talloc_get_type(req->async.private_data, + struct init_lsa_state); + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->openpolicy.out.result; + if (!composite_is_ok(state->ctx)) return; + + composite_done(state->ctx); +} + +NTSTATUS wb_init_lsa_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct dcerpc_pipe **lsa_pipe, + struct policy_handle **lsa_policy) +{ + NTSTATUS status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + struct init_lsa_state *state = + talloc_get_type(c->private_data, + struct init_lsa_state); + *lsa_pipe = talloc_steal(mem_ctx, state->lsa_pipe); + *lsa_policy = talloc_steal(mem_ctx, state->handle); + } + talloc_free(c); + return status; +} + diff --git a/source4/winbind/wb_connect_sam.c b/source4/winbind/wb_connect_sam.c new file mode 100644 index 0000000000..efd715b164 --- /dev/null +++ b/source4/winbind/wb_connect_sam.c @@ -0,0 +1,164 @@ +/* + Unix SMB/CIFS implementation. + + Connect to the SAMR pipe, and return connection and domain handles. + + Copyright (C) Volker Lendecke 2005 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007 + + 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 "includes.h" +#include "libcli/composite/composite.h" + +#include "libcli/raw/libcliraw.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "winbind/wb_server.h" + + +/* Helper to initialize SAMR with a specific auth methods. Verify by opening + * the SAM handle */ + +struct connect_samr_state { + struct composite_context *ctx; + struct dom_sid *sid; + + struct dcerpc_pipe *samr_pipe; + struct policy_handle *connect_handle; + struct policy_handle *domain_handle; + + struct samr_Connect2 c; + struct samr_OpenDomain o; +}; + +static void connect_samr_recv_pipe(struct composite_context *ctx); +static void connect_samr_recv_conn(struct rpc_request *req); +static void connect_samr_recv_open(struct rpc_request *req); + +struct composite_context *wb_connect_samr_send(TALLOC_CTX *mem_ctx, + struct wbsrv_domain *domain) +{ + struct composite_context *result, *ctx; + struct connect_samr_state *state; + + result = composite_create(mem_ctx, domain->netlogon_pipe->conn->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct connect_samr_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->sid = dom_sid_dup(state, domain->info->sid); + if (state->sid == NULL) goto failed; + + /* this will make the secondary connection on the same IPC$ share, + secured with SPNEGO, NTLMSSP or SCHANNEL */ + ctx = dcerpc_secondary_auth_connection_send(domain->netlogon_pipe, + domain->samr_binding, + &ndr_table_samr, + domain->libnet_ctx->cred, + domain->libnet_ctx->lp_ctx); + composite_continue(state->ctx, ctx, connect_samr_recv_pipe, state); + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void connect_samr_recv_pipe(struct composite_context *ctx) +{ + struct rpc_request *req; + struct connect_samr_state *state = + talloc_get_type(ctx->async.private_data, + struct connect_samr_state); + + state->ctx->status = dcerpc_secondary_auth_connection_recv(ctx, state, + &state->samr_pipe); + if (!composite_is_ok(state->ctx)) return; + + state->connect_handle = talloc(state, struct policy_handle); + if (composite_nomem(state->connect_handle, state->ctx)) return; + + state->c.in.system_name = + talloc_asprintf(state, "\\\\%s", + dcerpc_server_name(state->samr_pipe)); + state->c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + state->c.out.connect_handle = state->connect_handle; + + req = dcerpc_samr_Connect2_send(state->samr_pipe, state, &state->c); + composite_continue_rpc(state->ctx, req, connect_samr_recv_conn, state); + return; +} + +static void connect_samr_recv_conn(struct rpc_request *req) +{ + struct connect_samr_state *state = + talloc_get_type(req->async.private_data, + struct connect_samr_state); + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->c.out.result; + if (!composite_is_ok(state->ctx)) return; + + state->domain_handle = talloc(state, struct policy_handle); + if (composite_nomem(state->domain_handle, state->ctx)) return; + + state->o.in.connect_handle = state->connect_handle; + state->o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + state->o.in.sid = state->sid; + state->o.out.domain_handle = state->domain_handle; + + req = dcerpc_samr_OpenDomain_send(state->samr_pipe, state, &state->o); + composite_continue_rpc(state->ctx, req, + connect_samr_recv_open, state); +} + +static void connect_samr_recv_open(struct rpc_request *req) +{ + struct connect_samr_state *state = + talloc_get_type(req->async.private_data, + struct connect_samr_state); + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->o.out.result; + if (!composite_is_ok(state->ctx)) return; + + composite_done(state->ctx); +} + +NTSTATUS wb_connect_samr_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct dcerpc_pipe **samr_pipe, + struct policy_handle *connect_handle, + struct policy_handle *domain_handle) +{ + NTSTATUS status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + struct connect_samr_state *state = + talloc_get_type(c->private_data, + struct connect_samr_state); + *samr_pipe = talloc_steal(mem_ctx, state->samr_pipe); + *connect_handle = *state->connect_handle; + *domain_handle = *state->domain_handle; + } + talloc_free(c); + return status; +} + diff --git a/source4/winbind/wb_dom_info.c b/source4/winbind/wb_dom_info.c new file mode 100644 index 0000000000..4fcbf3a6e5 --- /dev/null +++ b/source4/winbind/wb_dom_info.c @@ -0,0 +1,126 @@ +/* + Unix SMB/CIFS implementation. + + Get a struct wb_dom_info for a domain using DNS, netbios, possibly cldap + etc. + + Copyright (C) Volker Lendecke 2005 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "libcli/resolve/resolve.h" +#include "libcli/security/security.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "librpc/gen_ndr/ndr_irpc.h" +#include "librpc/gen_ndr/samr.h" +#include "lib/messaging/irpc.h" +#include "libcli/finddcs.h" +#include "param/param.h" + +struct get_dom_info_state { + struct composite_context *ctx; + struct wb_dom_info *info; +}; + +static void get_dom_info_recv_addrs(struct composite_context *ctx); + +struct composite_context *wb_get_dom_info_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const char *domain_name, + const struct dom_sid *sid) +{ + struct composite_context *result, *ctx; + struct get_dom_info_state *state; + struct dom_sid *dom_sid; + result = composite_create(mem_ctx, service->task->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct get_dom_info_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->info = talloc_zero(state, struct wb_dom_info); + if (state->info == NULL) goto failed; + + state->info->name = talloc_strdup(state->info, domain_name); + if (state->info->name == NULL) goto failed; + + state->info->sid = dom_sid_dup(state->info, sid); + if (state->info->sid == NULL) goto failed; + + dom_sid = dom_sid_dup(mem_ctx, sid); + if (dom_sid == NULL) goto failed; + + ctx = finddcs_send(mem_ctx, lp_netbios_name(service->task->lp_ctx), + lp_nbt_port(service->task->lp_ctx), + domain_name, NBT_NAME_LOGON, + dom_sid, + lp_iconv_convenience(service->task->lp_ctx), + lp_resolve_context(service->task->lp_ctx), + service->task->event_ctx, + service->task->msg_ctx); + if (ctx == NULL) goto failed; + + composite_continue(state->ctx, ctx, get_dom_info_recv_addrs, state); + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void get_dom_info_recv_addrs(struct composite_context *ctx) +{ + struct get_dom_info_state *state = + talloc_get_type(ctx->async.private_data, + struct get_dom_info_state); + + state->ctx->status = finddcs_recv(ctx, state->info, + &state->info->num_dcs, + &state->info->dcs); + if (!composite_is_ok(state->ctx)) return; + + composite_done(state->ctx); +} + +NTSTATUS wb_get_dom_info_recv(struct composite_context *ctx, + TALLOC_CTX *mem_ctx, + struct wb_dom_info **result) +{ + NTSTATUS status = composite_wait(ctx); + if (NT_STATUS_IS_OK(status)) { + struct get_dom_info_state *state = + talloc_get_type(ctx->private_data, + struct get_dom_info_state); + *result = talloc_steal(mem_ctx, state->info); + } + talloc_free(ctx); + return status; +} + +NTSTATUS wb_get_dom_info(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const char *domain_name, + const struct dom_sid *sid, + struct wb_dom_info **result) +{ + struct composite_context *ctx = + wb_get_dom_info_send(mem_ctx, service, domain_name, sid); + return wb_get_dom_info_recv(ctx, mem_ctx, result); +} diff --git a/source4/winbind/wb_dom_info_trusted.c b/source4/winbind/wb_dom_info_trusted.c new file mode 100644 index 0000000000..46d3bf37f6 --- /dev/null +++ b/source4/winbind/wb_dom_info_trusted.c @@ -0,0 +1,239 @@ +/* + Unix SMB/CIFS implementation. + + Get a struct wb_dom_info for a trusted domain, relying on "our" DC. + + Copyright (C) Volker Lendecke 2005 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "libcli/resolve/resolve.h" +#include "libcli/security/security.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "libcli/libcli.h" +#include "param/param.h" + +struct trusted_dom_info_state { + struct composite_context *ctx; + struct wbsrv_service *service; + struct wbsrv_domain *my_domain; + + struct netr_DsRGetDCName d; + struct netr_GetAnyDCName g; + + struct wb_dom_info *info; +}; + +static void trusted_dom_info_recv_domain(struct composite_context *ctx); +static void trusted_dom_info_recv_dsr(struct rpc_request *req); +static void trusted_dom_info_recv_dcname(struct rpc_request *req); +static void trusted_dom_info_recv_dcaddr(struct composite_context *ctx); + +struct composite_context *wb_trusted_dom_info_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const char *domain_name, + const struct dom_sid *sid) +{ + struct composite_context *result, *ctx; + struct trusted_dom_info_state *state; + + result = composite_create(mem_ctx, service->task->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct trusted_dom_info_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->info = talloc_zero(state, struct wb_dom_info); + if (state->info == NULL) goto failed; + + state->service = service; + + state->info->sid = dom_sid_dup(state->info, sid); + if (state->info->sid == NULL) goto failed; + + state->info->name = talloc_strdup(state->info, domain_name); + if (state->info->name == NULL) goto failed; + + ctx = wb_sid2domain_send(state, service, service->primary_sid); + if (ctx == NULL) goto failed; + + ctx->async.fn = trusted_dom_info_recv_domain; + ctx->async.private_data = state; + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void trusted_dom_info_recv_domain(struct composite_context *ctx) +{ + struct trusted_dom_info_state *state = + talloc_get_type(ctx->async.private_data, + struct trusted_dom_info_state); + struct rpc_request *req; + + state->ctx->status = wb_sid2domain_recv(ctx, &state->my_domain); + if (!composite_is_ok(state->ctx)) return; + + state->d.in.server_unc = + talloc_asprintf(state, "\\\\%s", + dcerpc_server_name(state->my_domain->netlogon_pipe)); + if (composite_nomem(state->d.in.server_unc, + state->ctx)) return; + + state->d.in.domain_name = state->info->name; + state->d.in.domain_guid = NULL; + state->d.in.site_guid = NULL; + state->d.in.flags = DS_RETURN_DNS_NAME; + + req = dcerpc_netr_DsRGetDCName_send(state->my_domain->netlogon_pipe, + state, &state->d); + composite_continue_rpc(state->ctx, req, trusted_dom_info_recv_dsr, + state); +} + +/* + * dcerpc_netr_DsRGetDCName has replied + */ + +static void trusted_dom_info_recv_dsr(struct rpc_request *req) +{ + struct trusted_dom_info_state *state = + talloc_get_type(req->async.private_data, + struct trusted_dom_info_state); + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!NT_STATUS_IS_OK(state->ctx->status)) { + DEBUG(9, ("dcerpc_ndr_request_recv returned %s\n", + nt_errstr(state->ctx->status))); + goto fallback; + } + + state->ctx->status = + werror_to_ntstatus(state->d.out.result); + if (!NT_STATUS_IS_OK(state->ctx->status)) { + DEBUG(9, ("dsrgetdcname returned %s\n", + nt_errstr(state->ctx->status))); + goto fallback; + } + + /* Hey, that was easy! */ + state->info->num_dcs = 1; + state->info->dcs = talloc(state->info, struct nbt_dc_name); + state->info->dcs[0].name = talloc_steal(state->info, + state->d.out.info->dc_unc); + if (*state->info->dcs[0].name == '\\') state->info->dcs[0].name++; + if (*state->info->dcs[0].name == '\\') state->info->dcs[0].name++; + + state->info->dcs[0].address = talloc_steal(state->info, + state->d.out.info->dc_address); + if (*state->info->dcs[0].address == '\\') state->info->dcs[0].address++; + if (*state->info->dcs[0].address == '\\') state->info->dcs[0].address++; + + state->info->dns_name = talloc_steal(state->info, + state->d.out.info->domain_name); + + composite_done(state->ctx); + return; + + fallback: + + state->g.in.logon_server = talloc_asprintf( + state, "\\\\%s", + dcerpc_server_name(state->my_domain->netlogon_pipe)); + state->g.in.domainname = state->info->name; + + req = dcerpc_netr_GetAnyDCName_send(state->my_domain->netlogon_pipe, + state, &state->g); + if (composite_nomem(req, state->ctx)) return; + + composite_continue_rpc(state->ctx, req, trusted_dom_info_recv_dcname, + state); +} + +static void trusted_dom_info_recv_dcname(struct rpc_request *req) +{ + struct trusted_dom_info_state *state = + talloc_get_type(req->async.private_data, + struct trusted_dom_info_state); + struct composite_context *ctx; + struct nbt_name name; + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = werror_to_ntstatus(state->g.out.result); + if (!composite_is_ok(state->ctx)) return; + + /* Hey, that was easy! */ + state->info->num_dcs = 1; + state->info->dcs = talloc(state->info, struct nbt_dc_name); + state->info->dcs[0].name = talloc_steal(state->info, + state->g.out.dcname); + if (*state->info->dcs[0].name == '\\') state->info->dcs[0].name++; + if (*state->info->dcs[0].name == '\\') state->info->dcs[0].name++; + + make_nbt_name(&name, state->info->dcs[0].name, 0x20); + ctx = resolve_name_send(lp_resolve_context(state->service->task->lp_ctx), + &name, state->service->task->event_ctx); + + composite_continue(state->ctx, ctx, trusted_dom_info_recv_dcaddr, + state); +} + +static void trusted_dom_info_recv_dcaddr(struct composite_context *ctx) +{ + struct trusted_dom_info_state *state = + talloc_get_type(ctx->async.private_data, + struct trusted_dom_info_state); + + state->ctx->status = resolve_name_recv(ctx, state->info, + &state->info->dcs[0].address); + if (!composite_is_ok(state->ctx)) return; + + composite_done(state->ctx); +} + +NTSTATUS wb_trusted_dom_info_recv(struct composite_context *ctx, + TALLOC_CTX *mem_ctx, + struct wb_dom_info **result) +{ + NTSTATUS status = composite_wait(ctx); + if (NT_STATUS_IS_OK(status)) { + struct trusted_dom_info_state *state = + talloc_get_type(ctx->private_data, + struct trusted_dom_info_state); + *result = talloc_steal(mem_ctx, state->info); + } + talloc_free(ctx); + return status; +} + +NTSTATUS wb_trusted_dom_info(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const char *domain_name, + const struct dom_sid *sid, + struct wb_dom_info **result) +{ + struct composite_context *ctx = + wb_trusted_dom_info_send(mem_ctx, service, domain_name, sid); + return wb_trusted_dom_info_recv(ctx, mem_ctx, result); +} diff --git a/source4/winbind/wb_gid2sid.c b/source4/winbind/wb_gid2sid.c new file mode 100644 index 0000000000..834d869845 --- /dev/null +++ b/source4/winbind/wb_gid2sid.c @@ -0,0 +1,108 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for wbinfo -G + + Copyright (C) 2007-2008 Kai Blin + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "winbind/wb_helper.h" +#include "libcli/security/proto.h" +#include "winbind/idmap.h" + +struct gid2sid_state { + struct composite_context *ctx; + struct wbsrv_service *service; + struct dom_sid *sid; +}; + +static void gid2sid_recv_sid(struct composite_context *ctx); + +struct composite_context *wb_gid2sid_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, gid_t gid) +{ + struct composite_context *result, *ctx; + struct gid2sid_state *state; + struct unixid *unixid; + struct id_mapping *ids; + + DEBUG(5, ("wb_gid2sid_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(result, struct gid2sid_state); + if (composite_nomem(state, result)) return result; + + state->ctx = result; + result->private_data = state; + state->service = service; + + unixid = talloc(result, struct unixid); + if (composite_nomem(unixid, result)) return result; + unixid->id = gid; + unixid->type = ID_TYPE_GID; + + ids = talloc(result, struct id_mapping); + if (composite_nomem(ids, result)) return result; + ids->unixid = unixid; + ids->sid = NULL; + + ctx = wb_xids2sids_send(result, service, 1, ids); + if (composite_nomem(ctx, result)) return result; + + composite_continue(result, ctx, gid2sid_recv_sid, state); + return result; +} + +static void gid2sid_recv_sid(struct composite_context *ctx) +{ + struct gid2sid_state *state = talloc_get_type(ctx->async.private_data, + struct gid2sid_state); + struct id_mapping *ids = NULL; + state->ctx->status = wb_xids2sids_recv(ctx, &ids); + if (!composite_is_ok(state->ctx)) return; + + if (!NT_STATUS_IS_OK(ids->status)) { + composite_error(state->ctx, ids->status); + return; + } + + state->sid = ids->sid; + composite_done(state->ctx); +} + +NTSTATUS wb_gid2sid_recv(struct composite_context *ctx, TALLOC_CTX *mem_ctx, + struct dom_sid **sid) +{ + NTSTATUS status = composite_wait(ctx); + + DEBUG(5, ("wb_gid2sid_recv called.\n")); + + if (NT_STATUS_IS_OK(status)) { + struct gid2sid_state *state = + talloc_get_type(ctx->private_data, + struct gid2sid_state); + *sid = talloc_steal(mem_ctx, state->sid); + } + talloc_free(ctx); + return status; +} + diff --git a/source4/winbind/wb_init_domain.c b/source4/winbind/wb_init_domain.c new file mode 100644 index 0000000000..c6dee825a9 --- /dev/null +++ b/source4/winbind/wb_init_domain.c @@ -0,0 +1,428 @@ +/* + Unix SMB/CIFS implementation. + + A composite API for initializing a domain + + Copyright (C) Volker Lendecke 2005 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "winbind/wb_server.h" +#include "winbind/wb_async_helpers.h" +#include "winbind/wb_helper.h" +#include "smbd/service_task.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "libcli/libcli.h" + +#include "libcli/auth/credentials.h" +#include "libcli/security/security.h" + +#include "libcli/ldap/ldap_client.h" + +#include "auth/credentials/credentials.h" +#include "param/param.h" + +/* + * Initialize a domain: + * + * - With schannel credentials, try to open the SMB connection and + * NETLOGON pipe with the machine creds. This works against W2k3SP1 + * with an NTLMSSP session setup. Fall back to anonymous (for the CIFS level). + * + * - If we have schannel creds, do the auth2 and open the schannel'ed netlogon + * pipe. + * + * - Open LSA. If we have machine creds, try to open with SPNEGO or NTLMSSP. Fall back + * to schannel. + * + * - With queryinfopolicy, verify that we're talking to the right domain + * + * A bit complex, but with all the combinations I think it's the best we can + * get. NT4, W2k3 and W2k all have different combinations, but in the end we + * have a signed&sealed lsa connection on all of them. + * + * Not sure if it is overkill, but it seems to work. + */ + +struct init_domain_state { + struct composite_context *ctx; + struct wbsrv_domain *domain; + struct wbsrv_service *service; + + struct lsa_ObjectAttribute objectattr; + struct lsa_OpenPolicy2 lsa_openpolicy; + struct lsa_QueryInfoPolicy queryinfo; +}; + +static void init_domain_recv_netlogonpipe(struct composite_context *ctx); +static void init_domain_recv_lsa_pipe(struct composite_context *ctx); +static void init_domain_recv_lsa_policy(struct rpc_request *req); +static void init_domain_recv_queryinfo(struct rpc_request *req); +static void init_domain_recv_samr(struct composite_context *ctx); + +static struct dcerpc_binding *init_domain_binding(struct init_domain_state *state, + const struct ndr_interface_table *table) +{ + struct dcerpc_binding *binding; + NTSTATUS status; + + /* Make a binding string */ + { + char *s = talloc_asprintf(state, "ncacn_np:%s", state->domain->dc_name); + if (s == NULL) return NULL; + status = dcerpc_parse_binding(state, s, &binding); + talloc_free(s); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + } + + /* Alter binding to contain hostname, but also address (so we don't look it up twice) */ + binding->target_hostname = state->domain->dc_name; + binding->host = state->domain->dc_address; + + /* This shouldn't make a network call, as the mappings for named pipes are well known */ + status = dcerpc_epm_map_binding(binding, binding, table, state->service->task->event_ctx, + state->service->task->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + return binding; +} + +struct composite_context *wb_init_domain_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + struct wb_dom_info *dom_info) +{ + struct composite_context *result, *ctx; + struct init_domain_state *state; + + result = composite_create(mem_ctx, service->task->event_ctx); + if (result == NULL) goto failed; + + state = talloc_zero(result, struct init_domain_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->service = service; + + state->domain = talloc(state, struct wbsrv_domain); + if (state->domain == NULL) goto failed; + + state->domain->info = talloc_reference(state->domain, dom_info); + if (state->domain->info == NULL) goto failed; + + /* Caller should check, but to be safe: */ + if (dom_info->num_dcs < 1) { + goto failed; + } + + /* For now, we just pick the first. The next step will be to + * walk the entire list. Also need to fix finddcs() to return + * the entire list */ + state->domain->dc_name = dom_info->dcs[0].name; + state->domain->dc_address = dom_info->dcs[0].address; + + state->domain->libnet_ctx = libnet_context_init(service->task->event_ctx, + service->task->lp_ctx); + + /* Create a credentials structure */ + state->domain->libnet_ctx->cred = cli_credentials_init(state->domain); + if (state->domain->libnet_ctx->cred == NULL) goto failed; + + cli_credentials_set_conf(state->domain->libnet_ctx->cred, service->task->lp_ctx); + + /* Connect the machine account to the credentials */ + state->ctx->status = + cli_credentials_set_machine_account(state->domain->libnet_ctx->cred, state->domain->libnet_ctx->lp_ctx); + if (!NT_STATUS_IS_OK(state->ctx->status)) goto failed; + + state->domain->netlogon_binding = init_domain_binding(state, &ndr_table_netlogon); + + state->domain->netlogon_pipe = NULL; + + if ((!cli_credentials_is_anonymous(state->domain->libnet_ctx->cred)) && + ((lp_server_role(service->task->lp_ctx) == ROLE_DOMAIN_MEMBER) || + (lp_server_role(service->task->lp_ctx) == ROLE_DOMAIN_CONTROLLER)) && + (dom_sid_equal(state->domain->info->sid, + state->service->primary_sid))) { + state->domain->netlogon_binding->flags |= DCERPC_SCHANNEL; + + /* For debugging, it can be a real pain if all the traffic is encrypted */ + if (lp_winbind_sealed_pipes(service->task->lp_ctx)) { + state->domain->netlogon_binding->flags |= (DCERPC_SIGN | DCERPC_SEAL ); + } else { + state->domain->netlogon_binding->flags |= (DCERPC_SIGN); + } + } + + /* No encryption on anonymous pipes */ + + ctx = dcerpc_pipe_connect_b_send(state, state->domain->netlogon_binding, + &ndr_table_netlogon, + state->domain->libnet_ctx->cred, + service->task->event_ctx, + service->task->lp_ctx); + + if (composite_nomem(ctx, state->ctx)) { + goto failed; + } + + composite_continue(state->ctx, ctx, init_domain_recv_netlogonpipe, + state); + return result; + failed: + talloc_free(result); + return NULL; +} + +/* Having make a netlogon connection (possibly secured with schannel), + * make an LSA connection to the same DC, on the same IPC$ share */ +static void init_domain_recv_netlogonpipe(struct composite_context *ctx) +{ + struct init_domain_state *state = + talloc_get_type(ctx->async.private_data, + struct init_domain_state); + + state->ctx->status = dcerpc_pipe_connect_b_recv(ctx, state->domain, + &state->domain->netlogon_pipe); + + if (!composite_is_ok(state->ctx)) { + return; + } + talloc_steal(state->domain->netlogon_pipe, state->domain->netlogon_binding); + + state->domain->lsa_binding = init_domain_binding(state, &ndr_table_lsarpc); + + /* For debugging, it can be a real pain if all the traffic is encrypted */ + if (lp_winbind_sealed_pipes(state->service->task->lp_ctx)) { + state->domain->lsa_binding->flags |= (DCERPC_SIGN | DCERPC_SEAL ); + } else { + state->domain->lsa_binding->flags |= (DCERPC_SIGN); + } + + state->domain->libnet_ctx->lsa.pipe = NULL; + + /* this will make the secondary connection on the same IPC$ share, + secured with SPNEGO or NTLMSSP */ + ctx = dcerpc_secondary_auth_connection_send(state->domain->netlogon_pipe, + state->domain->lsa_binding, + &ndr_table_lsarpc, + state->domain->libnet_ctx->cred, + state->domain->libnet_ctx->lp_ctx + ); + composite_continue(state->ctx, ctx, init_domain_recv_lsa_pipe, state); +} + +static bool retry_with_schannel(struct init_domain_state *state, + struct dcerpc_binding *binding, + const struct ndr_interface_table *table, + void (*continuation)(struct composite_context *)) +{ + struct composite_context *ctx; + state->ctx->status = NT_STATUS_OK; + if (state->domain->netlogon_binding->flags & DCERPC_SCHANNEL + && !(binding->flags & DCERPC_SCHANNEL)) { + /* Opening a policy handle failed, perhaps it was + * because we don't get a 'wrong password' error on + * NTLMSSP binds */ + + /* Try again with schannel */ + binding->flags |= DCERPC_SCHANNEL; + + /* Try again, likewise on the same IPC$ share, + secured with SCHANNEL */ + ctx = dcerpc_secondary_auth_connection_send(state->domain->netlogon_pipe, + binding, + table, + state->domain->libnet_ctx->cred, + state->domain->libnet_ctx->lp_ctx); + composite_continue(state->ctx, ctx, continuation, state); + return true; + } else { + return false; + } +} +/* We should now have either an authenticated LSA pipe, or an error. + * On success, open a policy handle + */ +static void init_domain_recv_lsa_pipe(struct composite_context *ctx) +{ + struct rpc_request *req; + struct init_domain_state *state = + talloc_get_type(ctx->async.private_data, + struct init_domain_state); + + state->ctx->status = dcerpc_secondary_auth_connection_recv(ctx, state->domain, + &state->domain->libnet_ctx->lsa.pipe); + if (NT_STATUS_EQUAL(state->ctx->status, NT_STATUS_LOGON_FAILURE)) { + if (retry_with_schannel(state, state->domain->lsa_binding, + &ndr_table_lsarpc, + init_domain_recv_lsa_pipe)) { + return; + } + } + if (!composite_is_ok(state->ctx)) return; + + talloc_steal(state->domain->libnet_ctx, state->domain->libnet_ctx->lsa.pipe); + talloc_steal(state->domain->libnet_ctx->lsa.pipe, state->domain->lsa_binding); + state->domain->libnet_ctx->lsa.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + state->domain->libnet_ctx->lsa.name = state->domain->info->name; + + ZERO_STRUCT(state->domain->libnet_ctx->lsa.handle); + state->lsa_openpolicy.in.system_name = + talloc_asprintf(state, "\\\\%s", + dcerpc_server_name(state->domain->libnet_ctx->lsa.pipe)); + ZERO_STRUCT(state->objectattr); + state->lsa_openpolicy.in.attr = &state->objectattr; + state->lsa_openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + state->lsa_openpolicy.out.handle = &state->domain->libnet_ctx->lsa.handle; + + req = dcerpc_lsa_OpenPolicy2_send(state->domain->libnet_ctx->lsa.pipe, state, + &state->lsa_openpolicy); + + composite_continue_rpc(state->ctx, req, init_domain_recv_lsa_policy, state); +} + +/* Receive a policy handle (or not, and retry the authentication) and + * obtain some basic information about the domain */ + +static void init_domain_recv_lsa_policy(struct rpc_request *req) +{ + struct init_domain_state *state = + talloc_get_type(req->async.private_data, + struct init_domain_state); + + state->ctx->status = dcerpc_ndr_request_recv(req); + if ((!NT_STATUS_IS_OK(state->ctx->status) + || !NT_STATUS_IS_OK(state->lsa_openpolicy.out.result))) { + if (retry_with_schannel(state, state->domain->lsa_binding, + &ndr_table_lsarpc, + init_domain_recv_lsa_pipe)) { + return; + } + } + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->lsa_openpolicy.out.result; + if (!composite_is_ok(state->ctx)) return; + + state->queryinfo.in.handle = &state->domain->libnet_ctx->lsa.handle; + state->queryinfo.in.level = LSA_POLICY_INFO_ACCOUNT_DOMAIN; + + req = dcerpc_lsa_QueryInfoPolicy_send(state->domain->libnet_ctx->lsa.pipe, state, + &state->queryinfo); + composite_continue_rpc(state->ctx, req, + init_domain_recv_queryinfo, state); +} + +static void init_domain_recv_queryinfo(struct rpc_request *req) +{ + struct init_domain_state *state = + talloc_get_type(req->async.private_data, struct init_domain_state); + struct lsa_DomainInfo *dominfo; + struct composite_context *ctx; + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(state->ctx)) return; + state->ctx->status = state->queryinfo.out.result; + if (!composite_is_ok(state->ctx)) return; + + dominfo = &state->queryinfo.out.info->account_domain; + + if (strcasecmp(state->domain->info->name, dominfo->name.string) != 0) { + DEBUG(2, ("Expected domain name %s, DC %s said %s\n", + state->domain->info->name, + dcerpc_server_name(state->domain->libnet_ctx->lsa.pipe), + dominfo->name.string)); + composite_error(state->ctx, NT_STATUS_INVALID_DOMAIN_STATE); + return; + } + + if (!dom_sid_equal(state->domain->info->sid, dominfo->sid)) { + DEBUG(2, ("Expected domain sid %s, DC %s said %s\n", + dom_sid_string(state, state->domain->info->sid), + dcerpc_server_name(state->domain->libnet_ctx->lsa.pipe), + dom_sid_string(state, dominfo->sid))); + composite_error(state->ctx, NT_STATUS_INVALID_DOMAIN_STATE); + return; + } + + state->domain->samr_binding = init_domain_binding(state, &ndr_table_samr); + + /* We want to use the same flags as the LSA pipe did (so, if + * it needed schannel, then we need that here too) */ + state->domain->samr_binding->flags = state->domain->lsa_binding->flags; + + state->domain->libnet_ctx->samr.pipe = NULL; + + ctx = wb_connect_samr_send(state, state->domain); + composite_continue(state->ctx, ctx, init_domain_recv_samr, state); +} + +/* Recv the SAMR details (SamrConnect and SamrOpenDomain handle) and + * open an LDAP connection */ +static void init_domain_recv_samr(struct composite_context *ctx) +{ + const char *ldap_url; + struct init_domain_state *state = + talloc_get_type(ctx->async.private_data, + struct init_domain_state); + + state->ctx->status = wb_connect_samr_recv( + ctx, state->domain, + &state->domain->libnet_ctx->samr.pipe, + &state->domain->libnet_ctx->samr.handle, + &state->domain->libnet_ctx->samr.handle); + if (!composite_is_ok(state->ctx)) return; + + talloc_steal(state->domain->libnet_ctx->samr.pipe, state->domain->samr_binding); + state->domain->libnet_ctx->samr.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + state->domain->libnet_ctx->samr.name = state->domain->info->name; + state->domain->libnet_ctx->samr.sid = state->domain->info->sid; + + composite_done(state->ctx); +} + +NTSTATUS wb_init_domain_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct wbsrv_domain **result) +{ + NTSTATUS status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + struct init_domain_state *state = + talloc_get_type(c->private_data, + struct init_domain_state); + *result = talloc_steal(mem_ctx, state->domain); + } + talloc_free(c); + return status; +} + +NTSTATUS wb_init_domain(TALLOC_CTX *mem_ctx, struct wbsrv_service *service, + struct wb_dom_info *dom_info, + struct wbsrv_domain **result) +{ + struct composite_context *c = + wb_init_domain_send(mem_ctx, service, dom_info); + return wb_init_domain_recv(c, mem_ctx, result); +} diff --git a/source4/winbind/wb_irpc.c b/source4/winbind/wb_irpc.c new file mode 100644 index 0000000000..0535045adb --- /dev/null +++ b/source4/winbind/wb_irpc.c @@ -0,0 +1,155 @@ +/* + Unix SMB/CIFS implementation. + Main winbindd irpc handlers + + Copyright (C) Stefan Metzmacher 2006 + + 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 "includes.h" +#include "winbind/wb_server.h" +#include "lib/messaging/irpc.h" +#include "libcli/composite/composite.h" +#include "libcli/security/proto.h" +#include "librpc/gen_ndr/ndr_winbind.h" +#include "smbd/service_task.h" + +struct wb_irpc_SamLogon_state { + struct irpc_message *msg; + struct winbind_SamLogon *req; +}; + +static void wb_irpc_SamLogon_callback(struct composite_context *ctx); + +static NTSTATUS wb_irpc_SamLogon(struct irpc_message *msg, + struct winbind_SamLogon *req) +{ + struct wbsrv_service *service = talloc_get_type(msg->private, + struct wbsrv_service); + struct wb_irpc_SamLogon_state *s; + struct composite_context *ctx; + + DEBUG(5, ("wb_irpc_SamLogon called\n")); + + s = talloc(msg, struct wb_irpc_SamLogon_state); + NT_STATUS_HAVE_NO_MEMORY(s); + + s->msg = msg; + s->req = req; + + ctx = wb_sam_logon_send(msg, service, req); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = wb_irpc_SamLogon_callback; + ctx->async.private_data = s; + + msg->defer_reply = true; + return NT_STATUS_OK; +} + +static void wb_irpc_SamLogon_callback(struct composite_context *ctx) +{ + struct wb_irpc_SamLogon_state *s = talloc_get_type(ctx->async.private_data, + struct wb_irpc_SamLogon_state); + NTSTATUS status; + + DEBUG(5, ("wb_irpc_SamLogon_callback called\n")); + + status = wb_sam_logon_recv(ctx, s, s->req); + + irpc_send_reply(s->msg, status); +} + +struct wb_irpc_get_idmap_state { + struct irpc_message *msg; + struct winbind_get_idmap *req; + int level; +}; + +static void wb_irpc_get_idmap_callback(struct composite_context *ctx); + +static NTSTATUS wb_irpc_get_idmap(struct irpc_message *msg, + struct winbind_get_idmap *req) +{ + struct wbsrv_service *service = talloc_get_type(msg->private, + struct wbsrv_service); + struct wb_irpc_get_idmap_state *s; + struct composite_context *ctx; + + DEBUG(5, ("wb_irpc_get_idmap called\n")); + + s = talloc(msg, struct wb_irpc_get_idmap_state); + NT_STATUS_HAVE_NO_MEMORY(s); + + s->msg = msg; + s->req = req; + s->level = req->in.level; + + switch(s->level) { + case WINBIND_IDMAP_LEVEL_SIDS_TO_XIDS: + ctx = wb_sids2xids_send(msg, service, req->in.count, + req->in.ids); + break; + case WINBIND_IDMAP_LEVEL_XIDS_TO_SIDS: + ctx = wb_xids2sids_send(msg, service, req->in.count, + req->in.ids); + break; + } + NT_STATUS_HAVE_NO_MEMORY(ctx); + + composite_continue(ctx, ctx, wb_irpc_get_idmap_callback, s); + msg->defer_reply = true; + + return NT_STATUS_OK; +} + +static void wb_irpc_get_idmap_callback(struct composite_context *ctx) +{ + struct wb_irpc_get_idmap_state *s; + NTSTATUS status; + + DEBUG(5, ("wb_irpc_get_idmap_callback called\n")); + + s = talloc_get_type(ctx->async.private_data, + struct wb_irpc_get_idmap_state); + + switch(s->level) { + case WINBIND_IDMAP_LEVEL_SIDS_TO_XIDS: + status = wb_sids2xids_recv(ctx, &s->req->out.ids); + break; + case WINBIND_IDMAP_LEVEL_XIDS_TO_SIDS: + status = wb_xids2sids_recv(ctx, &s->req->out.ids); + break; + } + + irpc_send_reply(s->msg, status); +} + +NTSTATUS wbsrv_init_irpc(struct wbsrv_service *service) +{ + NTSTATUS status; + + irpc_add_name(service->task->msg_ctx, "winbind_server"); + + status = IRPC_REGISTER(service->task->msg_ctx, winbind, WINBIND_SAMLOGON, + wb_irpc_SamLogon, service); + NT_STATUS_NOT_OK_RETURN(status); + + status = IRPC_REGISTER(service->task->msg_ctx, winbind, WINBIND_GET_IDMAP, + wb_irpc_get_idmap, service); + NT_STATUS_NOT_OK_RETURN(status); + + return NT_STATUS_OK; +} diff --git a/source4/winbind/wb_name2domain.c b/source4/winbind/wb_name2domain.c new file mode 100644 index 0000000000..e19703b1e5 --- /dev/null +++ b/source4/winbind/wb_name2domain.c @@ -0,0 +1,121 @@ +/* + Unix SMB/CIFS implementation. + + Find and init a domain struct for a name + + Copyright (C) Kai Blin 2007 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "winbind/wb_helper.h" +#include "param/param.h" + +struct name2domain_state { + struct composite_context *ctx; + struct wbsrv_service *service; + + struct wbsrv_domain *domain; +}; + +static void name2domain_recv_sid(struct composite_context *ctx); +static void name2domain_recv_domain(struct composite_context *ctx); + +struct composite_context *wb_name2domain_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, const char* name) +{ + struct composite_context *result, *ctx; + struct name2domain_state *state; + char *user_dom, *user_name; + bool ok; + + DEBUG(5, ("wb_name2domain_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(result, struct name2domain_state); + if (composite_nomem(state, result)) return result; + state->ctx = result; + result->private_data = state; + state->service = service; + + ok = wb_samba3_split_username(state, service->task->lp_ctx, name, &user_dom, &user_name); + if(!ok) { + composite_error(state->ctx, NT_STATUS_OBJECT_NAME_INVALID); + return result; + } + + ctx = wb_cmd_lookupname_send(state, service, user_dom, user_name); + if (composite_nomem(ctx, state->ctx)) return result; + + composite_continue(result, ctx, name2domain_recv_sid, state); + return result; +} + +static void name2domain_recv_sid(struct composite_context *ctx) +{ + struct name2domain_state *state = + talloc_get_type(ctx->async.private_data, + struct name2domain_state); + struct wb_sid_object *sid; + + DEBUG(5, ("name2domain_recv_sid called\n")); + + state->ctx->status = wb_cmd_lookupname_recv(ctx, state, &sid); + if(!composite_is_ok(state->ctx)) return; + + ctx = wb_sid2domain_send(state, state->service, sid->sid); + + composite_continue(state->ctx, ctx, name2domain_recv_domain, state); +} + +static void name2domain_recv_domain(struct composite_context *ctx) +{ + struct name2domain_state *state = + talloc_get_type(ctx->async.private_data, + struct name2domain_state); + struct wbsrv_domain *domain; + + DEBUG(5, ("name2domain_recv_domain called\n")); + + state->ctx->status = wb_sid2domain_recv(ctx, &domain); + if(!composite_is_ok(state->ctx)) return; + + state->domain = domain; + + composite_done(state->ctx); +} + +NTSTATUS wb_name2domain_recv(struct composite_context *ctx, + struct wbsrv_domain **result) +{ + NTSTATUS status = composite_wait(ctx); + + DEBUG(5, ("wb_name2domain_recv called\n")); + + if (NT_STATUS_IS_OK(status)) { + struct name2domain_state *state = + talloc_get_type(ctx->private_data, + struct name2domain_state); + *result = state->domain; + } + talloc_free(ctx); + return status; +} + diff --git a/source4/winbind/wb_pam_auth.c b/source4/winbind/wb_pam_auth.c new file mode 100644 index 0000000000..ee54bcd58f --- /dev/null +++ b/source4/winbind/wb_pam_auth.c @@ -0,0 +1,271 @@ +/* + Unix SMB/CIFS implementation. + + Authenticate a user + + Copyright (C) Volker Lendecke 2005 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "auth/credentials/credentials.h" +#include "libcli/auth/libcli_auth.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/winbind.h" +#include "param/param.h" + +/* Oh, there is so much to keep an eye on when authenticating a user. Oh my! */ +struct pam_auth_crap_state { + struct composite_context *ctx; + struct event_context *event_ctx; + struct loadparm_context *lp_ctx; + + struct winbind_SamLogon *req; + char *unix_username; + + struct netr_NetworkInfo ninfo; + struct netr_LogonSamLogon r; + + const char *user_name; + const char *domain_name; + + struct netr_UserSessionKey user_session_key; + struct netr_LMSessionKey lm_key; + DATA_BLOB info3; +}; + +/* + * NTLM authentication. +*/ + +static void pam_auth_crap_recv_logon(struct composite_context *ctx); + +struct composite_context *wb_cmd_pam_auth_crap_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + uint32_t logon_parameters, + const char *domain, + const char *user, + const char *workstation, + DATA_BLOB chal, + DATA_BLOB nt_resp, + DATA_BLOB lm_resp) +{ + struct composite_context *result, *ctx; + struct pam_auth_crap_state *state; + struct netr_NetworkInfo *ninfo; + DATA_BLOB tmp_nt_resp, tmp_lm_resp; + + result = composite_create(mem_ctx, service->task->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct pam_auth_crap_state); + if (state == NULL) goto failed; + state->ctx = result; + state->lp_ctx = service->task->lp_ctx; + result->private_data = state; + + state->req = talloc(state, struct winbind_SamLogon); + + state->req->in.logon_level = 2; + state->req->in.validation_level = 3; + ninfo = state->req->in.logon.network = talloc(state, struct netr_NetworkInfo); + if (ninfo == NULL) goto failed; + + ninfo->identity_info.account_name.string = talloc_strdup(state, user); + ninfo->identity_info.domain_name.string = talloc_strdup(state, domain); + ninfo->identity_info.parameter_control = logon_parameters; + ninfo->identity_info.logon_id_low = 0; + ninfo->identity_info.logon_id_high = 0; + ninfo->identity_info.workstation.string = talloc_strdup(state, workstation); + + SMB_ASSERT(chal.length == sizeof(ninfo->challenge)); + memcpy(ninfo->challenge, chal.data, + sizeof(ninfo->challenge)); + + tmp_nt_resp = data_blob_talloc(ninfo, nt_resp.data, nt_resp.length); + if ((nt_resp.data != NULL) && + (tmp_nt_resp.data == NULL)) goto failed; + + tmp_lm_resp = data_blob_talloc(ninfo, lm_resp.data, lm_resp.length); + if ((lm_resp.data != NULL) && + (tmp_lm_resp.data == NULL)) goto failed; + + ninfo->nt.length = tmp_nt_resp.length; + ninfo->nt.data = tmp_nt_resp.data; + ninfo->lm.length = tmp_lm_resp.length; + ninfo->lm.data = tmp_lm_resp.data; + + state->unix_username = NULL; + + ctx = wb_sam_logon_send(mem_ctx, service, state->req); + if (ctx == NULL) goto failed; + + composite_continue(result, ctx, pam_auth_crap_recv_logon, state); + return result; + + failed: + talloc_free(result); + return NULL; +} + +/* + NTLM Authentication + + Send of a SamLogon request to authenticate a user. +*/ +static void pam_auth_crap_recv_logon(struct composite_context *ctx) +{ + DATA_BLOB tmp_blob; + enum ndr_err_code ndr_err; + struct netr_SamBaseInfo *base; + struct pam_auth_crap_state *state = + talloc_get_type(ctx->async.private_data, + struct pam_auth_crap_state); + + state->ctx->status = wb_sam_logon_recv(ctx, state, state->req); + if (!composite_is_ok(state->ctx)) return; + + ndr_err = ndr_push_struct_blob( + &tmp_blob, state, lp_iconv_convenience(state->lp_ctx), + state->req->out.validation.sam3, + (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + state->ctx->status = ndr_map_error2ntstatus(ndr_err); + if (!composite_is_ok(state->ctx)) return; + } + + /* The Samba3 protocol is a bit broken (due to non-IDL + * heritage, so for compatability we must add a non-zero 4 + * bytes to the info3 */ + state->info3 = data_blob_talloc(state, NULL, tmp_blob.length+4); + if (composite_nomem(state->info3.data, state->ctx)) return; + + SIVAL(state->info3.data, 0, 1); + memcpy(state->info3.data+4, tmp_blob.data, tmp_blob.length); + + base = &state->req->out.validation.sam3->base; + + state->user_session_key = base->key; + state->lm_key = base->LMSessKey; + + /* Give the caller the most accurate username possible. + * Assists where case sensitive comparisons may be done by our + * ntlm_auth callers */ + if (base->account_name.string) { + state->user_name = base->account_name.string; + talloc_steal(state, base->account_name.string); + } + if (base->domain.string) { + state->domain_name = base->domain.string; + talloc_steal(state, base->domain.string); + } + + state->unix_username = talloc_asprintf(state, "%s%s%s", + state->domain_name, + lp_winbind_separator(state->lp_ctx), + state->user_name); + if (composite_nomem(state->unix_username, state->ctx)) return; + + composite_done(state->ctx); +} + +/* Having received a NTLM authentication reply, parse out the useful + * reply data for the caller */ +NTSTATUS wb_cmd_pam_auth_crap_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + DATA_BLOB *info3, + struct netr_UserSessionKey *user_session_key, + struct netr_LMSessionKey *lm_key, + char **unix_username) +{ + struct pam_auth_crap_state *state = + talloc_get_type(c->private_data, struct pam_auth_crap_state); + NTSTATUS status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + info3->length = state->info3.length; + info3->data = talloc_steal(mem_ctx, state->info3.data); + *user_session_key = state->user_session_key; + *lm_key = state->lm_key; + *unix_username = talloc_steal(mem_ctx, state->unix_username); + } + talloc_free(state); + return status; +} + +/* Handle plaintext authentication, by encrypting the password and + * then sending via the NTLM calls */ + +struct composite_context *wb_cmd_pam_auth_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + struct cli_credentials *credentials) +{ + const char *workstation; + NTSTATUS status; + const char *user, *domain; + DATA_BLOB chal, nt_resp, lm_resp, names_blob; + int flags = CLI_CRED_NTLM_AUTH; + if (lp_client_lanman_auth(service->task->lp_ctx)) { + flags |= CLI_CRED_LANMAN_AUTH; + } + + if (lp_client_ntlmv2_auth(service->task->lp_ctx)) { + flags |= CLI_CRED_NTLMv2_AUTH; + } + + DEBUG(5, ("wbsrv_samba3_pam_auth called\n")); + + chal = data_blob_talloc(mem_ctx, NULL, 8); + if (!chal.data) { + return NULL; + } + generate_random_buffer(chal.data, chal.length); + cli_credentials_get_ntlm_username_domain(credentials, mem_ctx, + &user, &domain); + /* for best compatability with multiple vitual netbios names + * on the host, this should be generated from the + * cli_credentials associated with the machine account */ + workstation = cli_credentials_get_workstation(credentials); + + names_blob = NTLMv2_generate_names_blob( + mem_ctx, + lp_iconv_convenience(service->task->lp_ctx), + cli_credentials_get_workstation(credentials), + cli_credentials_get_domain(credentials)); + + status = cli_credentials_get_ntlm_response( + credentials, mem_ctx, &flags, chal, names_blob, + &lm_resp, &nt_resp, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + return wb_cmd_pam_auth_crap_send(mem_ctx, service, + MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT|MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT /* logon parameters */, + domain, user, workstation, + chal, nt_resp, lm_resp); +} + +NTSTATUS wb_cmd_pam_auth_recv(struct composite_context *c) +{ + struct pam_auth_crap_state *state = + talloc_get_type(c->private_data, struct pam_auth_crap_state); + NTSTATUS status = composite_wait(c); + talloc_free(state); + return status; +} diff --git a/source4/winbind/wb_sam_logon.c b/source4/winbind/wb_sam_logon.c new file mode 100644 index 0000000000..c9203c8bec --- /dev/null +++ b/source4/winbind/wb_sam_logon.c @@ -0,0 +1,166 @@ +/* + Unix SMB/CIFS implementation. + + Do a netr_LogonSamLogon to a remote DC + + Copyright (C) Volker Lendecke 2005 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 + Copyright (C) Stefan Metzmacher 2006 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "auth/credentials/credentials.h" +#include "libcli/auth/libcli_auth.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/winbind.h" + +struct wb_sam_logon_state { + struct composite_context *ctx; + + struct winbind_SamLogon *req; + + struct creds_CredentialState *creds_state; + struct netr_Authenticator auth1, auth2; + + TALLOC_CTX *r_mem_ctx; + struct netr_LogonSamLogon r; +}; + +static void wb_sam_logon_recv_domain(struct composite_context *ctx); +static void wb_sam_logon_recv_samlogon(struct rpc_request *req); + +/* + Find the connection to the DC (or find an existing connection) +*/ +struct composite_context *wb_sam_logon_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + struct winbind_SamLogon *req) +{ + struct composite_context *c, *creq; + struct wb_sam_logon_state *s; + + c = composite_create(mem_ctx, service->task->event_ctx); + if (!c) return NULL; + + s = talloc_zero(c, struct wb_sam_logon_state); + if (composite_nomem(s, c)) return c; + s->ctx = c; + s->req = req; + + c->private_data = s; + + creq = wb_sid2domain_send(s, service, service->primary_sid); + composite_continue(c, creq, wb_sam_logon_recv_domain, s); + return c; +} + +/* + Having finished making the connection to the DC + Send of a SamLogon request to authenticate a user. +*/ +static void wb_sam_logon_recv_domain(struct composite_context *creq) +{ + struct wb_sam_logon_state *s = talloc_get_type(creq->async.private_data, + struct wb_sam_logon_state); + struct rpc_request *req; + struct wbsrv_domain *domain; + + s->ctx->status = wb_sid2domain_recv(creq, &domain); + if (!composite_is_ok(s->ctx)) return; + + s->creds_state = cli_credentials_get_netlogon_creds(domain->libnet_ctx->cred); + creds_client_authenticator(s->creds_state, &s->auth1); + + s->r.in.server_name = talloc_asprintf(s, "\\\\%s", + dcerpc_server_name(domain->netlogon_pipe)); + if (composite_nomem(s->r.in.server_name, s->ctx)) return; + + s->r.in.computer_name = cli_credentials_get_workstation(domain->libnet_ctx->cred); + s->r.in.credential = &s->auth1; + s->r.in.return_authenticator = &s->auth2; + s->r.in.logon_level = s->req->in.logon_level; + s->r.in.logon = s->req->in.logon; + s->r.in.validation_level = s->req->in.validation_level; + s->r.out.return_authenticator = NULL; + + /* + * use a new talloc context for the LogonSamLogon call + * because then we can just to a talloc_steal on this context + * in the final _recv() function to give the caller all the content of + * the s->r.out.validation + */ + s->r_mem_ctx = talloc_new(s); + if (composite_nomem(s->r_mem_ctx, s->ctx)) return; + + req = dcerpc_netr_LogonSamLogon_send(domain->netlogon_pipe, s->r_mem_ctx, &s->r); + composite_continue_rpc(s->ctx, req, wb_sam_logon_recv_samlogon, s); +} + +/* + NTLM Authentication + + Check the SamLogon reply and decrypt the session keys +*/ +static void wb_sam_logon_recv_samlogon(struct rpc_request *req) +{ + struct wb_sam_logon_state *s = talloc_get_type(req->async.private_data, + struct wb_sam_logon_state); + + s->ctx->status = dcerpc_ndr_request_recv(req); + if (!composite_is_ok(s->ctx)) return; + + s->ctx->status = s->r.out.result; + if (!composite_is_ok(s->ctx)) return; + + if ((s->r.out.return_authenticator == NULL) || + (!creds_client_check(s->creds_state, + &s->r.out.return_authenticator->cred))) { + DEBUG(0, ("Credentials check failed!\n")); + composite_error(s->ctx, NT_STATUS_ACCESS_DENIED); + return; + } + + /* Decrypt the session keys before we reform the info3, so the + * person on the other end of winbindd pipe doesn't have to. + * They won't have the encryption key anyway */ + creds_decrypt_samlogon(s->creds_state, + s->r.in.validation_level, + &s->r.out.validation); + + composite_done(s->ctx); +} + +NTSTATUS wb_sam_logon_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct winbind_SamLogon *req) +{ + struct wb_sam_logon_state *s = talloc_get_type(c->private_data, + struct wb_sam_logon_state); + NTSTATUS status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + talloc_steal(mem_ctx, s->r_mem_ctx); + req->out.validation = s->r.out.validation; + req->out.authoritative = 1; + } + + talloc_free(s); + return status; +} diff --git a/source4/winbind/wb_samba3_cmd.c b/source4/winbind/wb_samba3_cmd.c new file mode 100644 index 0000000000..c2ba55ff18 --- /dev/null +++ b/source4/winbind/wb_samba3_cmd.c @@ -0,0 +1,1114 @@ +/* + Unix SMB/CIFS implementation. + Main winbindd samba3 server routines + + Copyright (C) Stefan Metzmacher 2005 + Copyright (C) Volker Lendecke 2005 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 + + 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 "includes.h" +#include "winbind/wb_server.h" +#include "winbind/wb_async_helpers.h" +#include "param/param.h" +#include "winbind/wb_helper.h" +#include "libcli/composite/composite.h" +#include "version.h" +#include "librpc/gen_ndr/netlogon.h" +#include "libcli/security/security.h" +#include "auth/ntlm/pam_errors.h" +#include "auth/credentials/credentials.h" +#include "smbd/service_task.h" + +/* + Send off the reply to an async Samba3 query, handling filling in the PAM, NTSTATUS and string errors. +*/ + +static void wbsrv_samba3_async_auth_epilogue(NTSTATUS status, + struct wbsrv_samba3_call *s3call) +{ + struct winbindd_response *resp = &s3call->response; + if (!NT_STATUS_IS_OK(status)) { + resp->result = WINBINDD_ERROR; + } else { + resp->result = WINBINDD_OK; + } + + WBSRV_SAMBA3_SET_STRING(resp->data.auth.nt_status_string, + nt_errstr(status)); + WBSRV_SAMBA3_SET_STRING(resp->data.auth.error_string, + get_friendly_nt_error_msg(status)); + + resp->data.auth.pam_error = nt_status_to_pam(status); + resp->data.auth.nt_status = NT_STATUS_V(status); + + wbsrv_samba3_send_reply(s3call); +} + +/* + Send of a generic reply to a Samba3 query +*/ + +static void wbsrv_samba3_async_epilogue(NTSTATUS status, + struct wbsrv_samba3_call *s3call) +{ + struct winbindd_response *resp = &s3call->response; + if (NT_STATUS_IS_OK(status)) { + resp->result = WINBINDD_OK; + } else { + resp->result = WINBINDD_ERROR; + } + + wbsrv_samba3_send_reply(s3call); +} + +/* + Boilerplate commands, simple queries without network traffic +*/ + +NTSTATUS wbsrv_samba3_interface_version(struct wbsrv_samba3_call *s3call) +{ + s3call->response.result = WINBINDD_OK; + s3call->response.data.interface_version = WINBIND_INTERFACE_VERSION; + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_info(struct wbsrv_samba3_call *s3call) +{ + s3call->response.result = WINBINDD_OK; + s3call->response.data.info.winbind_separator = *lp_winbind_separator(s3call->wbconn->lp_ctx); + WBSRV_SAMBA3_SET_STRING(s3call->response.data.info.samba_version, + SAMBA_VERSION_STRING); + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_domain_name(struct wbsrv_samba3_call *s3call) +{ + s3call->response.result = WINBINDD_OK; + WBSRV_SAMBA3_SET_STRING(s3call->response.data.domain_name, + lp_workgroup(s3call->wbconn->lp_ctx)); + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_netbios_name(struct wbsrv_samba3_call *s3call) +{ + s3call->response.result = WINBINDD_OK; + WBSRV_SAMBA3_SET_STRING(s3call->response.data.netbios_name, + lp_netbios_name(s3call->wbconn->lp_ctx)); + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_priv_pipe_dir(struct wbsrv_samba3_call *s3call) +{ + char *path = s3call->wbconn->listen_socket->service->priv_socket_path; + s3call->response.result = WINBINDD_OK; + s3call->response.extra_data.data = path; + + s3call->response.length += strlen(path) + 1; + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_ping(struct wbsrv_samba3_call *s3call) +{ + s3call->response.result = WINBINDD_OK; + return NT_STATUS_OK; +} + +/* Plaintext authentication + + This interface is used by ntlm_auth in it's 'basic' authentication + mode, as well as by pam_winbind to authenticate users where we are + given a plaintext password. +*/ + +static void check_machacc_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_check_machacc(struct wbsrv_samba3_call *s3call) +{ + NTSTATUS status; + struct cli_credentials *creds; + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + + /* Create a credentials structure */ + creds = cli_credentials_init(s3call); + if (creds == NULL) { + return NT_STATUS_NO_MEMORY; + } + + cli_credentials_set_conf(creds, service->task->lp_ctx); + + /* Connect the machine account to the credentials */ + status = cli_credentials_set_machine_account(creds, service->task->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(creds); + return status; + } + + ctx = wb_cmd_pam_auth_send(s3call, service, creds); + + if (!ctx) { + talloc_free(creds); + return NT_STATUS_NO_MEMORY; + } + + ctx->async.fn = check_machacc_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void check_machacc_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + + status = wb_cmd_pam_auth_recv(ctx); + + if (!NT_STATUS_IS_OK(status)) goto done; + + done: + wbsrv_samba3_async_auth_epilogue(status, s3call); +} + +/* + Find the name of a suitable domain controller, by query on the + netlogon pipe to the DC. +*/ + +static void getdcname_recv_dc(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_getdcname(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + + DEBUG(5, ("wbsrv_samba3_getdcname called\n")); + + ctx = wb_cmd_getdcname_send(s3call, service, + s3call->request.domain_name); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = getdcname_recv_dc; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void getdcname_recv_dc(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + const char *dcname; + NTSTATUS status; + + status = wb_cmd_getdcname_recv(ctx, s3call, &dcname); + if (!NT_STATUS_IS_OK(status)) goto done; + + s3call->response.result = WINBINDD_OK; + WBSRV_SAMBA3_SET_STRING(s3call->response.data.dc_name, dcname); + + done: + wbsrv_samba3_async_epilogue(status, s3call); +} + +/* + Lookup a user's domain groups +*/ + +static void userdomgroups_recv_groups(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_userdomgroups(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct dom_sid *sid; + + DEBUG(5, ("wbsrv_samba3_userdomgroups called\n")); + + sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid); + if (sid == NULL) { + DEBUG(5, ("Could not parse sid %s\n", + s3call->request.data.sid)); + return NT_STATUS_NO_MEMORY; + } + + ctx = wb_cmd_userdomgroups_send( + s3call, s3call->wbconn->listen_socket->service, sid); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = userdomgroups_recv_groups; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void userdomgroups_recv_groups(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + int i, num_sids; + struct dom_sid **sids; + char *sids_string; + NTSTATUS status; + + status = wb_cmd_userdomgroups_recv(ctx, s3call, &num_sids, &sids); + if (!NT_STATUS_IS_OK(status)) goto done; + + sids_string = talloc_strdup(s3call, ""); + if (sids_string == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0; i<num_sids; i++) { + sids_string = talloc_asprintf_append_buffer( + sids_string, "%s\n", dom_sid_string(s3call, sids[i])); + } + + if (sids_string == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + s3call->response.result = WINBINDD_OK; + s3call->response.extra_data.data = sids_string; + s3call->response.length += strlen(sids_string)+1; + s3call->response.data.num_entries = num_sids; + + done: + wbsrv_samba3_async_epilogue(status, s3call); +} + +/* + Lookup the list of SIDs for a user +*/ +static void usersids_recv_sids(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_usersids(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct dom_sid *sid; + + DEBUG(5, ("wbsrv_samba3_usersids called\n")); + + sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid); + if (sid == NULL) { + DEBUG(5, ("Could not parse sid %s\n", + s3call->request.data.sid)); + return NT_STATUS_NO_MEMORY; + } + + ctx = wb_cmd_usersids_send( + s3call, s3call->wbconn->listen_socket->service, sid); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = usersids_recv_sids; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void usersids_recv_sids(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + int i, num_sids; + struct dom_sid **sids; + char *sids_string; + NTSTATUS status; + + status = wb_cmd_usersids_recv(ctx, s3call, &num_sids, &sids); + if (!NT_STATUS_IS_OK(status)) goto done; + + sids_string = talloc_strdup(s3call, ""); + if (sids_string == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0; i<num_sids; i++) { + sids_string = talloc_asprintf_append_buffer( + sids_string, "%s\n", dom_sid_string(s3call, sids[i])); + if (sids_string == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + } + + s3call->response.result = WINBINDD_OK; + s3call->response.extra_data.data = sids_string; + s3call->response.length += strlen(sids_string); + s3call->response.data.num_entries = num_sids; + + /* Hmmmm. Nasty protocol -- who invented the zeros between the + * SIDs? Hmmm. Could have been me -- vl */ + + while (*sids_string != '\0') { + if ((*sids_string) == '\n') { + *sids_string = '\0'; + } + sids_string += 1; + } + + done: + wbsrv_samba3_async_epilogue(status, s3call); +} + +/* + Lookup a DOMAIN\\user style name, and return a SID +*/ + +static void lookupname_recv_sid(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_lookupname(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + + DEBUG(5, ("wbsrv_samba3_lookupname called\n")); + + ctx = wb_cmd_lookupname_send(s3call, service, + s3call->request.data.name.dom_name, + s3call->request.data.name.name); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + /* setup the callbacks */ + ctx->async.fn = lookupname_recv_sid; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void lookupname_recv_sid(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + struct wb_sid_object *sid; + NTSTATUS status; + + status = wb_cmd_lookupname_recv(ctx, s3call, &sid); + if (!NT_STATUS_IS_OK(status)) goto done; + + s3call->response.result = WINBINDD_OK; + s3call->response.data.sid.type = sid->type; + WBSRV_SAMBA3_SET_STRING(s3call->response.data.sid.sid, + dom_sid_string(s3call, sid->sid)); + + done: + wbsrv_samba3_async_epilogue(status, s3call); +} + +/* + Lookup a SID, and return a DOMAIN\\user style name +*/ + +static void lookupsid_recv_name(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_lookupsid(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + struct dom_sid *sid; + + DEBUG(5, ("wbsrv_samba3_lookupsid called\n")); + + sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid); + if (sid == NULL) { + DEBUG(5, ("Could not parse sid %s\n", + s3call->request.data.sid)); + return NT_STATUS_NO_MEMORY; + } + + ctx = wb_cmd_lookupsid_send(s3call, service, sid); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + /* setup the callbacks */ + ctx->async.fn = lookupsid_recv_name; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void lookupsid_recv_name(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + struct wb_sid_object *sid; + NTSTATUS status; + + status = wb_cmd_lookupsid_recv(ctx, s3call, &sid); + if (!NT_STATUS_IS_OK(status)) goto done; + + s3call->response.result = WINBINDD_OK; + s3call->response.data.name.type = sid->type; + WBSRV_SAMBA3_SET_STRING(s3call->response.data.name.dom_name, + sid->domain); + WBSRV_SAMBA3_SET_STRING(s3call->response.data.name.name, sid->name); + + done: + wbsrv_samba3_async_epilogue(status, s3call); +} + +/* + Challenge-response authentication. This interface is used by + ntlm_auth and the smbd auth subsystem to pass NTLM authentication + requests along a common pipe to the domain controller. + + The return value (in the async reply) may include the 'info3' + (effectivly most things you would want to know about the user), or + the NT and LM session keys seperated. +*/ + +static void pam_auth_crap_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_pam_auth_crap(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + DATA_BLOB chal, nt_resp, lm_resp; + + DEBUG(5, ("wbsrv_samba3_pam_auth_crap called\n")); + + chal.data = s3call->request.data.auth_crap.chal; + chal.length = sizeof(s3call->request.data.auth_crap.chal); + nt_resp.data = (uint8_t *)s3call->request.data.auth_crap.nt_resp; + nt_resp.length = s3call->request.data.auth_crap.nt_resp_len; + lm_resp.data = (uint8_t *)s3call->request.data.auth_crap.lm_resp; + lm_resp.length = s3call->request.data.auth_crap.lm_resp_len; + + ctx = wb_cmd_pam_auth_crap_send( + s3call, service, + s3call->request.data.auth_crap.logon_parameters, + s3call->request.data.auth_crap.domain, + s3call->request.data.auth_crap.user, + s3call->request.data.auth_crap.workstation, + chal, nt_resp, lm_resp); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = pam_auth_crap_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void pam_auth_crap_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + DATA_BLOB info3; + struct netr_UserSessionKey user_session_key; + struct netr_LMSessionKey lm_key; + char *unix_username; + + status = wb_cmd_pam_auth_crap_recv(ctx, s3call, &info3, + &user_session_key, &lm_key, &unix_username); + if (!NT_STATUS_IS_OK(status)) goto done; + + if (s3call->request.flags & WBFLAG_PAM_USER_SESSION_KEY) { + memcpy(s3call->response.data.auth.user_session_key, + &user_session_key.key, + sizeof(s3call->response.data.auth.user_session_key)); + } + + if (s3call->request.flags & WBFLAG_PAM_INFO3_NDR) { + s3call->response.extra_data.data = info3.data; + s3call->response.length += info3.length; + } + + if (s3call->request.flags & WBFLAG_PAM_LMKEY) { + memcpy(s3call->response.data.auth.first_8_lm_hash, + lm_key.key, + sizeof(s3call->response.data.auth.first_8_lm_hash)); + } + + if (s3call->request.flags & WBFLAG_PAM_UNIX_NAME) { + s3call->response.extra_data.data = unix_username; + s3call->response.length += strlen(unix_username)+1; + } + + done: + wbsrv_samba3_async_auth_epilogue(status, s3call); +} + +/* Plaintext authentication + + This interface is used by ntlm_auth in it's 'basic' authentication + mode, as well as by pam_winbind to authenticate users where we are + given a plaintext password. +*/ + +static void pam_auth_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_pam_auth(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + struct cli_credentials *credentials; + char *user, *domain; + + if (!wb_samba3_split_username(s3call, s3call->wbconn->lp_ctx, + s3call->request.data.auth.user, + &domain, &user)) { + return NT_STATUS_NO_SUCH_USER; + } + + credentials = cli_credentials_init(s3call); + if (!credentials) { + return NT_STATUS_NO_MEMORY; + } + cli_credentials_set_conf(credentials, service->task->lp_ctx); + cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED); + cli_credentials_set_username(credentials, user, CRED_SPECIFIED); + + cli_credentials_set_password(credentials, s3call->request.data.auth.pass, CRED_SPECIFIED); + + ctx = wb_cmd_pam_auth_send(s3call, service, credentials); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = pam_auth_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void pam_auth_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + + status = wb_cmd_pam_auth_recv(ctx); + + if (!NT_STATUS_IS_OK(status)) goto done; + + done: + wbsrv_samba3_async_auth_epilogue(status, s3call); +} + +/* + List trusted domains +*/ + +static void list_trustdom_recv_doms(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_list_trustdom(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + + DEBUG(5, ("wbsrv_samba3_list_trustdom called\n")); + + ctx = wb_cmd_list_trustdoms_send(s3call, service); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = list_trustdom_recv_doms; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void list_trustdom_recv_doms(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + int i, num_domains; + struct wb_dom_info **domains; + NTSTATUS status; + char *result; + + status = wb_cmd_list_trustdoms_recv(ctx, s3call, &num_domains, + &domains); + if (!NT_STATUS_IS_OK(status)) goto done; + + result = talloc_strdup(s3call, ""); + if (result == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0; i<num_domains; i++) { + result = talloc_asprintf_append_buffer( + result, "%s\\%s\\%s", + domains[i]->name, domains[i]->name, + dom_sid_string(s3call, domains[i]->sid)); + } + + if (result == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + s3call->response.result = WINBINDD_OK; + if (num_domains > 0) { + s3call->response.extra_data.data = result; + s3call->response.length += strlen(result)+1; + } + + done: + wbsrv_samba3_async_epilogue(status, s3call); +} + + +/* List users */ + +static void list_users_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_list_users(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + + DEBUG(5, ("wbsrv_samba3_list_users called\n")); + + ctx = wb_cmd_list_users_send(s3call, service, + s3call->request.domain_name); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = list_users_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void list_users_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + uint32_t extra_data_len; + char *extra_data; + NTSTATUS status; + + DEBUG(5, ("list_users_recv called\n")); + + status = wb_cmd_list_users_recv(ctx, s3call, &extra_data_len, + &extra_data); + + if (NT_STATUS_IS_OK(status)) { + s3call->response.extra_data.data = extra_data; + s3call->response.length += extra_data_len; + if (extra_data) { + s3call->response.length += 1; + } + } + + wbsrv_samba3_async_epilogue(status, s3call); +} + +/* NSS calls */ + +static void getpwnam_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_getpwnam(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + + DEBUG(5, ("wbsrv_samba3_getpwnam called\n")); + + ctx = wb_cmd_getpwnam_send(s3call, service, + s3call->request.data.username); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = getpwnam_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void getpwnam_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + struct winbindd_pw *pw; + + DEBUG(5, ("getpwnam_recv called\n")); + + status = wb_cmd_getpwnam_recv(ctx, s3call, &pw); + if(NT_STATUS_IS_OK(status)) + s3call->response.data.pw = *pw; + + wbsrv_samba3_async_epilogue(status, s3call); +} + +static void getpwuid_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_getpwuid(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = s3call->wbconn->listen_socket->service; + + DEBUG(5, ("wbsrv_samba3_getpwuid called\n")); + + ctx = wb_cmd_getpwuid_send(s3call, service, + s3call->request.data.uid); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = getpwuid_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void getpwuid_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + struct winbindd_pw *pw; + + DEBUG(5, ("getpwuid_recv called\n")); + + status = wb_cmd_getpwuid_recv(ctx, s3call, &pw); + if (NT_STATUS_IS_OK(status)) + s3call->response.data.pw = *pw; + + wbsrv_samba3_async_epilogue(status, s3call); +} + +static void setpwent_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_setpwent(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = s3call->wbconn->listen_socket->service; + + DEBUG(5, ("wbsrv_samba3_setpwent called\n")); + + ctx = wb_cmd_setpwent_send(s3call, service); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = setpwent_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void setpwent_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + struct wbsrv_pwent *pwent; + + DEBUG(5, ("setpwent_recv called\n")); + + status = wb_cmd_setpwent_recv(ctx, s3call->wbconn, &pwent); + if (NT_STATUS_IS_OK(status)) { + s3call->wbconn->protocol_private_data = pwent; + } + + wbsrv_samba3_async_epilogue(status, s3call); +} + +static void getpwent_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_getpwent(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = s3call->wbconn->listen_socket->service; + struct wbsrv_pwent *pwent; + + DEBUG(5, ("wbsrv_samba3_getpwent called\n")); + + NT_STATUS_HAVE_NO_MEMORY(s3call->wbconn->protocol_private_data); + + pwent = talloc_get_type(s3call->wbconn->protocol_private_data, + struct wbsrv_pwent); + NT_STATUS_HAVE_NO_MEMORY(pwent); + + ctx = wb_cmd_getpwent_send(s3call, service, pwent, + s3call->request.data.num_entries); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = getpwent_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; +} + +static void getpwent_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + struct winbindd_pw *pw; + uint32_t num_users; + + DEBUG(5, ("getpwent_recv called\n")); + + status = wb_cmd_getpwent_recv(ctx, s3call, &pw, &num_users); + if (NT_STATUS_IS_OK(status)) { + uint32_t extra_len = sizeof(struct winbindd_pw) * num_users; + + s3call->response.data.num_entries = num_users; + s3call->response.extra_data.data = pw; + s3call->response.length += extra_len; + } + + wbsrv_samba3_async_epilogue(status, s3call); +} + +NTSTATUS wbsrv_samba3_endpwent(struct wbsrv_samba3_call *s3call) +{ + struct wbsrv_pwent *pwent = + talloc_get_type(s3call->wbconn->protocol_private_data, + struct wbsrv_pwent); + DEBUG(5, ("wbsrv_samba3_endpwent called\n")); + + talloc_free(pwent); + + s3call->wbconn->protocol_private_data = NULL; + s3call->response.result = WINBINDD_OK; + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_getgrnam(struct wbsrv_samba3_call *s3call) +{ + DEBUG(5, ("wbsrv_samba3_getgrnam called\n")); + s3call->response.result = WINBINDD_ERROR; + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_getgrgid(struct wbsrv_samba3_call *s3call) +{ + DEBUG(5, ("wbsrv_samba3_getgrgid called\n")); + s3call->response.result = WINBINDD_ERROR; + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_getgroups(struct wbsrv_samba3_call *s3call) +{ + DEBUG(5, ("wbsrv_samba3_getgroups called\n")); + s3call->response.result = WINBINDD_ERROR; + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_setgrent(struct wbsrv_samba3_call *s3call) +{ + DEBUG(5, ("wbsrv_samba3_setgrent called\n")); + s3call->response.result = WINBINDD_OK; + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_getgrent(struct wbsrv_samba3_call *s3call) +{ + DEBUG(5, ("wbsrv_samba3_getgrent called\n")); + s3call->response.result = WINBINDD_ERROR; + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_endgrent(struct wbsrv_samba3_call *s3call) +{ + DEBUG(5, ("wbsrv_samba3_endgrent called\n")); + s3call->response.result = WINBINDD_OK; + return NT_STATUS_OK; +} + +static void sid2uid_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_sid2uid(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + struct dom_sid *sid; + + DEBUG(5, ("wbsrv_samba3_sid2uid called\n")); + + sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid); + NT_STATUS_HAVE_NO_MEMORY(sid); + + ctx = wb_sid2uid_send(s3call, service, sid); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = sid2uid_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; + +} + +static void sid2uid_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + + DEBUG(5, ("sid2uid_recv called\n")); + + status = wb_sid2uid_recv(ctx, &s3call->response.data.uid); + + wbsrv_samba3_async_epilogue(status, s3call); +} + +static void sid2gid_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_sid2gid(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + struct dom_sid *sid; + + DEBUG(5, ("wbsrv_samba3_sid2gid called\n")); + + sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid); + NT_STATUS_HAVE_NO_MEMORY(sid); + + ctx = wb_sid2gid_send(s3call, service, sid); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = sid2gid_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; + +} + +static void sid2gid_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + + DEBUG(5, ("sid2gid_recv called\n")); + + status = wb_sid2gid_recv(ctx, &s3call->response.data.gid); + + wbsrv_samba3_async_epilogue(status, s3call); +} + +static void uid2sid_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_uid2sid(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + + DEBUG(5, ("wbsrv_samba3_uid2sid called\n")); + + ctx = wb_uid2sid_send(s3call, service, s3call->request.data.uid); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = uid2sid_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; + +} + +static void uid2sid_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + struct dom_sid *sid; + char *sid_str; + + DEBUG(5, ("uid2sid_recv called\n")); + + status = wb_uid2sid_recv(ctx, s3call, &sid); + if(NT_STATUS_IS_OK(status)) { + sid_str = dom_sid_string(s3call, sid); + + /* If the conversion failed, bail out with a failure. */ + if (sid_str == NULL) + wbsrv_samba3_async_epilogue(NT_STATUS_NO_MEMORY,s3call); + + /* But we assume this worked, so we'll set the string. Work + * done. */ + WBSRV_SAMBA3_SET_STRING(s3call->response.data.sid.sid, sid_str); + s3call->response.data.sid.type = SID_NAME_USER; + } + + wbsrv_samba3_async_epilogue(status, s3call); +} + +static void gid2sid_recv(struct composite_context *ctx); + +NTSTATUS wbsrv_samba3_gid2sid(struct wbsrv_samba3_call *s3call) +{ + struct composite_context *ctx; + struct wbsrv_service *service = + s3call->wbconn->listen_socket->service; + + DEBUG(5, ("wbsrv_samba3_gid2sid called\n")); + + ctx = wb_gid2sid_send(s3call, service, s3call->request.data.gid); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = gid2sid_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; + +} + +static void gid2sid_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + struct dom_sid *sid; + char *sid_str; + + DEBUG(5, ("gid2sid_recv called\n")); + + status = wb_gid2sid_recv(ctx, s3call, &sid); + if(NT_STATUS_IS_OK(status)) { + sid_str = dom_sid_string(s3call, sid); + + if (sid_str == NULL) + wbsrv_samba3_async_epilogue(NT_STATUS_NO_MEMORY,s3call); + + WBSRV_SAMBA3_SET_STRING(s3call->response.data.sid.sid, sid_str); + s3call->response.data.sid.type = SID_NAME_DOMAIN; + } + + wbsrv_samba3_async_epilogue(status, s3call); +} + diff --git a/source4/winbind/wb_samba3_protocol.c b/source4/winbind/wb_samba3_protocol.c new file mode 100644 index 0000000000..3b97dff276 --- /dev/null +++ b/source4/winbind/wb_samba3_protocol.c @@ -0,0 +1,300 @@ +/* + Unix SMB/CIFS implementation. + Main winbindd samba3 server routines + + Copyright (C) Stefan Metzmacher 2005 + Copyright (C) Volker Lendecke 2005 + + 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 "includes.h" +#include "winbind/wb_server.h" +#include "smbd/service_stream.h" +#include "lib/stream/packet.h" + +/* + work out if a packet is complete for protocols that use a 32 bit host byte + order length +*/ +NTSTATUS wbsrv_samba3_packet_full_request(void *private, DATA_BLOB blob, size_t *size) +{ + uint32_t *len; + if (blob.length < 4) { + return STATUS_MORE_ENTRIES; + } + len = (uint32_t *)blob.data; + *size = (*len); + if (*size > blob.length) { + return STATUS_MORE_ENTRIES; + } + return NT_STATUS_OK; +} + + +NTSTATUS wbsrv_samba3_pull_request(DATA_BLOB blob, struct wbsrv_connection *wbconn, + struct wbsrv_samba3_call **_call) +{ + struct wbsrv_samba3_call *call; + + if (blob.length != sizeof(call->request)) { + DEBUG(0,("wbsrv_samba3_pull_request: invalid blob length %lu should be %lu\n" + " make sure you use the correct winbind client tools!\n", + (long)blob.length, (long)sizeof(call->request))); + return NT_STATUS_INVALID_PARAMETER; + } + + call = talloc_zero(wbconn, struct wbsrv_samba3_call); + NT_STATUS_HAVE_NO_MEMORY(call); + + /* the packet layout is the same as the in memory layout of the request, so just copy it */ + memcpy(&call->request, blob.data, sizeof(call->request)); + + call->wbconn = wbconn; + call->event_ctx = call->wbconn->conn->event.ctx; + + *_call = call; + return NT_STATUS_OK; +} + +NTSTATUS wbsrv_samba3_handle_call(struct wbsrv_samba3_call *s3call) +{ + DEBUG(10, ("Got winbind samba3 request %d\n", s3call->request.cmd)); + + s3call->response.length = sizeof(s3call->response); + + switch(s3call->request.cmd) { + case WINBINDD_INTERFACE_VERSION: + return wbsrv_samba3_interface_version(s3call); + + case WINBINDD_CHECK_MACHACC: + return wbsrv_samba3_check_machacc(s3call); + + case WINBINDD_PING: + return wbsrv_samba3_ping(s3call); + + case WINBINDD_INFO: + return wbsrv_samba3_info(s3call); + + case WINBINDD_DOMAIN_NAME: + return wbsrv_samba3_domain_name(s3call); + + case WINBINDD_NETBIOS_NAME: + return wbsrv_samba3_netbios_name(s3call); + + case WINBINDD_PRIV_PIPE_DIR: + return wbsrv_samba3_priv_pipe_dir(s3call); + + case WINBINDD_LOOKUPNAME: + return wbsrv_samba3_lookupname(s3call); + + case WINBINDD_LOOKUPSID: + return wbsrv_samba3_lookupsid(s3call); + + case WINBINDD_PAM_AUTH: + return wbsrv_samba3_pam_auth(s3call); + + case WINBINDD_PAM_AUTH_CRAP: + return wbsrv_samba3_pam_auth_crap(s3call); + + case WINBINDD_GETDCNAME: + return wbsrv_samba3_getdcname(s3call); + + case WINBINDD_GETUSERDOMGROUPS: + return wbsrv_samba3_userdomgroups(s3call); + + case WINBINDD_GETUSERSIDS: + return wbsrv_samba3_usersids(s3call); + + case WINBINDD_LIST_TRUSTDOM: + return wbsrv_samba3_list_trustdom(s3call); + + case WINBINDD_LIST_USERS: + return wbsrv_samba3_list_users(s3call); + + case WINBINDD_GETPWNAM: + return wbsrv_samba3_getpwnam(s3call); + + case WINBINDD_GETPWUID: + return wbsrv_samba3_getpwuid(s3call); + + case WINBINDD_SETPWENT: + return wbsrv_samba3_setpwent(s3call); + + case WINBINDD_GETPWENT: + return wbsrv_samba3_getpwent(s3call); + + case WINBINDD_ENDPWENT: + return wbsrv_samba3_endpwent(s3call); + + case WINBINDD_GETGRNAM: + return wbsrv_samba3_getgrnam(s3call); + + case WINBINDD_GETGRGID: + return wbsrv_samba3_getgrgid(s3call); + + case WINBINDD_GETGROUPS: + return wbsrv_samba3_getgroups(s3call); + + case WINBINDD_SETGRENT: + return wbsrv_samba3_setgrent(s3call); + + case WINBINDD_GETGRENT: + return wbsrv_samba3_getgrent(s3call); + + case WINBINDD_ENDGRENT: + return wbsrv_samba3_endgrent(s3call); + + case WINBINDD_SID_TO_UID: + case WINBINDD_DUAL_SID2UID: + return wbsrv_samba3_sid2uid(s3call); + + case WINBINDD_SID_TO_GID: + case WINBINDD_DUAL_SID2GID: + return wbsrv_samba3_sid2gid(s3call); + + case WINBINDD_UID_TO_SID: + case WINBINDD_DUAL_UID2SID: + return wbsrv_samba3_uid2sid(s3call); + + case WINBINDD_GID_TO_SID: + case WINBINDD_DUAL_GID2SID: + return wbsrv_samba3_gid2sid(s3call); + + /* Unimplemented commands */ + + case WINBINDD_PAM_CHAUTHTOK: + case WINBINDD_PAM_LOGOFF: + case WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP: + case WINBINDD_LIST_GROUPS: + case WINBINDD_LOOKUPRIDS: + case WINBINDD_SIDS_TO_XIDS: + case WINBINDD_ALLOCATE_UID: + case WINBINDD_ALLOCATE_GID: + case WINBINDD_SET_MAPPING: + case WINBINDD_SET_HWM: + case WINBINDD_DUMP_MAPS: + case WINBINDD_DOMAIN_INFO: + case WINBINDD_SHOW_SEQUENCE: + case WINBINDD_WINS_BYIP: + case WINBINDD_WINS_BYNAME: + case WINBINDD_GETGRLST: + case WINBINDD_INIT_CONNECTION: + case WINBINDD_DUAL_SIDS2XIDS: + case WINBINDD_DUAL_SET_MAPPING: + case WINBINDD_DUAL_SET_HWM: + case WINBINDD_DUAL_DUMP_MAPS: + case WINBINDD_DUAL_UID2NAME: + case WINBINDD_DUAL_NAME2UID: + case WINBINDD_DUAL_GID2NAME: + case WINBINDD_DUAL_NAME2GID: + case WINBINDD_DUAL_USERINFO: + case WINBINDD_DUAL_GETSIDALIASES: + case WINBINDD_CCACHE_NTLMAUTH: + case WINBINDD_NUM_CMDS: + DEBUG(10, ("Unimplemented winbind samba3 request %d\n", + s3call->request.cmd)); + break; + } + + s3call->response.result = WINBINDD_ERROR; + return NT_STATUS_OK; +} + +static NTSTATUS wbsrv_samba3_push_reply(struct wbsrv_samba3_call *call, TALLOC_CTX *mem_ctx, DATA_BLOB *_blob) +{ + DATA_BLOB blob; + uint8_t *extra_data; + size_t extra_data_len = 0; + + extra_data = (uint8_t *)call->response.extra_data.data; + if (extra_data != NULL) { + extra_data_len = call->response.length - + sizeof(call->response); + } + + blob = data_blob_talloc(mem_ctx, NULL, call->response.length); + NT_STATUS_HAVE_NO_MEMORY(blob.data); + + /* don't push real pointer values into sockets */ + if (extra_data) { + call->response.extra_data.data = (void *)0xFFFFFFFF; + } + memcpy(blob.data, &call->response, sizeof(call->response)); + /* set back the pointer */ + call->response.extra_data.data = extra_data; + + if (extra_data) { + memcpy(blob.data + sizeof(call->response), extra_data, extra_data_len); + } + + *_blob = blob; + return NT_STATUS_OK; +} + +/* + * queue a wbsrv_call reply on a wbsrv_connection + * NOTE: that this implies talloc_free(call), + * use talloc_reference(call) if you need it after + * calling wbsrv_queue_reply + */ +NTSTATUS wbsrv_samba3_send_reply(struct wbsrv_samba3_call *call) +{ + struct wbsrv_connection *wbconn = call->wbconn; + DATA_BLOB rep; + NTSTATUS status; + + status = wbsrv_samba3_push_reply(call, call, &rep); + NT_STATUS_NOT_OK_RETURN(status); + + status = packet_send(call->wbconn->packet, rep); + + talloc_free(call); + + if (!NT_STATUS_IS_OK(status)) { + wbsrv_terminate_connection(wbconn, + "failed to packet_send winbindd reply"); + return status; + } + /* the call isn't needed any more */ + return status; +} + +NTSTATUS wbsrv_samba3_process(void *private, DATA_BLOB blob) +{ + NTSTATUS status; + struct wbsrv_connection *wbconn = talloc_get_type(private, + struct wbsrv_connection); + struct wbsrv_samba3_call *call; + status = wbsrv_samba3_pull_request(blob, wbconn, &call); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = wbsrv_samba3_handle_call(call); + + if (!NT_STATUS_IS_OK(status)) { + talloc_free(call); + return status; + } + + if (call->flags & WBSRV_CALL_FLAGS_REPLY_ASYNC) { + return NT_STATUS_OK; + } + + status = wbsrv_samba3_send_reply(call); + return status; +} + diff --git a/source4/winbind/wb_server.c b/source4/winbind/wb_server.c new file mode 100644 index 0000000000..d56a82ea18 --- /dev/null +++ b/source4/winbind/wb_server.c @@ -0,0 +1,227 @@ +/* + Unix SMB/CIFS implementation. + Main winbindd server routines + + Copyright (C) Stefan Metzmacher 2005 + Copyright (C) Andrew Tridgell 2005 + + 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 "includes.h" +#include "lib/socket/socket.h" +#include "lib/util/dlinklist.h" +#include "lib/events/events.h" +#include "smbd/service_task.h" +#include "smbd/process_model.h" +#include "smbd/service_stream.h" +#include "nsswitch/winbind_nss_config.h" +#include "winbind/wb_server.h" +#include "lib/stream/packet.h" +#include "smbd/service.h" +#include "param/secrets.h" +#include "param/param.h" + +void wbsrv_terminate_connection(struct wbsrv_connection *wbconn, const char *reason) +{ + stream_terminate_connection(wbconn->conn, reason); +} + +/* + called on a tcp recv error +*/ +static void wbsrv_recv_error(void *private, NTSTATUS status) +{ + struct wbsrv_connection *wbconn = talloc_get_type(private, struct wbsrv_connection); + wbsrv_terminate_connection(wbconn, nt_errstr(status)); +} + +static void wbsrv_accept(struct stream_connection *conn) +{ + struct wbsrv_listen_socket *listen_socket = talloc_get_type(conn->private, + struct wbsrv_listen_socket); + struct wbsrv_connection *wbconn; + + wbconn = talloc_zero(conn, struct wbsrv_connection); + if (!wbconn) { + stream_terminate_connection(conn, "wbsrv_accept: out of memory"); + return; + } + wbconn->conn = conn; + wbconn->listen_socket = listen_socket; + wbconn->lp_ctx = listen_socket->service->task->lp_ctx; + conn->private = wbconn; + + wbconn->packet = packet_init(wbconn); + if (wbconn->packet == NULL) { + wbsrv_terminate_connection(wbconn, "wbsrv_accept: out of memory"); + return; + } + packet_set_private(wbconn->packet, wbconn); + packet_set_socket(wbconn->packet, conn->socket); + packet_set_callback(wbconn->packet, wbsrv_samba3_process); + packet_set_full_request(wbconn->packet, wbsrv_samba3_packet_full_request); + packet_set_error_handler(wbconn->packet, wbsrv_recv_error); + packet_set_event_context(wbconn->packet, conn->event.ctx); + packet_set_fde(wbconn->packet, conn->event.fde); + packet_set_serialise(wbconn->packet); +} + +/* + receive some data on a winbind connection +*/ +static void wbsrv_recv(struct stream_connection *conn, uint16_t flags) +{ + struct wbsrv_connection *wbconn = talloc_get_type(conn->private, + struct wbsrv_connection); + packet_recv(wbconn->packet); + +} + +/* + called when we can write to a connection +*/ +static void wbsrv_send(struct stream_connection *conn, uint16_t flags) +{ + struct wbsrv_connection *wbconn = talloc_get_type(conn->private, + struct wbsrv_connection); + packet_queue_run(wbconn->packet); +} + +static const struct stream_server_ops wbsrv_ops = { + .name = "winbind samba3 protocol", + .accept_connection = wbsrv_accept, + .recv_handler = wbsrv_recv, + .send_handler = wbsrv_send +}; + +/* + startup the winbind task +*/ +static void winbind_task_init(struct task_server *task) +{ + uint16_t port = 1; + const struct model_ops *model_ops; + NTSTATUS status; + struct wbsrv_service *service; + struct wbsrv_listen_socket *listen_socket; + + task_server_set_title(task, "task[winbind]"); + + /* within the winbind task we want to be a single process, so + ask for the single process model ops and pass these to the + stream_setup_socket() call. */ + model_ops = process_model_byname("single"); + if (!model_ops) { + task_server_terminate(task, + "Can't find 'single' process model_ops"); + return; + } + + /* Make sure the directory for the Samba3 socket exists, and is of the correct permissions */ + if (!directory_create_or_exist(lp_winbindd_socket_directory(task->lp_ctx), geteuid(), 0755)) { + task_server_terminate(task, + "Cannot create winbindd pipe directory"); + return; + } + + /* Make sure the directory for the Samba3 socket exists, and is of the correct permissions */ + if (!directory_create_or_exist(lp_winbindd_privileged_socket_directory(task->lp_ctx), geteuid(), 0750)) { + task_server_terminate(task, + "Cannot create winbindd privileged pipe directory"); + return; + } + + service = talloc_zero(task, struct wbsrv_service); + if (!service) goto nomem; + service->task = task; + + service->primary_sid = secrets_get_domain_sid(service, + task->event_ctx, + task->lp_ctx, + lp_workgroup(task->lp_ctx)); + if (service->primary_sid == NULL) { + task_server_terminate( + task, nt_errstr(NT_STATUS_CANT_ACCESS_DOMAIN_INFO)); + return; + } + + service->idmap_ctx = idmap_init(service, task->event_ctx, task->lp_ctx); + if (service->idmap_ctx == NULL) { + task_server_terminate(task, "Failed to load idmap database"); + return; + } + + /* setup the unprivileged samba3 socket */ + listen_socket = talloc(service, struct wbsrv_listen_socket); + if (!listen_socket) goto nomem; + listen_socket->socket_path = talloc_asprintf(listen_socket, "%s/%s", + lp_winbindd_socket_directory(task->lp_ctx), + WINBINDD_SAMBA3_SOCKET); + if (!listen_socket->socket_path) goto nomem; + listen_socket->service = service; + listen_socket->privileged = false; + status = stream_setup_socket(task->event_ctx, task->lp_ctx, model_ops, + &wbsrv_ops, "unix", + listen_socket->socket_path, &port, + lp_socket_options(task->lp_ctx), + listen_socket); + if (!NT_STATUS_IS_OK(status)) goto listen_failed; + + /* setup the privileged samba3 socket */ + listen_socket = talloc(service, struct wbsrv_listen_socket); + if (!listen_socket) goto nomem; + listen_socket->socket_path + = service->priv_socket_path + = talloc_asprintf(listen_socket, "%s/%s", + lp_winbindd_privileged_socket_directory(task->lp_ctx), + WINBINDD_SAMBA3_SOCKET); + if (!listen_socket->socket_path) goto nomem; + if (!listen_socket->socket_path) goto nomem; + listen_socket->service = service; + listen_socket->privileged = true; + status = stream_setup_socket(task->event_ctx, task->lp_ctx, model_ops, + &wbsrv_ops, "unix", + listen_socket->socket_path, &port, + lp_socket_options(task->lp_ctx), + listen_socket); + if (!NT_STATUS_IS_OK(status)) goto listen_failed; + + status = wbsrv_init_irpc(service); + if (!NT_STATUS_IS_OK(status)) goto irpc_failed; + + return; + +listen_failed: + DEBUG(0,("stream_setup_socket(path=%s) failed - %s\n", + listen_socket->socket_path, nt_errstr(status))); + task_server_terminate(task, nt_errstr(status)); + return; +irpc_failed: + DEBUG(0,("wbsrv_init_irpc() failed - %s\n", + nt_errstr(status))); + task_server_terminate(task, nt_errstr(status)); + return; +nomem: + task_server_terminate(task, nt_errstr(NT_STATUS_NO_MEMORY)); + return; +} + +/* + register ourselves as a available server +*/ +NTSTATUS server_service_winbind_init(void) +{ + return register_server_service("winbind", winbind_task_init); +} diff --git a/source4/winbind/wb_server.h b/source4/winbind/wb_server.h new file mode 100644 index 0000000000..97d7d8151e --- /dev/null +++ b/source4/winbind/wb_server.h @@ -0,0 +1,169 @@ +/* + Unix SMB/CIFS implementation. + Main winbindd server routines + + Copyright (C) Stefan Metzmacher 2005 + Copyright (C) Andrew Tridgell 2005 + + 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 "nsswitch/winbind_nss_config.h" +#include "nsswitch/winbind_struct_protocol.h" +#include "winbind/idmap.h" +#include "libnet/libnet.h" + +#define WINBINDD_SAMBA3_SOCKET "pipe" +/* the privileged socket is in smbd_tmp_dir() */ +#define WINBINDD_SAMBA3_PRIVILEGED_SOCKET "winbind_pipe" + +/* this struct stores global data for the winbind task */ +struct wbsrv_service { + struct task_server *task; + + const struct dom_sid *primary_sid; + struct wbsrv_domain *domains; + struct idmap_context *idmap_ctx; + + const char *priv_socket_path; +}; + +struct wbsrv_samconn { + struct wbsrv_domain *domain; + void *private_data; + + struct composite_context (*seqnum_send)(struct wbsrv_samconn *); + NTSTATUS (*seqnum_recv)(struct composite_context *, uint64_t *); +}; + +struct wb_dom_info { + const char *name; + const char *dns_name; + const struct dom_sid *sid; + + int num_dcs; + struct nbt_dc_name *dcs; +}; + +struct wbsrv_domain { + struct wbsrv_domain *next, *prev; + + struct wb_dom_info *info; + + /* Details for the server we are currently talking to */ + const char *dc_address; + const char *dc_name; + + struct libnet_context *libnet_ctx; + + struct dcerpc_binding *lsa_binding; + + struct dcerpc_binding *samr_binding; + + struct dcerpc_pipe *netlogon_pipe; + struct dcerpc_binding *netlogon_binding; +}; + +/* + state of a listen socket and it's protocol information +*/ +struct wbsrv_listen_socket { + const char *socket_path; + struct wbsrv_service *service; + bool privileged; +}; + +/* + state of an open winbind connection +*/ +struct wbsrv_connection { + /* stream connection we belong to */ + struct stream_connection *conn; + + /* the listening socket we belong to, it holds protocol hooks */ + struct wbsrv_listen_socket *listen_socket; + + /* storage for protocol specific data */ + void *protocol_private_data; + + /* how many calls are pending */ + uint32_t pending_calls; + + struct packet_context *packet; + + struct loadparm_context *lp_ctx; +}; + +#define WBSRV_SAMBA3_SET_STRING(dest, src) do { \ + safe_strcpy(dest, src, sizeof(dest)-1);\ +} while(0) + +/* + state of a pwent query +*/ +struct wbsrv_pwent { + /* Current UserList structure, contains 1+ user structs */ + struct libnet_UserList *user_list; + + /* Index of the next user struct in the current UserList struct */ + uint32_t page_index; + + /* The libnet_ctx to use for the libnet_UserList call */ + struct libnet_context *libnet_ctx; +}; + +/* + state of one request + + NOTE about async replies: + if the backend wants to reply later: + + - it should set the WBSRV_CALL_FLAGS_REPLY_ASYNC flag, and may set a + talloc_destructor on the this structure or on the private_data (if it's a + talloc child of this structure), so that wbsrv_terminate_connection + called by another call clean up the whole connection correct. + - When the backend is ready to reply it should call wbsrv_send_reply(call), + wbsrv_send_reply implies talloc_free(call), so the backend should use + talloc_reference(call), if it needs it later. + - If wbsrv_send_reply doesn't return NT_STATUS_OK, the backend function + should call, wbsrv_terminate_connection(call->wbconn, nt_errstr(status)); + return; + +*/ +struct wbsrv_samba3_call { +#define WBSRV_CALL_FLAGS_REPLY_ASYNC 0x00000001 + uint32_t flags; + + /* the connection the call belongs to */ + struct wbsrv_connection *wbconn; + + /* the backend should use this event context */ + struct event_context *event_ctx; + + /* here the backend can store stuff like composite_context's ... */ + void *private_data; + + /* the request structure of the samba3 protocol */ + struct winbindd_request request; + + /* the response structure of the samba3 protocol*/ + struct winbindd_response response; +}; + +struct netr_LMSessionKey; +struct netr_UserSessionKey; +struct winbind_SamLogon; + +#include "winbind/wb_async_helpers.h" +#include "winbind/wb_proto.h" diff --git a/source4/winbind/wb_sid2domain.c b/source4/winbind/wb_sid2domain.c new file mode 100644 index 0000000000..fcf02cedcd --- /dev/null +++ b/source4/winbind/wb_sid2domain.c @@ -0,0 +1,212 @@ +/* + Unix SMB/CIFS implementation. + + Find and init a domain struct for a SID + + Copyright (C) Volker Lendecke 2005 + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "winbind/wb_async_helpers.h" +#include "libcli/security/security.h" +#include "lib/util/dlinklist.h" +#include "param/param.h" + +static struct wbsrv_domain *find_domain_from_sid(struct wbsrv_service *service, + const struct dom_sid *sid) +{ + struct wbsrv_domain *domain; + + for (domain = service->domains; domain!=NULL; domain = domain->next) { + if (dom_sid_equal(domain->info->sid, sid)) { + break; + } + if (dom_sid_in_domain(domain->info->sid, sid)) { + break; + } + } + return domain; +} + +struct sid2domain_state { + struct composite_context *ctx; + struct wbsrv_service *service; + struct dom_sid *sid; + + struct wbsrv_domain *domain; +}; + +static void sid2domain_recv_dom_info(struct composite_context *ctx); +static void sid2domain_recv_name(struct composite_context *ctx); +static void sid2domain_recv_trusted_dom_info(struct composite_context *ctx); +static void sid2domain_recv_init(struct composite_context *ctx); + +struct composite_context *wb_sid2domain_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const struct dom_sid *sid) +{ + struct composite_context *result, *ctx; + struct sid2domain_state *state; + + result = composite_create(mem_ctx, service->task->event_ctx); + if (result == NULL) goto failed; + + state = talloc(result, struct sid2domain_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->service = service; + state->sid = dom_sid_dup(state, sid); + if (state->sid == NULL) goto failed; + + state->domain = find_domain_from_sid(service, sid); + if (state->domain != NULL) { + result->status = NT_STATUS_OK; + composite_done(result); + return result; + } + + if (dom_sid_equal(service->primary_sid, sid) || + dom_sid_in_domain(service->primary_sid, sid)) { + ctx = wb_get_dom_info_send(state, service, lp_workgroup(service->task->lp_ctx), + service->primary_sid); + if (ctx == NULL) goto failed; + ctx->async.fn = sid2domain_recv_dom_info; + ctx->async.private_data = state; + return result; + } + + ctx = wb_cmd_lookupsid_send(state, service, state->sid); + if (ctx == NULL) goto failed; + composite_continue(result, ctx, sid2domain_recv_name, state); + + return result; + + failed: + talloc_free(result); + return NULL; + +} + +static void sid2domain_recv_dom_info(struct composite_context *ctx) +{ + struct sid2domain_state *state = + talloc_get_type(ctx->async.private_data, + struct sid2domain_state); + struct wb_dom_info *info; + + state->ctx->status = wb_get_dom_info_recv(ctx, state, &info); + if (!composite_is_ok(state->ctx)) return; + + ctx = wb_init_domain_send(state, state->service, info); + + composite_continue(state->ctx, ctx, sid2domain_recv_init, state); +} + +static void sid2domain_recv_name(struct composite_context *ctx) +{ + struct sid2domain_state *state = + talloc_get_type(ctx->async.private_data, + struct sid2domain_state); + struct wb_sid_object *name; + + state->ctx->status = wb_cmd_lookupsid_recv(ctx, state, &name); + if (!composite_is_ok(state->ctx)) return; + + if (name->type == SID_NAME_UNKNOWN) { + composite_error(state->ctx, NT_STATUS_NO_SUCH_DOMAIN); + return; + } + + if (name->type != SID_NAME_DOMAIN) { + state->sid->num_auths -= 1; + } + + ctx = wb_trusted_dom_info_send(state, state->service, name->domain, + state->sid); + + composite_continue(state->ctx, ctx, sid2domain_recv_trusted_dom_info, + state); +} + +static void sid2domain_recv_trusted_dom_info(struct composite_context *ctx) +{ + struct sid2domain_state *state = + talloc_get_type(ctx->async.private_data, + struct sid2domain_state); + struct wb_dom_info *info; + + state->ctx->status = wb_trusted_dom_info_recv(ctx, state, &info); + if (!composite_is_ok(state->ctx)) return; + + ctx = wb_init_domain_send(state, state->service, info); + + composite_continue(state->ctx, ctx, sid2domain_recv_init, state); +} + +static void sid2domain_recv_init(struct composite_context *ctx) +{ + struct sid2domain_state *state = + talloc_get_type(ctx->async.private_data, + struct sid2domain_state); + struct wbsrv_domain *existing; + + state->ctx->status = wb_init_domain_recv(ctx, state, + &state->domain); + if (!composite_is_ok(state->ctx)) { + DEBUG(10, ("Could not init domain\n")); + return; + } + + existing = find_domain_from_sid(state->service, state->sid); + if (existing != NULL) { + DEBUG(5, ("Initialized domain twice, dropping second one\n")); + talloc_free(state->domain); + state->domain = existing; + } + + talloc_steal(state->service, state->domain); + DLIST_ADD(state->service->domains, state->domain); + + composite_done(state->ctx); +} + +NTSTATUS wb_sid2domain_recv(struct composite_context *ctx, + struct wbsrv_domain **result) +{ + NTSTATUS status = composite_wait(ctx); + if (NT_STATUS_IS_OK(status)) { + struct sid2domain_state *state = + talloc_get_type(ctx->private_data, + struct sid2domain_state); + *result = state->domain; + } + talloc_free(ctx); + return status; +} + +NTSTATUS wb_sid2domain(TALLOC_CTX *mem_ctx, struct wbsrv_service *service, + const struct dom_sid *sid, + struct wbsrv_domain **result) +{ + struct composite_context *c = wb_sid2domain_send(mem_ctx, service, + sid); + return wb_sid2domain_recv(c, result); +} diff --git a/source4/winbind/wb_sid2gid.c b/source4/winbind/wb_sid2gid.c new file mode 100644 index 0000000000..d68956ce85 --- /dev/null +++ b/source4/winbind/wb_sid2gid.c @@ -0,0 +1,109 @@ +/* + Unix SMB/CIFS implementation. + + Map a SID to a gid + + Copyright (C) 2007-2008 Kai Blin + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "winbind/wb_helper.h" +#include "libcli/security/proto.h" +#include "winbind/idmap.h" + +struct sid2gid_state { + struct composite_context *ctx; + struct wbsrv_service *service; + gid_t gid; +}; + +static void sid2gid_recv_gid(struct composite_context *ctx); + +struct composite_context *wb_sid2gid_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, const struct dom_sid *sid) +{ + struct composite_context *result, *ctx; + struct sid2gid_state *state; + struct id_mapping *ids; + + DEBUG(5, ("wb_sid2gid_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(result, struct sid2gid_state); + if(composite_nomem(state, result)) return result; + + state->ctx = result; + result->private_data = state; + state->service = service; + + ids = talloc(result, struct id_mapping); + if (composite_nomem(ids, result)) return result; + + ids->sid = dom_sid_dup(result, sid); + if (composite_nomem(ids->sid, result)) return result; + + ctx = wb_sids2xids_send(result, service, 1, ids); + if (composite_nomem(ctx, result)) return result; + + composite_continue(result, ctx, sid2gid_recv_gid, state); + return result; +} + +static void sid2gid_recv_gid(struct composite_context *ctx) +{ + struct sid2gid_state *state = talloc_get_type(ctx->async.private_data, + struct sid2gid_state); + + struct id_mapping *ids = NULL; + + state->ctx->status = wb_sids2xids_recv(ctx, &ids); + if (!composite_is_ok(state->ctx)) return; + + if (!NT_STATUS_IS_OK(ids->status)) { + composite_error(state->ctx, ids->status); + return; + } + + if (ids->unixid->type == ID_TYPE_BOTH || + ids->unixid->type == ID_TYPE_GID) { + state->gid = ids->unixid->id; + composite_done(state->ctx); + } else { + composite_error(state->ctx, NT_STATUS_INVALID_SID); + } +} + +NTSTATUS wb_sid2gid_recv(struct composite_context *ctx, gid_t *gid) +{ + NTSTATUS status = composite_wait(ctx); + + DEBUG(5, ("wb_sid2gid_recv called\n")); + + if (NT_STATUS_IS_OK(status)) { + struct sid2gid_state *state = + talloc_get_type(ctx->private_data, + struct sid2gid_state); + *gid = state->gid; + } + talloc_free(ctx); + return status; +} + diff --git a/source4/winbind/wb_sid2uid.c b/source4/winbind/wb_sid2uid.c new file mode 100644 index 0000000000..b65e41978c --- /dev/null +++ b/source4/winbind/wb_sid2uid.c @@ -0,0 +1,109 @@ +/* + Unix SMB/CIFS implementation. + + Map a SID to a uid + + Copyright (C) 2007-2008 Kai Blin + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "winbind/wb_helper.h" +#include "libcli/security/proto.h" +#include "winbind/idmap.h" + +struct sid2uid_state { + struct composite_context *ctx; + struct wbsrv_service *service; + uid_t uid; +}; + +static void sid2uid_recv_uid(struct composite_context *ctx); + +struct composite_context *wb_sid2uid_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, const struct dom_sid *sid) +{ + struct composite_context *result, *ctx; + struct sid2uid_state *state; + struct id_mapping *ids; + + DEBUG(5, ("wb_sid2uid_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(result, struct sid2uid_state); + if (composite_nomem(state, result)) return result; + + state->ctx = result; + result->private_data = state; + state->service = service; + + ids = talloc(result, struct id_mapping); + if (composite_nomem(ids, result)) return result; + + ids->sid = dom_sid_dup(result, sid); + if (composite_nomem(ids->sid, result)) return result; + + ctx = wb_sids2xids_send(result, service, 1, ids); + if (composite_nomem(ctx, result)) return result; + + composite_continue(result, ctx, sid2uid_recv_uid, state); + return result; +} + +static void sid2uid_recv_uid(struct composite_context *ctx) +{ + struct sid2uid_state *state = talloc_get_type(ctx->async.private_data, + struct sid2uid_state); + + struct id_mapping *ids = NULL; + + state->ctx->status = wb_sids2xids_recv(ctx, &ids); + if (!composite_is_ok(state->ctx)) return; + + if (!NT_STATUS_IS_OK(ids->status)) { + composite_error(state->ctx, ids->status); + return; + } + + if (ids->unixid->type == ID_TYPE_BOTH || + ids->unixid->type == ID_TYPE_UID) { + state->uid = ids->unixid->id; + composite_done(state->ctx); + } else { + composite_error(state->ctx, NT_STATUS_INVALID_SID); + } +} + +NTSTATUS wb_sid2uid_recv(struct composite_context *ctx, uid_t *uid) +{ + NTSTATUS status = composite_wait(ctx); + + DEBUG(5, ("wb_sid2uid_recv called\n")); + + if (NT_STATUS_IS_OK(status)) { + struct sid2uid_state *state = + talloc_get_type(ctx->private_data, + struct sid2uid_state); + *uid = state->uid; + } + talloc_free(ctx); + return status; +} + diff --git a/source4/winbind/wb_sids2xids.c b/source4/winbind/wb_sids2xids.c new file mode 100644 index 0000000000..6b89caf465 --- /dev/null +++ b/source4/winbind/wb_sids2xids.c @@ -0,0 +1,80 @@ +/* + Unix SMB/CIFS implementation. + + Map SIDs to unixids. + + Copyright (C) 2008 Kai Blin + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "winbind/wb_helper.h" +#include "libcli/security/proto.h" +#include "winbind/idmap.h" + +struct sids2xids_state { + struct composite_context *ctx; + struct wbsrv_service *service; + struct id_mapping *ids; + int count; +}; + +struct composite_context *wb_sids2xids_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + int count, struct id_mapping *ids) +{ + struct composite_context *result; + struct sids2xids_state *state; + + DEBUG(5, ("wb_sids2xids_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(result, struct sids2xids_state); + if (composite_nomem(state, result)) return result; + + state->ctx = result; + result->private_data = state; + state->service = service; + state->count = count; + state->ids = ids; + + state->ctx->status = idmap_sids_to_xids(service->idmap_ctx, mem_ctx, + count, state->ids); + if (!composite_is_ok(state->ctx)) return result; + + composite_done(state->ctx); + return result; +} + +NTSTATUS wb_sids2xids_recv(struct composite_context *ctx, + struct id_mapping **ids) +{ + NTSTATUS status = composite_wait(ctx); + struct sids2xids_state *state = talloc_get_type(ctx->private_data, + struct sids2xids_state); + + DEBUG(5, ("wb_sids2xids_recv called\n")); + + *ids = state->ids; + + talloc_free(ctx); + return status; +} + diff --git a/source4/winbind/wb_uid2sid.c b/source4/winbind/wb_uid2sid.c new file mode 100644 index 0000000000..fd43dd64b9 --- /dev/null +++ b/source4/winbind/wb_uid2sid.c @@ -0,0 +1,110 @@ +/* + Unix SMB/CIFS implementation. + + Command backend for wbinfo -U + + Copyright (C) 2007-2008 Kai Blin + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "winbind/wb_helper.h" +#include "libcli/security/proto.h" +#include "winbind/idmap.h" + +struct uid2sid_state { + struct composite_context *ctx; + struct wbsrv_service *service; + struct dom_sid *sid; +}; + +static void uid2sid_recv_sid(struct composite_context *ctx); + +struct composite_context *wb_uid2sid_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, uid_t uid) +{ + struct composite_context *result, *ctx; + struct uid2sid_state *state; + struct unixid *unixid; + struct id_mapping *ids; + + DEBUG(5, ("wb_uid2sid_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(result, struct uid2sid_state); + if (composite_nomem(state, result)) return result; + + state->ctx = result; + result->private_data = state; + state->service = service; + + unixid = talloc(result, struct unixid); + if (composite_nomem(unixid, result)) return result; + unixid->id = uid; + unixid->type = ID_TYPE_UID; + + ids = talloc(result, struct id_mapping); + if (composite_nomem(ids, result)) return result; + ids->unixid = unixid; + ids->sid = NULL; + + ctx = wb_xids2sids_send(result, service, 1, ids); + if (composite_nomem(ctx, result)) return result; + + composite_continue(result, ctx, uid2sid_recv_sid, state); + return result; +} + +static void uid2sid_recv_sid(struct composite_context *ctx) +{ + struct uid2sid_state *state = talloc_get_type(ctx->async.private_data, + struct uid2sid_state); + struct id_mapping *ids = NULL; + + state->ctx->status = wb_xids2sids_recv(ctx, &ids); + if (!composite_is_ok(state->ctx)) return; + + if (!NT_STATUS_IS_OK(ids->status)) { + composite_error(state->ctx, ids->status); + return; + } + + state->sid = ids->sid; + + composite_done(state->ctx); +} + +NTSTATUS wb_uid2sid_recv(struct composite_context *ctx, TALLOC_CTX *mem_ctx, + struct dom_sid **sid) +{ + NTSTATUS status = composite_wait(ctx); + + DEBUG(5, ("wb_uid2sid_recv called.\n")); + + if (NT_STATUS_IS_OK(status)) { + struct uid2sid_state *state = + talloc_get_type(ctx->private_data, + struct uid2sid_state); + *sid = talloc_steal(mem_ctx, state->sid); + } + talloc_free(ctx); + return status; +} + diff --git a/source4/winbind/wb_utils.c b/source4/winbind/wb_utils.c new file mode 100644 index 0000000000..43effc3028 --- /dev/null +++ b/source4/winbind/wb_utils.c @@ -0,0 +1,49 @@ +/* + Unix SMB/CIFS implementation. + + Utility functions that are not related with async operations. + + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 + + 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 "includes.h" +#include "param/param.h" + + +/* Split a domain\\user string into it's parts, because the client supplies it + * as one string. + * TODO: We probably will need to handle other formats later. */ + +bool wb_samba3_split_username(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, + const char *domuser, + char **domain, char **user) +{ + char *p = strchr(domuser, *lp_winbind_separator(lp_ctx)); + + if (p == NULL) { + *domain = talloc_strdup(mem_ctx, lp_workgroup(lp_ctx)); + } else { + *domain = talloc_strndup(mem_ctx, domuser, + PTR_DIFF(p, domuser)); + domuser = p+1; + } + + *user = talloc_strdup(mem_ctx, domuser); + + return ((*domain != NULL) && (*user != NULL)); +} + + diff --git a/source4/winbind/wb_xids2sids.c b/source4/winbind/wb_xids2sids.c new file mode 100644 index 0000000000..a1cf2667ff --- /dev/null +++ b/source4/winbind/wb_xids2sids.c @@ -0,0 +1,80 @@ +/* + Unix SMB/CIFS implementation. + + Convet an unixid struct to a SID + + Copyright (C) 2008 Kai Blin + + 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 "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "winbind/wb_helper.h" +#include "libcli/security/proto.h" +#include "winbind/idmap.h" + +struct xids2sids_state { + struct composite_context *ctx; + struct wbsrv_service *service; + struct id_mapping *ids; + int count; +}; + +struct composite_context *wb_xids2sids_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + int count, struct id_mapping *ids) +{ + struct composite_context *result; + struct xids2sids_state *state; + + DEBUG(5, ("wb_xids2sids_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(mem_ctx, struct xids2sids_state); + if (composite_nomem(state, result)) return result; + + state->ctx = result; + result->private_data = state; + state->service = service; + state->count = count; + state->ids = ids; + + state->ctx->status = idmap_xids_to_sids(service->idmap_ctx, mem_ctx, + count, state->ids); + if (!composite_is_ok(state->ctx)) return result; + + composite_done(state->ctx); + return result; +} + +NTSTATUS wb_xids2sids_recv(struct composite_context *ctx, + struct id_mapping **ids) +{ + NTSTATUS status = composite_wait(ctx); + struct xids2sids_state *state = talloc_get_type(ctx->private_data, + struct xids2sids_state); + + DEBUG(5, ("wb_xids2sids_recv called.\n")); + + *ids = state->ids; + + talloc_free(ctx); + return status; +} + |