From cc17ac9bba0de5ea8f057cf7683aa914ff4e5702 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 4 Oct 2011 12:42:11 +0200 Subject: s4:smb_server: implement dfs referral handing on top of dfs_server_ad_get_referrals() metze --- source4/smb_server/smb/trans2.c | 865 +++-------------------------------- source4/smb_server/smb/wscript_build | 1 + 2 files changed, 54 insertions(+), 812 deletions(-) (limited to 'source4/smb_server/smb') diff --git a/source4/smb_server/smb/trans2.c b/source4/smb_server/smb/trans2.c index 1bc5c12b0a..89402bac82 100644 --- a/source4/smb_server/smb/trans2.c +++ b/source4/smb_server/smb/trans2.c @@ -33,6 +33,7 @@ #include "auth/session.h" #include "param/param.h" #include "lib/tsocket/tsocket.h" +#include "dfs_server/dfs_server_ad.h" #define MAX_DFS_RESPONSE 56*1024 /* 56 Kb */ @@ -63,13 +64,7 @@ struct trans_op { NTSTATUS (*send_fn)(struct trans_op *); void *op_info; }; -/* A DC set is a group of DC, they might have been grouped together - because they belong to the same site, or to site with same cost ... -*/ -struct dc_set { - const char **names; - uint32_t count; -}; + #define CHECK_MIN_BLOB_SIZE(blob, size) do { \ if ((blob)->length < (size)) { \ return NT_STATUS_INFO_LENGTH_MISMATCH; \ @@ -858,456 +853,69 @@ static NTSTATUS trans2_findfirst_send(struct trans_op *op) return NT_STATUS_OK; } - -/* - fill a referral type structure - */ -static NTSTATUS fill_normal_dfs_referraltype(struct dfs_referral_type *ref, - uint16_t version, - const char *dfs_path, - const char *server_path, int isfirstoffset) -{ - ZERO_STRUCTP(ref); - switch (version) { - case 4: - version = 3; -# if 0 - /* For the moment there is a bug with XP that don't seems to appriciate much - * level4 so we return just level 3 for everyone - */ - ref->referral.v4.server_type = DFS_SERVER_NON_ROOT; - /* "normal" referral seems to always include the GUID */ - ref->referral.v4.size = 34; - - if (isfirstoffset) { - ref->referral.v4.entry_flags = DFS_HEADER_FLAG_TARGET_BCK; - } - ref->referral.v4.ttl = 900; /* As w2k8r2 */ - ref->referral.v4.referrals.r1.DFS_path = talloc_strdup(ref, dfs_path); - ref->referral.v4.referrals.r1.DFS_alt_path = talloc_strdup(ref, dfs_path); - ref->referral.v4.referrals.r1.netw_address = talloc_strdup(ref, server_path); - return NT_STATUS_OK; -#endif - case 3: - ref->version = version; - ref->referral.v3.server_type = DFS_SERVER_NON_ROOT; - /* "normal" referral seems to always include the GUID */ - ref->referral.v3.size = 34; - - ref->referral.v3.entry_flags = 0; - ref->referral.v3.ttl = 600; /* As w2k3 */ - ref->referral.v3.referrals.r1.DFS_path = talloc_strdup(ref, dfs_path); - ref->referral.v3.referrals.r1.DFS_alt_path = talloc_strdup(ref, dfs_path); - ref->referral.v3.referrals.r1.netw_address = talloc_strdup(ref, server_path); - return NT_STATUS_OK; - } - return NT_STATUS_INVALID_LEVEL; -} - -/* - fill a domain refererral - */ -static NTSTATUS fill_domain_dfs_referraltype(struct dfs_referral_type *ref, - uint16_t version, - const char *domain, - const char **names, - uint16_t numnames) -{ - switch (version) { - case 3: - ZERO_STRUCTP(ref); - DEBUG(8, ("Called fill_domain_dfs_referraltype\n")); - ref->version = version; - ref->referral.v3.server_type = DFS_SERVER_NON_ROOT; - /* It's hard coded ... don't think it's a good way but the sizeof return not the - * correct values - * - * We have 18 if the GUID is not included 34 otherwise - */ - if (numnames == 0) { - /* Windows return without the guid when returning domain list - */ - ref->referral.v3.size = 18; - } else { - ref->referral.v3.size = 34; - } - ref->referral.v3.entry_flags = DFS_FLAG_REFERRAL_DOMAIN_RESP; - ref->referral.v3.ttl = 600; /* As w2k3 */ - ref->referral.v3.referrals.r2.special_name = domain; - ref->referral.v3.referrals.r2.nb_expanded_names = numnames; - /* Put the final terminator */ - if (names) { - const char **names2 = talloc_array(ref, const char *, numnames+1); - NT_STATUS_HAVE_NO_MEMORY(names2); - int i; - for (i = 0; ireferral.v3.referrals.r2.expanded_names = names2; - } - return NT_STATUS_OK; - } - return NT_STATUS_INVALID_LEVEL; -} - /* - get the DCs list within a site - */ -static NTSTATUS get_dcs_insite(TALLOC_CTX *ctx, struct ldb_context *ldb, - struct ldb_dn *sitedn, struct dc_set *list, - bool dofqdn) -{ - static const char *attrs[] = { "serverReference", NULL }; - static const char *attrs2[] = { "dNSHostName", "sAMAccountName", NULL }; - struct ldb_result *r; - unsigned int i; - int ret; - const char **dc_list; - - ret = ldb_search(ldb, ctx, &r, sitedn, LDB_SCOPE_SUBTREE, attrs, - "(&(objectClass=server)(serverReference=*))"); - if (ret != LDB_SUCCESS) { - DEBUG(2,(__location__ ": Failed to get list of servers - %s\n", - ldb_errstring(ldb))); - return NT_STATUS_INTERNAL_ERROR; - } - - if (r->count == 0) { - /* none in this site */ - talloc_free(r); - return NT_STATUS_OK; - } - - /* - * need to search for all server object to know the size of the array. - * Search all the object of class server in this site - */ - dc_list = talloc_array(r, const char *, r->count); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dc_list, r); - - /* TODO put some random here in the order */ - list->names = talloc_realloc(list, list->names, const char *, list->count + r->count); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(list->names, r); - - for (i = 0; icount; i++) { - struct ldb_dn *dn; - struct ldb_result *r2; - - dn = ldb_msg_find_attr_as_dn(ldb, ctx, r->msgs[i], "serverReference"); - if (!dn) { - return NT_STATUS_INTERNAL_ERROR; - } - - ret = ldb_search(ldb, r, &r2, dn, LDB_SCOPE_BASE, attrs2, "(objectClass=computer)"); - if (ret != LDB_SUCCESS) { - DEBUG(2,(__location__ ": Search for computer on %s failed - %s\n", - ldb_dn_get_linearized(dn), ldb_errstring(ldb))); - return NT_STATUS_INTERNAL_ERROR; - } - - if (dofqdn) { - const char *dns = ldb_msg_find_attr_as_string(r2->msgs[0], "dNSHostName", NULL); - if (dns == NULL) { - DEBUG(2,(__location__ ": dNSHostName missing on %s\n", - ldb_dn_get_linearized(dn))); - talloc_free(r); - return NT_STATUS_INTERNAL_ERROR; - } - - list->names[list->count] = talloc_strdup(list->names, dns); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(list->names[list->count], r); - } else { - char *tmp; - const char *acct = ldb_msg_find_attr_as_string(r2->msgs[0], "sAMAccountName", NULL); - if (acct == NULL) { - DEBUG(2,(__location__ ": sAMAccountName missing on %s\n", - ldb_dn_get_linearized(dn))); - talloc_free(r); - return NT_STATUS_INTERNAL_ERROR; - } - - tmp = talloc_strdup(list->names, acct); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(tmp, r); - - /* Netbios name is also the sAMAccountName for - computer but without the final $ */ - tmp[strlen(tmp) - 1] = '\0'; - list->names[list->count] = tmp; - } - list->count++; - talloc_free(r2); - } - - talloc_free(r); - return NT_STATUS_OK; -} - - -/* - get all DCs - */ -static NTSTATUS get_dcs(TALLOC_CTX *ctx, struct ldb_context *ldb, - const char *searched_site, bool need_fqdn, - struct dc_set ***pset_list, uint32_t flags) + trans2 getdfsreferral implementation +*/ +static NTSTATUS trans2_getdfsreferral(struct smbsrv_request *req, + struct trans_op *op) { - /* - * Flags will be used later to indicate things like least-expensive - * or same-site options - */ - const char *attrs_none[] = { NULL }; - const char *attrs3[] = { "name", NULL }; - struct ldb_dn *configdn, *sitedn, *dn, *sitescontainerdn; - struct ldb_result *r; - struct dc_set **set_list = NULL; - uint32_t i; - int ret; - uint32_t current_pos = 0; + enum ndr_err_code ndr_err; + struct smb_trans2 *trans = op->trans; + struct ldb_context *ldb; + struct loadparm_context *lp_ctx; NTSTATUS status; - TALLOC_CTX *subctx = talloc_new(ctx); - - *pset_list = set_list = NULL; - - subctx = talloc_new(ctx); - NT_STATUS_HAVE_NO_MEMORY(subctx); - - configdn = ldb_get_config_basedn(ldb); + struct dfs_GetDFSReferral *r; + DATA_BLOB outblob = data_blob_null; + uint16_t nb_referrals = 0; - /* Let's search for the Site container */ - ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE, attrs_none, - "(objectClass=sitesContainer)"); - if (ret != LDB_SUCCESS) { - DEBUG(2,(__location__ ": Failed to find sitesContainer within %s - %s\n", - ldb_dn_get_linearized(configdn), ldb_errstring(ldb))); - talloc_free(subctx); - return NT_STATUS_INTERNAL_ERROR; - } - if (r->count > 1) { - DEBUG(2,(__location__ ": Expected 1 sitesContainer - found %u within %s\n", - r->count, ldb_dn_get_linearized(configdn))); - talloc_free(subctx); - return NT_STATUS_INTERNAL_ERROR; + lp_ctx = req->tcon->ntvfs->lp_ctx; + if (!lpcfg_host_msdfs(lp_ctx)) { + return NT_STATUS_NOT_IMPLEMENTED; } - sitescontainerdn = talloc_steal(subctx, r->msgs[0]->dn); - talloc_free(r); + r = talloc_zero(req, struct dfs_GetDFSReferral); + NT_STATUS_HAVE_NO_MEMORY(r); - /* - * TODO: Here we should have a more subtle handling - * for the case "same-site" - */ - ret = ldb_search(ldb, subctx, &r, sitescontainerdn, LDB_SCOPE_SUBTREE, - attrs_none, "(objectClass=server)"); - if (ret != LDB_SUCCESS) { - DEBUG(2,(__location__ ": Failed to find servers within %s - %s\n", - ldb_dn_get_linearized(sitescontainerdn), ldb_errstring(ldb))); - talloc_free(subctx); + ldb = samdb_connect(r, req->tcon->ntvfs->event_ctx, lp_ctx, system_session(lp_ctx), 0); + if (ldb == NULL) { + DEBUG(2,(__location__ ": Failed to open samdb\n")); + talloc_free(r); return NT_STATUS_INTERNAL_ERROR; } - talloc_free(r); - - if (searched_site != NULL && searched_site[0] != '\0') { - ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE, - attrs_none, "(&(name=%s)(objectClass=site))", searched_site); - if (ret != LDB_SUCCESS) { - talloc_free(subctx); - return NT_STATUS_FOOBAR; - } else if (r->count != 1) { - talloc_free(subctx); - return NT_STATUS_FOOBAR; - } - /* All of this was to get the DN of the searched_site */ - sitedn = r->msgs[0]->dn; - - set_list = talloc_realloc(subctx, set_list, struct dc_set *, current_pos+1); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list, subctx); - - set_list[current_pos] = talloc(set_list, struct dc_set); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list[current_pos], subctx); - - set_list[current_pos]->names = NULL; - set_list[current_pos]->count = 0; - status = get_dcs_insite(subctx, ldb, sitedn, - set_list[current_pos], need_fqdn); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(2,(__location__ ": Failed to get DC from site %s - %s\n", - ldb_dn_get_linearized(sitedn), nt_errstr(status))); - talloc_free(subctx); - return status; - } + ndr_err = ndr_pull_struct_blob(&trans->in.params, r, + &r->in.req, + (ndr_pull_flags_fn_t)ndr_pull_dfs_GetDFSReferral_in); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + DEBUG(2,(__location__ ": Failed to parse GetDFSReferral_in - %s\n", + nt_errstr(status))); talloc_free(r); - current_pos++; - } - - /* Let's find all the sites */ - ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE, attrs3, "(objectClass=site)"); - if (ret != LDB_SUCCESS) { - DEBUG(2,(__location__ ": Failed to find any site containers in %s\n", - ldb_dn_get_linearized(configdn))); - talloc_free(subctx); - return NT_STATUS_INTERNAL_DB_CORRUPTION; - } - - /* - * TODO: - * We should randomize the order in the main site, - * it's mostly needed for sysvol/netlogon referral. - * Depending of flag we either randomize order of the - * not "in the same site DCs" - * or we randomize by group of site that have the same cost - * In the long run we want to manipulate an array of site_set - * All the site in one set have the same cost (if least-expansive options is selected) - * and we will put all the dc related to 1 site set into 1 DCs set. - * Within a site set, site order has to be randomized - * - * But for the moment we just return the list of sites - */ - if (r->count) { - /* - * We will realloc + 2 because we will need one additional place - * for element at current_pos + 1 for the NULL element - */ - set_list = talloc_realloc(subctx, set_list, struct dc_set *, - current_pos+2); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list, subctx); - - set_list[current_pos] = talloc(ctx, struct dc_set); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list[current_pos], subctx); - - set_list[current_pos]->names = NULL; - set_list[current_pos]->count = 0; - - set_list[current_pos+1] = NULL; - } - - for (i=0; icount; i++) { - const char *site_name = ldb_msg_find_attr_as_string(r->msgs[i], "name", NULL); - if (site_name == NULL) { - DEBUG(2,(__location__ ": Failed to find name attribute in %s\n", - ldb_dn_get_linearized(r->msgs[i]->dn))); - talloc_free(subctx); - return NT_STATUS_INTERNAL_DB_CORRUPTION; - } - - if (searched_site == NULL || - strcmp(searched_site, site_name) != 0) { - DEBUG(2,(__location__ ": Site: %s %s\n", - searched_site, site_name)); - - /* - * Do all the site but the one of the client - * (because it has already been done ...) - */ - dn = r->msgs[i]->dn; - - status = get_dcs_insite(subctx, ldb, dn, - set_list[current_pos], - need_fqdn); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(subctx); - return status; - } - } - } - current_pos++; - set_list[current_pos] = NULL; - - *pset_list = talloc_move(ctx, &set_list); - talloc_free(subctx); - return NT_STATUS_OK; -} - -static NTSTATUS dodomain_referral(TALLOC_CTX *ctx, - const struct dfs_GetDFSReferral_in *dfsreq, - struct ldb_context *ldb, - struct smb_trans2 *trans, - struct loadparm_context *lp_ctx) -{ - /* - * TODO for the moment we just return the local domain - */ - DATA_BLOB outblob; - enum ndr_err_code ndr_err; - NTSTATUS status; - const char *dns_domain = lpcfg_dnsdomain(lp_ctx); - const char *netbios_domain = lpcfg_workgroup(lp_ctx); - struct dfs_referral_resp resp; - struct dfs_referral_type *tab; - struct dfs_referral_type *referral; - const char *referral_str; - /* In the future this needs to be fetched from the ldb */ - uint32_t found_domain = 2; - uint32_t current_pos = 0; - TALLOC_CTX *context; - - if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_CONTROLLER) { - DEBUG(10 ,("Received a domain referral request on a non DC\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - if (dfsreq->max_referral_level < 3) { - DEBUG(2,("invalid max_referral_level %u\n", - dfsreq->max_referral_level)); - return NT_STATUS_UNSUCCESSFUL; - } - - context = talloc_new(ctx); - NT_STATUS_HAVE_NO_MEMORY(context); - - resp.path_consumed = 0; - resp.header_flags = 0; /* Do like w2k3 */ - resp.nb_referrals = found_domain; /* the fqdn one + the NT domain */ - - tab = talloc_array(context, struct dfs_referral_type, found_domain); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(tab, context); - - referral = talloc(tab, struct dfs_referral_type); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(referral, context); - referral_str = talloc_asprintf(referral, "\\%s", netbios_domain); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(referral_str, context); - status = fill_domain_dfs_referraltype(referral, 3, - referral_str, - NULL, 0); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(2,(__location__ ":Unable to fill domain referral structure\n")); - talloc_free(context); - return NT_STATUS_UNSUCCESSFUL; + return status; } - tab[current_pos] = *referral; - current_pos++; + DEBUG(8, ("Requested DFS name: %s length: %u\n", + r->in.req.servername, + (unsigned int)strlen_m(r->in.req.servername)*2)); - referral = talloc(tab, struct dfs_referral_type); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(referral, context); - referral_str = talloc_asprintf(referral, "\\%s", dns_domain); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(referral_str, context); - status = fill_domain_dfs_referraltype(referral, 3, - referral_str, - NULL, 0); + status = dfs_server_ad_get_referrals(lp_ctx, ldb, + req->smb_conn->connection->remote_address, r); if (!NT_STATUS_IS_OK(status)) { - DEBUG(2,(__location__ ":Unable to fill domain referral structure\n")); - talloc_free(context); - return NT_STATUS_UNSUCCESSFUL; + talloc_free(r); + return status; } - tab[current_pos] = *referral; - current_pos++; - /* - * Put here the code from filling the array for trusted domain - */ - resp.referral_entries = tab; - - ndr_err = ndr_push_struct_blob(&outblob, context, - &resp, + ndr_err = ndr_push_struct_blob(&outblob, trans, + r->out.resp, (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(2,(__location__ ":NDR marchalling of domain deferral response failed\n")); - talloc_free(context); + talloc_free(r); return NT_STATUS_INTERNAL_ERROR; } + nb_referrals = r->out.resp->nb_referrals; + if (outblob.length > trans->in.max_data) { bool ok = false; @@ -1317,43 +925,42 @@ static NTSTATUS dodomain_referral(TALLOC_CTX *ctx, if (trans->in.max_data != MAX_DFS_RESPONSE) { /* As specified in MS-DFSC.pdf 3.3.5.2 */ - talloc_free(context); + talloc_free(r); return STATUS_BUFFER_OVERFLOW; } /* * The answer is too big, so let's remove some answers */ - while (!ok && resp.nb_referrals > 2) { + while (!ok && r->out.resp->nb_referrals > 2) { data_blob_free(&outblob); /* - * Let's scrap the first referral (for now) + * Let's scrap the last referral (for now) */ - resp.nb_referrals -= 1; - resp.referral_entries += 1; + r->out.resp->nb_referrals -= 1; - ndr_err = ndr_push_struct_blob(&outblob, context, - &resp, + ndr_err = ndr_push_struct_blob(&outblob, trans, + r->out.resp, (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - talloc_free(context); + talloc_free(r); return NT_STATUS_INTERNAL_ERROR; } if (outblob.length <= MAX_DFS_RESPONSE) { DEBUG(10,("DFS: managed to reduce the size of referral initial" "number of referral %d, actual count: %d", - found_domain, resp.nb_referrals)); + nb_referrals, r->out.resp->nb_referrals)); ok = true; break; } } - if (!ok && resp.nb_referrals == 2) { + if (!ok && r->out.resp->nb_referrals <= 2) { DEBUG(8, (__location__ "; Not able to fit the domain and realm in DFS a " " 56K buffer, something must be broken")); - talloc_free(context); + talloc_free(r); return NT_STATUS_INTERNAL_ERROR; } } @@ -1361,376 +968,10 @@ static NTSTATUS dodomain_referral(TALLOC_CTX *ctx, TRANS2_CHECK(trans2_setup_reply(trans, 0, outblob.length, 0)); trans->out.data = outblob; - talloc_steal(ctx, outblob.data); - talloc_free(context); - return NT_STATUS_OK; -} - -/* - * Handle the logic for dfs referral request like \\domain - * or \\domain\sysvol or \\fqdn or \\fqdn\netlogon - */ -static NTSTATUS dodc_or_sysvol_referral(TALLOC_CTX *ctx, - const struct dfs_GetDFSReferral_in dfsreq, - const char* requesteddomain, - const char* requestedshare, - const char* requestedname, - struct ldb_context *ldb, - struct smb_trans2 *trans, - struct smbsrv_request *req, - struct loadparm_context *lp_ctx) -{ - /* - * It's not a "standard" DFS referral but a referral to get the DC list - * or sysvol/netlogon - * Let's check that it's for one of our domain ... - */ - DATA_BLOB outblob; - NTSTATUS status; - unsigned int num_domain = 1; - enum ndr_err_code ndr_err; - const char *realm = lpcfg_realm(lp_ctx); - const char *domain = lpcfg_workgroup(lp_ctx); - const char *site_name = NULL; /* Name of the site where the client is */ - bool found = false; - bool need_fqdn = false; - bool dc_referral = true; - unsigned int i; - struct dc_set **set; - char const **domain_list; - struct tsocket_address *remote_address; - char *client_addr = NULL; - TALLOC_CTX *context; - - if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_CONTROLLER) { - return NT_STATUS_INVALID_PARAMETER; - } - - if (dfsreq.max_referral_level < 3) { - DEBUG(2,("invalid max_referral_level %u\n", - dfsreq.max_referral_level)); - return NT_STATUS_UNSUCCESSFUL; - } - - context = talloc_new(ctx); - NT_STATUS_HAVE_NO_MEMORY(context); - - DEBUG(10, ("in this we have request for %s and share %s requested is %s\n", - requesteddomain, - requestedshare, - requestedname)); - - if (requestedshare) { - DEBUG(10, ("Have a non DC domain referal\n")); - dc_referral = false; - } - - /* - * We will fetch the trusted domain list soon with something like this: - * - * "(&(|(flatname=%s)(cn=%s)(trustPartner=%s)(flatname=%s)(cn=%s) - * (trustPartner=%s))(objectclass=trustedDomain))" - * - * Allocate for num_domain + 1 so that the last element will be NULL) - */ - domain_list = talloc_array(context, const char*, num_domain+1); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(domain_list, context); - - domain_list[0] = realm; - domain_list[1] = domain; - for (i=0; i<=num_domain; i++) { - if (strncasecmp(domain_list[i], requesteddomain, strlen(domain_list[i])) == 0) { - found = true; - break; - } - } - - if (!found) { - /* The requested domain is not one that we support */ - DEBUG(3,("Requested referral for domain %s, but we don't handle it", - requesteddomain)); - return NT_STATUS_INVALID_PARAMETER; - } - - if (strchr(requestedname,'.')) { - need_fqdn = 1; - } - - remote_address = req->smb_conn->connection->remote_address; - if (tsocket_address_is_inet(remote_address, "ip")) { - client_addr = tsocket_address_inet_addr_string(remote_address, context); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(client_addr, context); - } - site_name = samdb_client_site_name(ldb, context, client_addr, NULL); - status = get_dcs(context, ldb, site_name, need_fqdn, &set, 0); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3,("Unable to get list of DCs\n")); - talloc_free(context); - return status; - } - - if (dc_referral) { - const char **dc_list = NULL; - uint32_t num_dcs = 0; - struct dfs_referral_type *referral; - const char *referral_str; - struct dfs_referral_resp resp; - - for(i=0; set[i]; i++) { - uint32_t j; - - dc_list = talloc_realloc(context, dc_list, const char*, - num_dcs + set[i]->count + 1); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dc_list, context); - - for(j=0; jcount; j++) { - dc_list[num_dcs + j] = talloc_steal(context, set[i]->names[j]); - } - num_dcs = num_dcs + set[i]->count; - TALLOC_FREE(set[i]); - dc_list[num_dcs] = NULL; - } - - resp.path_consumed = 0; - resp.header_flags = 0; /* Do like w2k3 and like in 3.3.5.3 of MS-DFSC*/ - - /* - * The NumberOfReferrals field MUST be set to 1, - * independent of the number of DC names - * returned. (as stated in 3.3.5.3 of MS-DFSC) - */ - resp.nb_referrals = 1; - - /* Put here the code from filling the array for trusted domain */ - referral = talloc(context, struct dfs_referral_type); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(referral, context); - - if (requestedname[0] == '\\') { - referral_str = talloc_asprintf(referral, "%s", - requestedname); - } else { - referral_str = talloc_asprintf(referral, "\\%s", - requestedname); - } - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(referral_str, context); - - status = fill_domain_dfs_referraltype(referral, 3, - referral_str, - dc_list, num_dcs); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(2,(__location__ ":Unable to fill domain referral structure\n")); - talloc_free(context); - return NT_STATUS_UNSUCCESSFUL; - } - resp.referral_entries = referral; - - ndr_err = ndr_push_struct_blob(&outblob, context, - &resp, - (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - DEBUG(2,(__location__ ":NDR marshalling of dfs referral response failed\n")); - talloc_free(context); - return NT_STATUS_INTERNAL_ERROR; - } - } else { - unsigned int nb_entries = 0; - unsigned int current = 0; - struct dfs_referral_type *tab; - struct dfs_referral_resp resp; - - for(i=0; set[i]; i++) { - nb_entries = nb_entries + set[i]->count; - } - - resp.path_consumed = 2*strlen(requestedname); /* The length is expected in bytes */ - resp.header_flags = DFS_HEADER_FLAG_STORAGE_SVR; /* Do like w2k3 and like in 3.3.5.3 of MS-DFSC*/ - - /* - * The NumberOfReferrals field MUST be set to 1, - * independent of the number of DC names - * returned. (as stated in 3.3.5.3 of MS-DFSC) - */ - resp.nb_referrals = nb_entries; - - tab = talloc_array(context, struct dfs_referral_type, nb_entries); - NT_STATUS_HAVE_NO_MEMORY(tab); - - for(i=0; set[i]; i++) { - uint32_t j; - - for(j=0; j< set[i]->count; j++) { - struct dfs_referral_type *referral; - const char *referral_str; - - referral = talloc(tab, struct dfs_referral_type); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(referral, context); - - referral_str = talloc_asprintf(referral, "\\%s\\%s", - set[i]->names[j], requestedshare); - DEBUG(8, ("Doing a dfs referral for %s with this value %s requested %s\n", set[i]->names[j], referral_str, requestedname)); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(referral_str, context); - - status = fill_normal_dfs_referraltype(referral, - dfsreq.max_referral_level, - requestedname, referral_str, j==0); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(2, (__location__ ": Unable to fill a normal dfs referral object")); - talloc_free(context); - return NT_STATUS_UNSUCCESSFUL; - } - tab[current] = *referral; - current++; - } - } - resp.referral_entries = tab; - - ndr_err = ndr_push_struct_blob(&outblob, context, - &resp, - (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - DEBUG(2,(__location__ ":NDR marchalling of domain deferral response failed\n")); - talloc_free(context); - return NT_STATUS_INTERNAL_ERROR; - } - } - - TRANS2_CHECK(trans2_setup_reply(trans, 0, outblob.length, 0)); - - /* - * TODO If the size is too big we should remove - * some DC from the answer or return STATUS_BUFFER_OVERFLOW - */ - trans->out.data = outblob; - talloc_steal(ctx, outblob.data); - talloc_free(context); + talloc_free(r); return NT_STATUS_OK; } -/* - trans2 getdfsreferral implementation -*/ -static NTSTATUS trans2_getdfsreferral(struct smbsrv_request *req, - struct trans_op *op) -{ - enum ndr_err_code ndr_err; - struct smb_trans2 *trans = op->trans; - struct dfs_GetDFSReferral_in dfsreq; - TALLOC_CTX *context; - struct ldb_context *ldb; - struct loadparm_context *lp_ctx; - const char *realm, *nbname, *requestedname; - char *fqdn, *share, *domain, *tmp; - NTSTATUS status; - - lp_ctx = req->tcon->ntvfs->lp_ctx; - if (!lpcfg_host_msdfs(lp_ctx)) { - return NT_STATUS_NOT_IMPLEMENTED; - } - - context = talloc_new(req); - NT_STATUS_HAVE_NO_MEMORY(context); - - ldb = samdb_connect(context, req->tcon->ntvfs->event_ctx, lp_ctx, system_session(lp_ctx), 0); - if (ldb == NULL) { - DEBUG(2,(__location__ ": Failed to open samdb\n")); - talloc_free(context); - return NT_STATUS_INTERNAL_ERROR; - } - - ndr_err = ndr_pull_struct_blob(&trans->in.params, op, - &dfsreq, - (ndr_pull_flags_fn_t)ndr_pull_dfs_GetDFSReferral_in); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - status = ndr_map_error2ntstatus(ndr_err); - DEBUG(2,(__location__ ": Failed to parse GetDFSReferral_in - %s\n", - nt_errstr(status))); - talloc_free(context); - return status; - } - - DEBUG(8, ("Requested DFS name: %s length: %u\n", - dfsreq.servername, (unsigned int)strlen(dfsreq.servername))); - - /* - * If the servername is "" then we are in a case of domain dfs - * and the client just searches for the list of local domain - * it is attached and also trusted ones. - */ - requestedname = dfsreq.servername; - if (requestedname == NULL || requestedname[0] == '\0') { - return dodomain_referral(op, &dfsreq, ldb, trans, lp_ctx); - } - - realm = lpcfg_realm(lp_ctx); - nbname = lpcfg_netbios_name(lp_ctx); - fqdn = talloc_asprintf(context, "%s.%s", nbname, realm); - - if ((strncasecmp(requestedname+1, nbname, strlen(nbname)) == 0) || - (strncasecmp(requestedname+1, fqdn, strlen(fqdn)) == 0) ) { - /* - * the referral request starts with \NETBIOSNAME or \fqdn - * it's a standalone referral we do not do it - * (TODO correct this) - * If a DFS link that is a complete prefix of the DFS referral - * request path is identified, the server MUST return a DFS link - * referral response; otherwise, if it has a match for the DFS root, - * it MUST return a root referral response. - */ - DEBUG(3, ("Received a standalone request for %s, we do not support standalone referral yet",requestedname)); - talloc_free(context); - return NT_STATUS_NOT_FOUND; - } - talloc_free(fqdn); - - domain = talloc_strdup(context, requestedname); - while(*domain && *domain == '\\') { - domain++; - } - - tmp = strchr(domain,'\\'); /* To get second \ if any */ - share = NULL; - if (tmp) { - /* - * We are finishing properly the domain string - * and the share one will start after the \ - */ - tmp[0] = '\\'; - tmp++; - share = talloc_strdup(context, tmp); - } - /* - * Here we have filtered the thing the requested name don't contain our DNS name. - * So if the share == NULL or if share in ("sysvol", "netlogon") - * then we proceed. In the first case it will be a dc refereal in the second it will - * be just a sysvol/netlogon referral. - */ - if (share == NULL || - strcasecmp(share, "sysvol") == 0 || - strcasecmp(share, "netlogon") == 0) { - status = dodc_or_sysvol_referral(op, dfsreq, domain, share, requestedname, - ldb, trans, req, lp_ctx); - talloc_free(context); - return status; - } - - tmp = strchr(share, '\\'); - if (tmp && - (strncasecmp(share, "sysvol", 6) == 0 || - strncasecmp(share, "netlogon", 8) == 0)) { - /* - * We have more than two \ so it something like - * \domain\sysvol\foobar - */ - talloc_free(context); - return NT_STATUS_NOT_FOUND; - } - - talloc_free(context); - /* By default until all the case are handled*/ - return NT_STATUS_NOT_FOUND; -} - /* trans2 findfirst implementation */ diff --git a/source4/smb_server/smb/wscript_build b/source4/smb_server/smb/wscript_build index 4004bac2d3..5c717a3601 100644 --- a/source4/smb_server/smb/wscript_build +++ b/source4/smb_server/smb/wscript_build @@ -3,6 +3,7 @@ bld.SAMBA_SUBSYSTEM('SMB_PROTOCOL', source='receive.c negprot.c nttrans.c reply.c request.c search.c service.c sesssetup.c srvtime.c trans2.c signing.c', autoproto='smb_proto.h', + deps='dfs_server_ad', public_deps='ntvfs LIBPACKET samba-credentials samba_server_gensec' ) -- cgit