dfs_server: fix the response to please XP and Windows 2008R2 doing so avoid continiou...
[samba.git] / dfs_server / dfs_server_ad.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Copyright Matthieu Patou <mat@matws.net> 2010-2011
5    Copyright Stefan Metzmacher 2011
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "librpc/gen_ndr/dfsblobs.h"
23 #include "librpc/gen_ndr/ndr_dfsblobs.h"
24 #include "dsdb/samdb/samdb.h"
25 #include "auth/session.h"
26 #include "param/param.h"
27 #include "lib/tsocket/tsocket.h"
28 #include "dfs_server/dfs_server_ad.h"
29
30 #define MAX_DFS_RESPONSE 56*1024 /* 56 Kb */
31
32 /* A DC set is a group of DC, they might have been grouped together
33    because they belong to the same site, or to site with same cost ...
34 */
35 struct dc_set {
36         const char **names;
37         uint32_t count;
38 };
39
40 /*
41   fill a referral type structure
42  */
43 static NTSTATUS fill_normal_dfs_referraltype(TALLOC_CTX *mem_ctx,
44                                              struct dfs_referral_type *ref,
45                                              uint16_t version,
46                                              const char *dfs_path,
47                                              const char *server_path, int isfirstoffset)
48 {
49         ZERO_STRUCTP(ref);
50         switch (version) {
51         case 4:
52                 ref->version = version;
53                 /* For the moment there is a bug with XP that don't seems to appriciate much
54                  * level4 so we return just level 3 for everyone
55                  */
56                 ref->referral.v4.server_type = DFS_SERVER_NON_ROOT;
57                 /* "normal" referral seems to always include the GUID */
58                 ref->referral.v4.size = 34;
59
60                 if (isfirstoffset) {
61                         ref->referral.v4.entry_flags =  DFS_HEADER_FLAG_TARGET_BCK;
62                 }
63                 ref->referral.v4.ttl = 900; /* As w2k8r2 */
64                 ref->referral.v4.referrals.r1.DFS_path = talloc_strdup(mem_ctx, dfs_path);
65                 if (ref->referral.v4.referrals.r1.DFS_path == NULL) {
66                         return NT_STATUS_NO_MEMORY;
67                 }
68                 ref->referral.v4.referrals.r1.DFS_alt_path = talloc_strdup(mem_ctx, dfs_path);
69                 if (ref->referral.v4.referrals.r1.DFS_alt_path == NULL) {
70                         return NT_STATUS_NO_MEMORY;
71                 }
72                 ref->referral.v4.referrals.r1.netw_address = talloc_strdup(mem_ctx, server_path);
73                 if (ref->referral.v4.referrals.r1.netw_address == NULL) {
74                         return NT_STATUS_NO_MEMORY;
75                 }
76                 return NT_STATUS_OK;
77         case 3:
78                 ref->version = version;
79                 ref->referral.v3.server_type = DFS_SERVER_NON_ROOT;
80                 /* "normal" referral seems to always include the GUID */
81                 ref->referral.v3.size = 34;
82
83                 ref->referral.v3.entry_flags = 0;
84                 ref->referral.v3.ttl = 600; /* As w2k3 */
85                 ref->referral.v3.referrals.r1.DFS_path = talloc_strdup(mem_ctx, dfs_path);
86                 if (ref->referral.v3.referrals.r1.DFS_path == NULL) {
87                         return NT_STATUS_NO_MEMORY;
88                 }
89                 ref->referral.v3.referrals.r1.DFS_alt_path = talloc_strdup(mem_ctx, dfs_path);
90                 if (ref->referral.v3.referrals.r1.DFS_alt_path == NULL) {
91                         return NT_STATUS_NO_MEMORY;
92                 }
93                 ref->referral.v3.referrals.r1.netw_address = talloc_strdup(mem_ctx, server_path);
94                 if (ref->referral.v3.referrals.r1.netw_address == NULL) {
95                         return NT_STATUS_NO_MEMORY;
96                 }
97                 return NT_STATUS_OK;
98         }
99         return NT_STATUS_INVALID_LEVEL;
100 }
101
102 /*
103   fill a domain refererral
104  */
105 static NTSTATUS fill_domain_dfs_referraltype(TALLOC_CTX *mem_ctx,
106                                              struct dfs_referral_type *ref,
107                                              uint16_t version,
108                                              const char *domain,
109                                              const char **names,
110                                              uint16_t numnames)
111 {
112         switch (version) {
113         case 3:
114                 ZERO_STRUCTP(ref);
115                 DEBUG(8, ("Called fill_domain_dfs_referraltype\n"));
116                 ref->version = version;
117                 ref->referral.v3.server_type = DFS_SERVER_NON_ROOT;
118 #if 0
119                 /* We use to have variable size, on Windows 2008R2 it's the same
120                  * and it seems that it gives better results so ... let's use the same
121                  * size.
122                  *
123                  * Additional note: XP SP2 will ask for version 3 and SP3 for version 4.
124                  */
125                 /*
126                  * It's hard coded ... don't think it's a good way but the
127                  * sizeof return not the correct values
128                  *
129                  * We have 18 if the GUID is not included 34 otherwise
130                  */
131                 if (numnames == 0) {
132                         /* Windows return without the guid when returning domain list
133                          */
134                         ref->referral.v3.size = 18;
135                 } else {
136                         ref->referral.v3.size = 34;
137                 }
138 #endif
139                 /* As seen in w2k8r2 it always return the null GUID */
140                 ref->referral.v3.size = 34;
141                 ref->referral.v3.entry_flags = DFS_FLAG_REFERRAL_DOMAIN_RESP;
142                 ref->referral.v3.ttl = 600; /* As w2k3 and w2k8r2*/
143                 ref->referral.v3.referrals.r2.special_name = talloc_strdup(mem_ctx,
144                                                                         domain);
145                 if (ref->referral.v3.referrals.r2.special_name == NULL) {
146                         return NT_STATUS_NO_MEMORY;
147                 }
148                 ref->referral.v3.referrals.r2.nb_expanded_names = numnames;
149                 /* Put the final terminator */
150                 if (names) {
151                         int i;
152                         const char **names2 = talloc_array(mem_ctx, const char *,
153                                                            numnames+1);
154                         NT_STATUS_HAVE_NO_MEMORY(names2);
155                         for (i = 0; i<numnames; i++) {
156                                 names2[i] = talloc_asprintf(names2, "\\%s", names[i]);
157                                 NT_STATUS_HAVE_NO_MEMORY(names2[i]);
158                         }
159                         names2[numnames] = NULL;
160                         ref->referral.v3.referrals.r2.expanded_names = names2;
161                 }
162                 return NT_STATUS_OK;
163         }
164         return NT_STATUS_INVALID_LEVEL;
165 }
166
167 /*
168   get the DCs list within a site
169  */
170 static NTSTATUS get_dcs_insite(TALLOC_CTX *ctx, struct ldb_context *ldb,
171                                struct ldb_dn *sitedn, struct dc_set *list,
172                                bool dofqdn)
173 {
174         static const char *attrs[] = { "serverReference", NULL };
175         static const char *attrs2[] = { "dNSHostName", "sAMAccountName", NULL };
176         struct ldb_result *r;
177         unsigned int i;
178         int ret;
179         const char **dc_list;
180
181         ret = ldb_search(ldb, ctx, &r, sitedn, LDB_SCOPE_SUBTREE, attrs,
182                          "(&(objectClass=server)(serverReference=*))");
183         if (ret != LDB_SUCCESS) {
184                 DEBUG(2,(__location__ ": Failed to get list of servers - %s\n",
185                          ldb_errstring(ldb)));
186                 return NT_STATUS_INTERNAL_ERROR;
187         }
188
189         if (r->count == 0) {
190                 /* none in this site */
191                 talloc_free(r);
192                 return NT_STATUS_OK;
193         }
194
195         /*
196          * need to search for all server object to know the size of the array.
197          * Search all the object of class server in this site
198          */
199         dc_list = talloc_array(r, const char *, r->count);
200         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dc_list, r);
201
202         /* TODO put some random here in the order */
203         list->names = talloc_realloc(list, list->names, const char *, list->count + r->count);
204         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(list->names, r);
205
206         for (i = 0; i<r->count; i++) {
207                 struct ldb_dn  *dn;
208                 struct ldb_result *r2;
209
210                 dn = ldb_msg_find_attr_as_dn(ldb, ctx, r->msgs[i], "serverReference");
211                 if (!dn) {
212                         return NT_STATUS_INTERNAL_ERROR;
213                 }
214
215                 ret = ldb_search(ldb, r, &r2, dn, LDB_SCOPE_BASE, attrs2, "(objectClass=computer)");
216                 if (ret != LDB_SUCCESS) {
217                         DEBUG(2,(__location__ ": Search for computer on %s failed - %s\n",
218                                  ldb_dn_get_linearized(dn), ldb_errstring(ldb)));
219                         return NT_STATUS_INTERNAL_ERROR;
220                 }
221
222                 if (dofqdn) {
223                         const char *dns = ldb_msg_find_attr_as_string(r2->msgs[0], "dNSHostName", NULL);
224                         if (dns == NULL) {
225                                 DEBUG(2,(__location__ ": dNSHostName missing on %s\n",
226                                          ldb_dn_get_linearized(dn)));
227                                 talloc_free(r);
228                                 return NT_STATUS_INTERNAL_ERROR;
229                         }
230
231                         list->names[list->count] = talloc_strdup(list->names, dns);
232                         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(list->names[list->count], r);
233                 } else {
234                         char *tmp;
235                         const char *acct = ldb_msg_find_attr_as_string(r2->msgs[0], "sAMAccountName", NULL);
236                         if (acct == NULL) {
237                                 DEBUG(2,(__location__ ": sAMAccountName missing on %s\n",
238                                          ldb_dn_get_linearized(dn)));
239                                 talloc_free(r);
240                                 return NT_STATUS_INTERNAL_ERROR;
241                         }
242
243                         tmp = talloc_strdup(list->names, acct);
244                         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(tmp, r);
245
246                         /* Netbios name is also the sAMAccountName for
247                            computer but without the final $ */
248                         tmp[strlen(tmp) - 1] = '\0';
249                         list->names[list->count] = tmp;
250                 }
251                 list->count++;
252                 talloc_free(r2);
253         }
254
255         talloc_free(r);
256         return NT_STATUS_OK;
257 }
258
259
260 /*
261   get all DCs
262  */
263 static NTSTATUS get_dcs(TALLOC_CTX *ctx, struct ldb_context *ldb,
264                         const char *searched_site, bool need_fqdn,
265                         struct dc_set ***pset_list, uint32_t flags)
266 {
267         /*
268          * Flags will be used later to indicate things like least-expensive
269          * or same-site options
270          */
271         const char *attrs_none[] = { NULL };
272         const char *attrs3[] = { "name", NULL };
273         struct ldb_dn *configdn, *sitedn, *dn, *sitescontainerdn;
274         struct ldb_result *r;
275         struct dc_set **set_list = NULL;
276         uint32_t i;
277         int ret;
278         uint32_t current_pos = 0;
279         NTSTATUS status;
280         TALLOC_CTX *subctx = talloc_new(ctx);
281
282         *pset_list = set_list = NULL;
283
284         subctx = talloc_new(ctx);
285         NT_STATUS_HAVE_NO_MEMORY(subctx);
286
287         configdn = ldb_get_config_basedn(ldb);
288
289         /* Let's search for the Site container */
290         ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE, attrs_none,
291                          "(objectClass=sitesContainer)");
292         if (ret != LDB_SUCCESS) {
293                 DEBUG(2,(__location__ ": Failed to find sitesContainer within %s - %s\n",
294                          ldb_dn_get_linearized(configdn), ldb_errstring(ldb)));
295                 talloc_free(subctx);
296                 return NT_STATUS_INTERNAL_ERROR;
297         }
298         if (r->count > 1) {
299                 DEBUG(2,(__location__ ": Expected 1 sitesContainer - found %u within %s\n",
300                          r->count, ldb_dn_get_linearized(configdn)));
301                 talloc_free(subctx);
302                 return NT_STATUS_INTERNAL_ERROR;
303         }
304
305         sitescontainerdn = talloc_steal(subctx, r->msgs[0]->dn);
306         talloc_free(r);
307
308         /*
309          * TODO: Here we should have a more subtle handling
310          * for the case "same-site"
311          */
312         ret = ldb_search(ldb, subctx, &r, sitescontainerdn, LDB_SCOPE_SUBTREE,
313                          attrs_none, "(objectClass=server)");
314         if (ret != LDB_SUCCESS) {
315                 DEBUG(2,(__location__ ": Failed to find servers within %s - %s\n",
316                          ldb_dn_get_linearized(sitescontainerdn), ldb_errstring(ldb)));
317                 talloc_free(subctx);
318                 return NT_STATUS_INTERNAL_ERROR;
319         }
320         talloc_free(r);
321
322         if (searched_site != NULL && searched_site[0] != '\0') {
323                 ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE,
324                                  attrs_none, "(&(name=%s)(objectClass=site))", searched_site);
325                 if (ret != LDB_SUCCESS) {
326                         talloc_free(subctx);
327                         return NT_STATUS_FOOBAR;
328                 } else if (r->count != 1) {
329                         talloc_free(subctx);
330                         return NT_STATUS_FOOBAR;
331                 }
332
333                 /* All of this was to get the DN of the searched_site */
334                 sitedn = r->msgs[0]->dn;
335
336                 set_list = talloc_realloc(subctx, set_list, struct dc_set *, current_pos+1);
337                 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list, subctx);
338
339                 set_list[current_pos] = talloc(set_list, struct dc_set);
340                 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list[current_pos], subctx);
341
342                 set_list[current_pos]->names = NULL;
343                 set_list[current_pos]->count = 0;
344                 status = get_dcs_insite(subctx, ldb, sitedn,
345                                         set_list[current_pos], need_fqdn);
346                 if (!NT_STATUS_IS_OK(status)) {
347                         DEBUG(2,(__location__ ": Failed to get DC from site %s - %s\n",
348                                  ldb_dn_get_linearized(sitedn), nt_errstr(status)));
349                         talloc_free(subctx);
350                         return status;
351                 }
352                 talloc_free(r);
353                 current_pos++;
354         }
355
356         /* Let's find all the sites */
357         ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE, attrs3, "(objectClass=site)");
358         if (ret != LDB_SUCCESS) {
359                 DEBUG(2,(__location__ ": Failed to find any site containers in %s\n",
360                          ldb_dn_get_linearized(configdn)));
361                 talloc_free(subctx);
362                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
363         }
364
365         /*
366          * TODO:
367          * We should randomize the order in the main site,
368          * it's mostly needed for sysvol/netlogon referral.
369          * Depending of flag we either randomize order of the
370          * not "in the same site DCs"
371          * or we randomize by group of site that have the same cost
372          * In the long run we want to manipulate an array of site_set
373          * All the site in one set have the same cost (if least-expansive options is selected)
374          * and we will put all the dc related to 1 site set into 1 DCs set.
375          * Within a site set, site order has to be randomized
376          *
377          * But for the moment we just return the list of sites
378          */
379         if (r->count) {
380                 /*
381                  * We will realloc + 2 because we will need one additional place
382                  * for element at current_pos + 1 for the NULL element
383                  */
384                 set_list = talloc_realloc(subctx, set_list, struct dc_set *,
385                                           current_pos+2);
386                 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list, subctx);
387
388                 set_list[current_pos] = talloc(ctx, struct dc_set);
389                 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list[current_pos], subctx);
390
391                 set_list[current_pos]->names = NULL;
392                 set_list[current_pos]->count = 0;
393
394                 set_list[current_pos+1] = NULL;
395         }
396
397         for (i=0; i<r->count; i++) {
398                 const char *site_name = ldb_msg_find_attr_as_string(r->msgs[i], "name", NULL);
399                 if (site_name == NULL) {
400                         DEBUG(2,(__location__ ": Failed to find name attribute in %s\n",
401                                  ldb_dn_get_linearized(r->msgs[i]->dn)));
402                         talloc_free(subctx);
403                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
404                 }
405
406                 if (searched_site == NULL ||
407                     strcmp(searched_site, site_name) != 0) {
408                         DEBUG(2,(__location__ ": Site: %s %s\n",
409                                 searched_site, site_name));
410
411                         /*
412                          * Do all the site but the one of the client
413                          * (because it has already been done ...)
414                          */
415                         dn = r->msgs[i]->dn;
416
417                         status = get_dcs_insite(subctx, ldb, dn,
418                                                 set_list[current_pos],
419                                                 need_fqdn);
420                         if (!NT_STATUS_IS_OK(status)) {
421                                 talloc_free(subctx);
422                                 return status;
423                         }
424                 }
425         }
426         current_pos++;
427         set_list[current_pos] = NULL;
428
429         *pset_list = talloc_move(ctx, &set_list);
430         talloc_free(subctx);
431         return NT_STATUS_OK;
432 }
433
434 static NTSTATUS dodomain_referral(struct loadparm_context *lp_ctx,
435                                   struct ldb_context *sam_ctx,
436                                   const struct tsocket_address *client,
437                                   struct dfs_GetDFSReferral *r)
438 {
439         /*
440          * TODO for the moment we just return the local domain
441          */
442         NTSTATUS status;
443         const char *dns_domain = lpcfg_dnsdomain(lp_ctx);
444         const char *netbios_domain = lpcfg_workgroup(lp_ctx);
445         struct dfs_referral_type *referrals;
446         const char *referral_str;
447         /* In the future this needs to be fetched from the ldb */
448         uint32_t found_domain = 2;
449
450         if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_CONTROLLER) {
451                 DEBUG(10 ,("Received a domain referral request on a non DC\n"));
452                 return NT_STATUS_INVALID_PARAMETER;
453         }
454
455         if (r->in.req.max_referral_level < 3) {
456                 DEBUG(2,("invalid max_referral_level %u\n",
457                          r->in.req.max_referral_level));
458                 return NT_STATUS_UNSUCCESSFUL;
459         }
460
461         r->out.resp = talloc_zero(r, struct dfs_referral_resp);
462         if (r->out.resp == NULL) {
463                 return NT_STATUS_NO_MEMORY;
464         }
465
466         r->out.resp->path_consumed = 0;
467         r->out.resp->header_flags = 0; /* Do like w2k3 */
468         r->out.resp->nb_referrals = found_domain; /* the fqdn one + the NT domain */
469
470         referrals = talloc_zero_array(r->out.resp,
471                                       struct dfs_referral_type,
472                                       r->out.resp->nb_referrals);
473         if (referrals == NULL) {
474                 return NT_STATUS_NO_MEMORY;
475         }
476         r->out.resp->referral_entries = referrals;
477
478         referral_str = talloc_asprintf(r, "\\%s", netbios_domain);
479         if (referral_str == NULL) {
480                 return NT_STATUS_NO_MEMORY;
481         }
482
483         status = fill_domain_dfs_referraltype(referrals,
484                                               &referrals[0], 3,
485                                               referral_str,
486                                               NULL, 0);
487         if (!NT_STATUS_IS_OK(status)) {
488                 DEBUG(2,("%s: Unable to fill domain referral structure - %s\n",
489                          __location__, nt_errstr(status)));
490                 return status;
491         }
492
493         referral_str = talloc_asprintf(r, "\\%s", dns_domain);
494         if (referral_str == NULL) {
495                 return NT_STATUS_NO_MEMORY;
496         }
497
498         status = fill_domain_dfs_referraltype(referrals,
499                                               &referrals[1], 3,
500                                               referral_str,
501                                               NULL, 0);
502         if (!NT_STATUS_IS_OK(status)) {
503                 DEBUG(2,("%s: Unable to fill domain referral structure - %s\n",
504                          __location__, nt_errstr(status)));
505                 return status;
506         }
507
508         return NT_STATUS_OK;
509 }
510
511 /*
512  * Handle the logic for dfs referral request like
513  * \\dns_domain or \\netbios_domain.
514  */
515 static NTSTATUS dodc_referral(struct loadparm_context *lp_ctx,
516                               struct ldb_context *sam_ctx,
517                               const struct tsocket_address *client,
518                               struct dfs_GetDFSReferral *r,
519                               const char *domain_name)
520 {
521         NTSTATUS status;
522         const char *site_name = NULL; /* Name of the site where the client is */
523         bool need_fqdn = false;
524         unsigned int i;
525         const char **dc_list = NULL;
526         uint32_t num_dcs = 0;
527         struct dc_set **set;
528         char *client_str = NULL;
529         struct dfs_referral_type *referrals;
530         const char *referral_str;
531
532         if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_CONTROLLER) {
533                 return NT_STATUS_INVALID_PARAMETER;
534         }
535
536         if (r->in.req.max_referral_level < 3) {
537                 DEBUG(2,("invalid max_referral_level %u\n",
538                          r->in.req.max_referral_level));
539                 return NT_STATUS_UNSUCCESSFUL;
540         }
541
542         DEBUG(10, ("in this we have request for %s requested is %s\n",
543                    domain_name, r->in.req.servername));
544
545         if (strchr(domain_name,'.')) {
546                 need_fqdn = 1;
547         }
548
549         if (tsocket_address_is_inet(client, "ip")) {
550                 client_str = tsocket_address_inet_addr_string(client, r);
551                 if (client_str == NULL) {
552                         return NT_STATUS_NO_MEMORY;
553                 }
554         }
555
556         site_name = samdb_client_site_name(sam_ctx, r, client_str, NULL);
557
558         status = get_dcs(r, sam_ctx, site_name, need_fqdn, &set, 0);
559         if (!NT_STATUS_IS_OK(status)) {
560                 DEBUG(3,("Unable to get list of DCs - %s\n",
561                          nt_errstr(status)));
562                 return status;
563         }
564
565         for(i=0; set[i]; i++) {
566                 uint32_t j;
567
568                 dc_list = talloc_realloc(r, dc_list, const char*,
569                                          num_dcs + set[i]->count + 1);
570                 if (dc_list == NULL) {
571                         return NT_STATUS_NO_MEMORY;
572                 }
573
574                 for(j=0; j<set[i]->count; j++) {
575                         dc_list[num_dcs + j] = talloc_move(dc_list,
576                                                            &set[i]->names[j]);
577                 }
578                 num_dcs = num_dcs + set[i]->count;
579                 TALLOC_FREE(set[i]);
580                 dc_list[num_dcs] = NULL;
581         }
582
583         r->out.resp = talloc_zero(r, struct dfs_referral_resp);
584         if (r->out.resp == NULL) {
585                 return NT_STATUS_NO_MEMORY;
586         }
587
588         r->out.resp->path_consumed = 0;
589         r->out.resp->header_flags = 0; /* Do like w2k3 */
590         r->out.resp->nb_referrals = 1;
591
592         referrals = talloc_zero_array(r->out.resp,
593                                       struct dfs_referral_type,
594                                       r->out.resp->nb_referrals);
595         if (referrals == NULL) {
596                 return NT_STATUS_NO_MEMORY;
597         }
598         r->out.resp->referral_entries = referrals;
599
600         if (r->in.req.servername[0] == '\\') {
601                 referral_str = talloc_asprintf(referrals, "%s",
602                                                domain_name);
603         } else {
604                 referral_str = talloc_asprintf(referrals, "\\%s",
605                                                domain_name);
606         }
607         if (referral_str == NULL) {
608                 return NT_STATUS_NO_MEMORY;
609         }
610
611         status = fill_domain_dfs_referraltype(referrals,
612                                               &referrals[0], 3,
613                                               referral_str,
614                                               dc_list, num_dcs);
615         if (!NT_STATUS_IS_OK(status)) {
616                 DEBUG(2,("%s: Unable to fill domain referral structure - %s\n",
617                          __location__, nt_errstr(status)));
618                 return status;
619         }
620
621         return NT_STATUS_OK;
622 }
623
624 /*
625  * Handle the logic for dfs referral request like
626  * \\domain\sysvol or \\domain\netlogon
627  */
628 static NTSTATUS dosysvol_referral(struct loadparm_context *lp_ctx,
629                                   struct ldb_context *sam_ctx,
630                                   const struct tsocket_address *client,
631                                   struct dfs_GetDFSReferral *r,
632                                   const char *domain_name,
633                                   const char *dfs_name)
634 {
635         const char *site_name = NULL; /* Name of the site where the client is */
636         bool need_fqdn = false;
637         unsigned int i, c = 0, nb_entries = 0;
638         struct dc_set **set;
639         char *client_str = NULL;
640         NTSTATUS status;
641         struct dfs_referral_type *referrals;
642
643         if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_CONTROLLER) {
644                 return NT_STATUS_INVALID_PARAMETER;
645         }
646
647         if (r->in.req.max_referral_level < 3) {
648                 DEBUG(2,("invalid max_referral_level %u\n",
649                          r->in.req.max_referral_level));
650                 return NT_STATUS_UNSUCCESSFUL;
651         }
652
653         DEBUG(10, ("in this we have request for %s and share %s requested is %s\n",
654                    domain_name, dfs_name, r->in.req.servername));
655
656         if (strchr(domain_name,'.')) {
657                 need_fqdn = 1;
658         }
659
660         if (tsocket_address_is_inet(client, "ip")) {
661                 client_str = tsocket_address_inet_addr_string(client, r);
662                 if (client_str == NULL) {
663                         return NT_STATUS_NO_MEMORY;
664                 }
665         }
666
667         site_name = samdb_client_site_name(sam_ctx, r, client_str, NULL);
668
669         status = get_dcs(r, sam_ctx, site_name, need_fqdn, &set, 0);
670         if (!NT_STATUS_IS_OK(status)) {
671                 DEBUG(3,("Unable to get list of DCs - %s\n",
672                          nt_errstr(status)));
673                 return status;
674         }
675
676         for(i=0; set[i]; i++) {
677                 nb_entries = nb_entries + set[i]->count;
678         }
679
680         r->out.resp = talloc_zero(r, struct dfs_referral_resp);
681         if (r->out.resp == NULL) {
682                 return NT_STATUS_NO_MEMORY;
683         }
684
685         /* The length is expected in bytes */
686         r->out.resp->path_consumed = strlen_m(r->in.req.servername) * 2;
687         /* Do like w2k3 and like in 3.3.5.3 of MS-DFSC*/
688         r->out.resp->header_flags = DFS_HEADER_FLAG_STORAGE_SVR;
689         r->out.resp->nb_referrals = nb_entries;
690
691         referrals = talloc_zero_array(r->out.resp,
692                                       struct dfs_referral_type,
693                                       r->out.resp->nb_referrals);
694         if (referrals == NULL) {
695                 return NT_STATUS_NO_MEMORY;
696         }
697         r->out.resp->referral_entries = referrals;
698
699         c = 0;
700         for(i=0; set[i]; i++) {
701                 uint32_t j;
702
703                 for(j=0; j< set[i]->count; j++) {
704                         struct dfs_referral_type *ref = &referrals[c];
705                         const char *referral_str;
706
707                         referral_str = talloc_asprintf(referrals, "\\%s\\%s",
708                                                        set[i]->names[j], dfs_name);
709                         if (referral_str == NULL) {
710                                 return NT_STATUS_NO_MEMORY;
711                         }
712
713                         DEBUG(8,("Doing a dfs referral for %s with this value "
714                                  "%s requested %s\n",
715                                  set[i]->names[j], referral_str,
716                                  r->in.req.servername));
717
718                         status = fill_normal_dfs_referraltype(referrals, ref,
719                                         r->in.req.max_referral_level,
720                                         r->in.req.servername,
721                                         referral_str, c==0);
722
723
724                         if (!NT_STATUS_IS_OK(status)) {
725                                 DEBUG(2,("%s: Unable to fill domain referral "
726                                          "structure - %s\n",
727                                          __location__, nt_errstr(status)));
728                                 return status;
729                         }
730
731                         c++;
732                 }
733         }
734
735         return NT_STATUS_OK;
736 }
737
738 /*
739   trans2 getdfsreferral implementation
740 */
741 NTSTATUS dfs_server_ad_get_referrals(struct loadparm_context *lp_ctx,
742                                      struct ldb_context *sam_ctx,
743                                      const struct tsocket_address *client,
744                                      struct dfs_GetDFSReferral *r)
745 {
746         char *server_name = NULL;
747         char *dfs_name = NULL;
748         char *link_path = NULL;
749         const char *netbios_domain;
750         const char *dns_domain;
751         const char *netbios_name;
752         const char *dns_name;
753
754         if (!lpcfg_host_msdfs(lp_ctx)) {
755                 return NT_STATUS_FS_DRIVER_REQUIRED;
756         }
757
758         if (r->in.req.servername == NULL) {
759                 return NT_STATUS_INVALID_PARAMETER;
760         }
761
762         DEBUG(8, ("Requested DFS name: %s length: %u\n",
763                   r->in.req.servername,
764                   (unsigned int)strlen_m(r->in.req.servername)*2));
765
766         /*
767          * If the servername is "" then we are in a case of domain dfs
768          * and the client just searches for the list of local domain
769          * it is attached and also trusted ones.
770          */
771         if (strlen(r->in.req.servername) == 0) {
772                 return dodomain_referral(lp_ctx, sam_ctx, client, r);
773         }
774
775         server_name = talloc_strdup(r, r->in.req.servername);
776         if (server_name == NULL) {
777                 return NT_STATUS_NO_MEMORY;
778         }
779
780         while(*server_name && *server_name == '\\') {
781                 server_name++;
782         }
783
784         dfs_name = strchr(server_name, '\\');
785         if (dfs_name != NULL) {
786                 dfs_name[0] = '\0';
787                 dfs_name++;
788
789                 link_path = strchr(dfs_name, '\\');
790                 if (link_path != NULL) {
791                         link_path[0] = '\0';
792                         link_path++;
793                 }
794         }
795
796         if (link_path != NULL) {
797                 /*
798                  * If it is a DFS Link we do not
799                  * handle it here.
800                  */
801                 return NT_STATUS_NOT_FOUND;
802         }
803
804         netbios_domain = lpcfg_workgroup(lp_ctx);
805         dns_domain = lpcfg_dnsdomain(lp_ctx);
806         netbios_name = lpcfg_netbios_name(lp_ctx);
807         dns_name = talloc_asprintf(r, "%s.%s", netbios_name, dns_domain);
808         if (dns_name == NULL) {
809                 return NT_STATUS_NO_MEMORY;
810         }
811
812         if ((strcasecmp_m(server_name, netbios_name) == 0) ||
813             (strcasecmp_m(server_name, dns_name) == 0)) {
814                 /*
815                  * If it is not domain related do not
816                  * handle it here.
817                  */
818                 return NT_STATUS_NOT_FOUND;
819
820         }
821
822         if ((strcasecmp_m(server_name, netbios_domain) != 0) &&
823             (strcasecmp_m(server_name, dns_domain) != 0)) {
824                 /*
825                  * Not a domain we handle.
826                  */
827                 return NT_STATUS_INVALID_PARAMETER;
828         }
829
830         /*
831          * Here we have filtered the thing the requested name don't contain our DNS name.
832          * So if the share == NULL or if share in ("sysvol", "netlogon")
833          * then we proceed. In the first case it will be a dc refereal in the second it will
834          * be just a sysvol/netlogon referral.
835          */
836         if (dfs_name == NULL) {
837                 return dodc_referral(lp_ctx, sam_ctx,
838                                      client, r, server_name);
839         }
840
841         /*
842          * Here we have filtered the thing the requested name don't contain our DNS name.
843          * So if the share == NULL or if share in ("sysvol", "netlogon")
844          * then we proceed. In the first case it will be a dc refereal in the second it will
845          * be just a sysvol/netlogon referral.
846          */
847         if (strcasecmp(dfs_name, "sysvol") == 0 ||
848             strcasecmp(dfs_name, "netlogon") == 0) {
849                 return dosysvol_referral(lp_ctx, sam_ctx, client, r,
850                                          server_name, dfs_name);
851         }
852
853         /* By default until all the case are handled */
854         return NT_STATUS_NOT_FOUND;
855 }