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