winbindd: add service routines to support a sorted client list
[samba.git] / source3 / winbindd / winbindd_util.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon for ntdom nss module
5
6    Copyright (C) Tim Potter 2000-2001
7    Copyright (C) 2001 by Martin Pool <mbp@samba.org>
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "winbindd.h"
25 #include "secrets.h"
26 #include "../libcli/security/security.h"
27 #include "../libcli/auth/pam_errors.h"
28 #include "passdb/machine_sid.h"
29 #include "passdb.h"
30 #include "source4/lib/messaging/messaging.h"
31 #include "librpc/gen_ndr/ndr_lsa.h"
32 #include "auth/credentials/credentials.h"
33
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_WINBIND
36
37 extern struct winbindd_methods cache_methods;
38
39 /**
40  * @file winbindd_util.c
41  *
42  * Winbind daemon for NT domain authentication nss module.
43  **/
44
45
46 /* The list of trusted domains.  Note that the list can be deleted and
47    recreated using the init_domain_list() function so pointers to
48    individual winbindd_domain structures cannot be made.  Keep a copy of
49    the domain name instead. */
50
51 static struct winbindd_domain *_domain_list = NULL;
52
53 struct winbindd_domain *domain_list(void)
54 {
55         /* Initialise list */
56
57         if ((!_domain_list) && (!init_domain_list())) {
58                 smb_panic("Init_domain_list failed");
59         }
60
61         return _domain_list;
62 }
63
64 /* Free all entries in the trusted domain list */
65
66 static void free_domain_list(void)
67 {
68         struct winbindd_domain *domain = _domain_list;
69
70         while(domain) {
71                 struct winbindd_domain *next = domain->next;
72
73                 DLIST_REMOVE(_domain_list, domain);
74                 TALLOC_FREE(domain);
75                 domain = next;
76         }
77 }
78
79 /**
80  * Iterator for winbindd's domain list.
81  * To be used (e.g.) in tevent based loops.
82  */
83 struct winbindd_domain *wb_next_domain(struct winbindd_domain *domain)
84 {
85         if (domain == NULL) {
86                 domain = domain_list();
87         } else {
88                 domain = domain->next;
89         }
90
91         if ((domain != NULL) &&
92             (lp_server_role() != ROLE_ACTIVE_DIRECTORY_DC) &&
93             sid_check_is_our_sam(&domain->sid))
94         {
95                 domain = domain->next;
96         }
97
98         return domain;
99 }
100
101 static bool is_internal_domain(const struct dom_sid *sid)
102 {
103         if (sid == NULL)
104                 return False;
105
106         return (sid_check_is_our_sam(sid) || sid_check_is_builtin(sid));
107 }
108
109 static bool is_in_internal_domain(const struct dom_sid *sid)
110 {
111         if (sid == NULL)
112                 return False;
113
114         return (sid_check_is_in_our_sam(sid) || sid_check_is_in_builtin(sid));
115 }
116
117
118 /* Add a trusted domain to our list of domains.
119    If the domain already exists in the list,
120    return it and don't re-initialize.  */
121
122 static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
123                                                   struct winbindd_methods *methods,
124                                                   const struct dom_sid *sid)
125 {
126         struct winbindd_domain *domain;
127         const char *alternative_name = NULL;
128         char *idmap_config_option;
129         const char *param;
130         const char **ignored_domains, **dom;
131         int role = lp_server_role();
132
133         ignored_domains = lp_parm_string_list(-1, "winbind", "ignore domains", NULL);
134         for (dom=ignored_domains; dom && *dom; dom++) {
135                 if (gen_fnmatch(*dom, domain_name) == 0) {
136                         DEBUG(2,("Ignoring domain '%s'\n", domain_name));
137                         return NULL;
138                 }
139         }
140
141         /* use alt_name if available to allow DNS lookups */
142
143         if (alt_name && *alt_name) {
144                 alternative_name = alt_name;
145         }
146
147         /* We can't call domain_list() as this function is called from
148            init_domain_list() and we'll get stuck in a loop. */
149         for (domain = _domain_list; domain; domain = domain->next) {
150                 if (strequal(domain_name, domain->name) ||
151                     strequal(domain_name, domain->alt_name))
152                 {
153                         break;
154                 }
155
156                 if (alternative_name && *alternative_name)
157                 {
158                         if (strequal(alternative_name, domain->name) ||
159                             strequal(alternative_name, domain->alt_name))
160                         {
161                                 break;
162                         }
163                 }
164
165                 if (sid)
166                 {
167                         if (is_null_sid(sid)) {
168                                 continue;
169                         }
170
171                         if (dom_sid_equal(sid, &domain->sid)) {
172                                 break;
173                         }
174                 }
175         }
176
177         if (domain != NULL) {
178                 /*
179                  * We found a match on domain->name or
180                  * domain->alt_name. Possibly update the SID
181                  * if the stored SID was the NULL SID
182                  * and return the matching entry.
183                  */
184                 if ((sid != NULL)
185                     && dom_sid_equal(&domain->sid, &global_sid_NULL)) {
186                         sid_copy( &domain->sid, sid );
187                 }
188                 return domain;
189         }
190
191         /* Create new domain entry */
192         domain = talloc_zero(NULL, struct winbindd_domain);
193         if (domain == NULL) {
194                 return NULL;
195         }
196
197         domain->children = talloc_zero_array(domain,
198                                              struct winbindd_child,
199                                              lp_winbind_max_domain_connections());
200         if (domain->children == NULL) {
201                 TALLOC_FREE(domain);
202                 return NULL;
203         }
204
205         domain->name = talloc_strdup(domain, domain_name);
206         if (domain->name == NULL) {
207                 TALLOC_FREE(domain);
208                 return NULL;
209         }
210
211         if (alternative_name) {
212                 domain->alt_name = talloc_strdup(domain, alternative_name);
213                 if (domain->alt_name == NULL) {
214                         TALLOC_FREE(domain);
215                         return NULL;
216                 }
217         }
218
219         domain->methods = methods;
220         domain->backend = NULL;
221         domain->internal = is_internal_domain(sid);
222         domain->sequence_number = DOM_SEQUENCE_NONE;
223         domain->last_seq_check = 0;
224         domain->initialized = False;
225         domain->online = is_internal_domain(sid);
226         domain->check_online_timeout = 0;
227         domain->dc_probe_pid = (pid_t)-1;
228         if (sid) {
229                 sid_copy(&domain->sid, sid);
230         }
231
232         /* Is this our primary domain ? */
233         if (strequal(domain_name, get_global_sam_name()) &&
234                         (role != ROLE_DOMAIN_MEMBER)) {
235                 domain->primary = true;
236         } else if (strequal(domain_name, lp_workgroup()) &&
237                         (role == ROLE_DOMAIN_MEMBER)) {
238                 domain->primary = true;
239         }
240
241         if (domain->primary) {
242                 if (role == ROLE_ACTIVE_DIRECTORY_DC) {
243                         domain->active_directory = true;
244                 }
245                 if (lp_security() == SEC_ADS) {
246                         domain->active_directory = true;
247                 }
248         }
249
250         /* Link to domain list */
251         DLIST_ADD_END(_domain_list, domain, struct winbindd_domain *);
252
253         wcache_tdc_add_domain( domain );
254
255         idmap_config_option = talloc_asprintf(talloc_tos(), "idmap config %s",
256                                               domain->name);
257         if (idmap_config_option == NULL) {
258                 DEBUG(0, ("talloc failed, not looking for idmap config\n"));
259                 goto done;
260         }
261
262         param = lp_parm_const_string(-1, idmap_config_option, "range", NULL);
263
264         DEBUG(10, ("%s : range = %s\n", idmap_config_option,
265                    param ? param : "not defined"));
266
267         if (param != NULL) {
268                 unsigned low_id, high_id;
269                 if (sscanf(param, "%u - %u", &low_id, &high_id) != 2) {
270                         DEBUG(1, ("invalid range syntax in %s: %s\n",
271                                   idmap_config_option, param));
272                         goto done;
273                 }
274                 if (low_id > high_id) {
275                         DEBUG(1, ("invalid range in %s: %s\n",
276                                   idmap_config_option, param));
277                         goto done;
278                 }
279                 domain->have_idmap_config = true;
280                 domain->id_range_low = low_id;
281                 domain->id_range_high = high_id;
282         }
283
284 done:
285
286         setup_domain_child(domain);
287
288         DEBUG(2,("Added domain %s %s %s\n",
289                  domain->name, domain->alt_name,
290                  &domain->sid?sid_string_dbg(&domain->sid):""));
291
292         return domain;
293 }
294
295 bool domain_is_forest_root(const struct winbindd_domain *domain)
296 {
297         const uint32_t fr_flags =
298                 (NETR_TRUST_FLAG_TREEROOT|NETR_TRUST_FLAG_IN_FOREST);
299
300         return ((domain->domain_flags & fr_flags) == fr_flags);
301 }
302
303 /********************************************************************
304   rescan our domains looking for new trusted domains
305 ********************************************************************/
306
307 struct trustdom_state {
308         struct winbindd_domain *domain;
309         struct winbindd_request request;
310 };
311
312 static void trustdom_list_done(struct tevent_req *req);
313 static void rescan_forest_root_trusts( void );
314 static void rescan_forest_trusts( void );
315
316 static void add_trusted_domains( struct winbindd_domain *domain )
317 {
318         struct trustdom_state *state;
319         struct tevent_req *req;
320
321         state = talloc_zero(NULL, struct trustdom_state);
322         if (state == NULL) {
323                 DEBUG(0, ("talloc failed\n"));
324                 return;
325         }
326         state->domain = domain;
327
328         state->request.length = sizeof(state->request);
329         state->request.cmd = WINBINDD_LIST_TRUSTDOM;
330
331         req = wb_domain_request_send(state, winbind_event_context(),
332                                      domain, &state->request);
333         if (req == NULL) {
334                 DEBUG(1, ("wb_domain_request_send failed\n"));
335                 TALLOC_FREE(state);
336                 return;
337         }
338         tevent_req_set_callback(req, trustdom_list_done, state);
339 }
340
341 static void trustdom_list_done(struct tevent_req *req)
342 {
343         struct trustdom_state *state = tevent_req_callback_data(
344                 req, struct trustdom_state);
345         struct winbindd_response *response;
346         int res, err;
347         char *p;
348
349         res = wb_domain_request_recv(req, state, &response, &err);
350         if ((res == -1) || (response->result != WINBINDD_OK)) {
351                 DEBUG(1, ("Could not receive trustdoms\n"));
352                 TALLOC_FREE(state);
353                 return;
354         }
355
356         p = (char *)response->extra_data.data;
357
358         while ((p != NULL) && (*p != '\0')) {
359                 char *q, *sidstr, *alt_name;
360                 struct dom_sid sid;
361                 char *alternate_name = NULL;
362
363                 alt_name = strchr(p, '\\');
364                 if (alt_name == NULL) {
365                         DEBUG(0, ("Got invalid trustdom response\n"));
366                         break;
367                 }
368
369                 *alt_name = '\0';
370                 alt_name += 1;
371
372                 sidstr = strchr(alt_name, '\\');
373                 if (sidstr == NULL) {
374                         DEBUG(0, ("Got invalid trustdom response\n"));
375                         break;
376                 }
377
378                 *sidstr = '\0';
379                 sidstr += 1;
380
381                 q = strchr(sidstr, '\n');
382                 if (q != NULL)
383                         *q = '\0';
384
385                 if (!string_to_sid(&sid, sidstr)) {
386                         DEBUG(0, ("Got invalid trustdom response\n"));
387                         break;
388                 }
389
390                 /* use the real alt_name if we have one, else pass in NULL */
391
392                 if ( !strequal( alt_name, "(null)" ) )
393                         alternate_name = alt_name;
394
395                 /*
396                  * We always call add_trusted_domain() cause on an existing
397                  * domain structure, it will update the SID if necessary.
398                  * This is important because we need the SID for sibling
399                  * domains.
400                  */
401                 (void)add_trusted_domain(p, alternate_name,
402                                             &cache_methods,
403                                             &sid);
404
405                 p=q;
406                 if (p != NULL)
407                         p += 1;
408         }
409
410         /*
411            Cases to consider when scanning trusts:
412            (a) we are calling from a child domain (primary && !forest_root)
413            (b) we are calling from the root of the forest (primary && forest_root)
414            (c) we are calling from a trusted forest domain (!primary
415                && !forest_root)
416         */
417
418         if (state->domain->primary) {
419                 /* If this is our primary domain and we are not in the
420                    forest root, we have to scan the root trusts first */
421
422                 if (!domain_is_forest_root(state->domain))
423                         rescan_forest_root_trusts();
424                 else
425                         rescan_forest_trusts();
426
427         } else if (domain_is_forest_root(state->domain)) {
428                 /* Once we have done root forest trust search, we can
429                    go on to search the trusted forests */
430
431                 rescan_forest_trusts();
432         }
433
434         TALLOC_FREE(state);
435
436         return;
437 }
438
439 /********************************************************************
440  Scan the trusts of our forest root
441 ********************************************************************/
442
443 static void rescan_forest_root_trusts( void )
444 {
445         struct winbindd_tdc_domain *dom_list = NULL;
446         size_t num_trusts = 0;
447         int i;
448
449         /* The only transitive trusts supported by Windows 2003 AD are
450            (a) Parent-Child, (b) Tree-Root, and (c) Forest.   The
451            first two are handled in forest and listed by
452            DsEnumerateDomainTrusts().  Forest trusts are not so we
453            have to do that ourselves. */
454
455         if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
456                 return;
457
458         for ( i=0; i<num_trusts; i++ ) {
459                 struct winbindd_domain *d = NULL;
460
461                 /* Find the forest root.  Don't necessarily trust
462                    the domain_list() as our primary domain may not
463                    have been initialized. */
464
465                 if ( !(dom_list[i].trust_flags & NETR_TRUST_FLAG_TREEROOT) ) {
466                         continue;
467                 }
468
469                 /* Here's the forest root */
470
471                 d = find_domain_from_name_noinit( dom_list[i].domain_name );
472
473                 if ( !d ) {
474                         d = add_trusted_domain( dom_list[i].domain_name,
475                                                 dom_list[i].dns_name,
476                                                 &cache_methods,
477                                                 &dom_list[i].sid );
478                 }
479
480                 if (d == NULL) {
481                         continue;
482                 }
483
484                 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
485                           "for domain tree root %s (%s)\n",
486                           d->name, d->alt_name ));
487
488                 d->domain_flags = dom_list[i].trust_flags;
489                 d->domain_type  = dom_list[i].trust_type;
490                 d->domain_trust_attribs = dom_list[i].trust_attribs;
491
492                 add_trusted_domains( d );
493
494                 break;
495         }
496
497         TALLOC_FREE( dom_list );
498
499         return;
500 }
501
502 /********************************************************************
503  scan the transitive forest trusts (not our own)
504 ********************************************************************/
505
506
507 static void rescan_forest_trusts( void )
508 {
509         struct winbindd_domain *d = NULL;
510         struct winbindd_tdc_domain *dom_list = NULL;
511         size_t num_trusts = 0;
512         int i;
513
514         /* The only transitive trusts supported by Windows 2003 AD are
515            (a) Parent-Child, (b) Tree-Root, and (c) Forest.   The
516            first two are handled in forest and listed by
517            DsEnumerateDomainTrusts().  Forest trusts are not so we
518            have to do that ourselves. */
519
520         if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
521                 return;
522
523         for ( i=0; i<num_trusts; i++ ) {
524                 uint32_t flags   = dom_list[i].trust_flags;
525                 uint32_t type    = dom_list[i].trust_type;
526                 uint32_t attribs = dom_list[i].trust_attribs;
527
528                 d = find_domain_from_name_noinit( dom_list[i].domain_name );
529
530                 /* ignore our primary and internal domains */
531
532                 if ( d && (d->internal || d->primary ) )
533                         continue;
534
535                 if ( (flags & NETR_TRUST_FLAG_INBOUND) &&
536                      (type == LSA_TRUST_TYPE_UPLEVEL) &&
537                      (attribs == LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) )
538                 {
539                         /* add the trusted domain if we don't know
540                            about it */
541
542                         if ( !d ) {
543                                 d = add_trusted_domain( dom_list[i].domain_name,
544                                                         dom_list[i].dns_name,
545                                                         &cache_methods,
546                                                         &dom_list[i].sid );
547                         }
548
549                         if (d == NULL) {
550                                 continue;
551                         }
552
553                         DEBUG(10,("Following trust path for domain %s (%s)\n",
554                                   d->name, d->alt_name ));
555                         add_trusted_domains( d );
556                 }
557         }
558
559         TALLOC_FREE( dom_list );
560
561         return;
562 }
563
564 /*********************************************************************
565  The process of updating the trusted domain list is a three step
566  async process:
567  (a) ask our domain
568  (b) ask the root domain in our forest
569  (c) ask the a DC in any Win2003 trusted forests
570 *********************************************************************/
571
572 void rescan_trusted_domains(struct tevent_context *ev, struct tevent_timer *te,
573                             struct timeval now, void *private_data)
574 {
575         TALLOC_FREE(te);
576
577         /* I use to clear the cache here and start over but that
578            caused problems in child processes that needed the
579            trust dom list early on.  Removing it means we
580            could have some trusted domains listed that have been
581            removed from our primary domain's DC until a full
582            restart.  This should be ok since I think this is what
583            Windows does as well. */
584
585         /* this will only add new domains we didn't already know about
586            in the domain_list()*/
587
588         add_trusted_domains( find_our_domain() );
589
590         te = tevent_add_timer(
591                 ev, NULL, timeval_current_ofs(WINBINDD_RESCAN_FREQ, 0),
592                 rescan_trusted_domains, NULL);
593         /*
594          * If te == NULL, there's not much we can do here. Don't fail, the
595          * only thing we miss is new trusted domains.
596          */
597
598         return;
599 }
600
601 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
602                                                    struct winbindd_cli_state *state)
603 {
604         /* Ensure null termination */
605         state->request->domain_name
606                 [sizeof(state->request->domain_name)-1]='\0';
607         state->request->data.init_conn.dcname
608                 [sizeof(state->request->data.init_conn.dcname)-1]='\0';
609
610         if (strlen(state->request->data.init_conn.dcname) > 0) {
611                 fstrcpy(domain->dcname, state->request->data.init_conn.dcname);
612         }
613
614         init_dc_connection(domain, false);
615
616         if (!domain->initialized) {
617                 /* If we return error here we can't do any cached authentication,
618                    but we may be in disconnected mode and can't initialize correctly.
619                    Do what the previous code did and just return without initialization,
620                    once we go online we'll re-initialize.
621                 */
622                 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
623                         "online = %d\n", domain->name, (int)domain->online ));
624         }
625
626         fstrcpy(state->response->data.domain_info.name, domain->name);
627         fstrcpy(state->response->data.domain_info.alt_name, domain->alt_name);
628         sid_to_fstring(state->response->data.domain_info.sid, &domain->sid);
629
630         state->response->data.domain_info.native_mode
631                 = domain->native_mode;
632         state->response->data.domain_info.active_directory
633                 = domain->active_directory;
634         state->response->data.domain_info.primary
635                 = domain->primary;
636
637         return WINBINDD_OK;
638 }
639
640 static void wb_imsg_new_trusted_domain(struct imessaging_context *msg,
641                                        void *private_data,
642                                        uint32_t msg_type,
643                                        struct server_id server_id,
644                                        DATA_BLOB *data)
645 {
646         TALLOC_CTX *frame = talloc_stackframe();
647         struct lsa_TrustDomainInfoInfoEx info;
648         enum ndr_err_code ndr_err;
649         struct winbindd_domain *d = NULL;
650
651         DEBUG(5, ("wb_imsg_new_trusted_domain\n"));
652
653         if (data == NULL) {
654                 TALLOC_FREE(frame);
655                 return;
656         }
657
658         ndr_err = ndr_pull_struct_blob_all(data, frame, &info,
659                         (ndr_pull_flags_fn_t)ndr_pull_lsa_TrustDomainInfoInfoEx);
660         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
661                 TALLOC_FREE(frame);
662                 return;
663         }
664
665         d = find_domain_from_name_noinit(info.netbios_name.string);
666         if (d != NULL) {
667                 TALLOC_FREE(frame);
668                 return;
669         }
670
671         d = add_trusted_domain(info.netbios_name.string,
672                                info.domain_name.string,
673                                &cache_methods,
674                                info.sid);
675         if (d == NULL) {
676                 TALLOC_FREE(frame);
677                 return;
678         }
679
680         if (d->internal) {
681                 TALLOC_FREE(frame);
682                 return;
683         }
684
685         if (d->primary) {
686                 TALLOC_FREE(frame);
687                 return;
688         }
689
690         if (info.trust_direction & LSA_TRUST_DIRECTION_INBOUND) {
691                 d->domain_flags |= NETR_TRUST_FLAG_INBOUND;
692         }
693         if (info.trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) {
694                 d->domain_flags |= NETR_TRUST_FLAG_OUTBOUND;
695         }
696         if (info.trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
697                 d->domain_flags |= NETR_TRUST_FLAG_IN_FOREST;
698         }
699         d->domain_type = info.trust_type;
700         d->domain_trust_attribs = info.trust_attributes;
701
702         TALLOC_FREE(frame);
703 }
704
705 /*
706  * We did not get the secret when we queried secrets.tdb, so read it
707  * from secrets.tdb and re-sync the databases
708  */
709 static bool migrate_secrets_tdb_to_ldb(struct winbindd_domain *domain)
710 {
711         bool ok;
712         struct cli_credentials *creds;
713         NTSTATUS can_migrate = pdb_get_trust_credentials(domain->name,
714                                                          NULL, domain, &creds);
715         if (!NT_STATUS_IS_OK(can_migrate)) {
716                 DEBUG(0, ("Failed to fetch our own, local AD domain join "
717                         "password for winbindd's internal use, both from "
718                         "secrets.tdb and secrets.ldb: %s\n",
719                         nt_errstr(can_migrate)));
720                 return false;
721         }
722
723         /*
724          * NOTE: It is very unlikely we end up here if there is an
725          * oldpass, because a new password is created at
726          * classicupgrade, so this is not a concern.
727          */
728         ok = secrets_store_machine_pw_sync(cli_credentials_get_password(creds),
729                    NULL /* oldpass */,
730                    cli_credentials_get_domain(creds),
731                    cli_credentials_get_realm(creds),
732                    cli_credentials_get_salt_principal(creds),
733                    0, /* Supported enc types, unused */
734                    &domain->sid,
735                    cli_credentials_get_password_last_changed_time(creds),
736                    cli_credentials_get_secure_channel_type(creds),
737                    false /* do_delete: Do not delete */);
738         TALLOC_FREE(creds);
739         if (ok == false) {
740                 DEBUG(0, ("Failed to write our our own, "
741                           "local AD domain join password for "
742                           "winbindd's internal use into secrets.tdb\n"));
743                 return false;
744         }
745         return true;
746 }
747
748 /* Look up global info for the winbind daemon */
749 bool init_domain_list(void)
750 {
751         int role = lp_server_role();
752         NTSTATUS status;
753
754         /* Free existing list */
755         free_domain_list();
756
757         /* BUILTIN domain */
758
759         (void)add_trusted_domain("BUILTIN", NULL, &cache_methods,
760                                     &global_sid_Builtin);
761
762         /* Local SAM */
763
764         if ( role == ROLE_ACTIVE_DIRECTORY_DC ) {
765                 struct winbindd_domain *domain;
766                 enum netr_SchannelType sec_chan_type;
767                 const char *account_name;
768                 struct samr_Password current_nt_hash;
769                 struct pdb_domain_info *pdb_domain_info;
770                 bool ok;
771
772                 pdb_domain_info = pdb_get_domain_info(talloc_tos());
773                 if (pdb_domain_info == NULL) {
774                         DEBUG(0, ("Failed to fetch our own, local AD "
775                                 "domain info from sam.ldb\n"));
776                         return false;
777                 }
778                 domain = add_trusted_domain(pdb_domain_info->name,
779                                         pdb_domain_info->dns_domain,
780                                         &cache_methods,
781                                         &pdb_domain_info->sid);
782                 TALLOC_FREE(pdb_domain_info);
783                 if (domain == NULL) {
784                         DEBUG(0, ("Failed to add our own, local AD "
785                                 "domain to winbindd's internal list\n"));
786                         return false;
787                 }
788
789                 /*
790                  * We need to call this to find out if we are an RODC
791                  */
792                 ok = get_trust_pw_hash(domain->name,
793                                        current_nt_hash.hash,
794                                        &account_name,
795                                        &sec_chan_type);
796                 if (!ok) {
797                         /*
798                          * If get_trust_pw_hash() fails, then try and
799                          * fetch the password from the more recent of
800                          * secrets.{ldb,tdb} using the
801                          * pdb_get_trust_credentials()
802                          */
803                         ok = migrate_secrets_tdb_to_ldb(domain);
804
805                         if (ok == false) {
806                                 DEBUG(0, ("Failed to migrate our own, "
807                                           "local AD domain join password for "
808                                           "winbindd's internal use into "
809                                           "secrets.tdb\n"));
810                                 return false;
811                         }
812                         ok = get_trust_pw_hash(domain->name,
813                                                current_nt_hash.hash,
814                                                &account_name,
815                                                &sec_chan_type);
816                         if (ok == false) {
817                                 DEBUG(0, ("Failed to find our our own, just "
818                                           "written local AD domain join "
819                                           "password for winbindd's internal "
820                                           "use in secrets.tdb\n"));
821                                 return false;
822                         }
823                 }
824                 if (sec_chan_type == SEC_CHAN_RODC) {
825                         domain->rodc = true;
826                 }
827
828         } else {
829                 (void)add_trusted_domain(get_global_sam_name(), NULL,
830                                          &cache_methods, get_global_sam_sid());
831         }
832         /* Add ourselves as the first entry. */
833
834         if ( role == ROLE_DOMAIN_MEMBER ) {
835                 struct winbindd_domain *domain;
836                 struct dom_sid our_sid;
837
838                 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
839                         DEBUG(0, ("Could not fetch our SID - did we join?\n"));
840                         return False;
841                 }
842
843                 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
844                                              &cache_methods, &our_sid);
845                 if (domain) {
846                         /* Even in the parent winbindd we'll need to
847                            talk to the DC, so try and see if we can
848                            contact it. Theoretically this isn't neccessary
849                            as the init_dc_connection() in init_child_recv()
850                            will do this, but we can start detecting the DC
851                            early here. */
852                         set_domain_online_request(domain);
853                 }
854         }
855
856         status = imessaging_register(winbind_imessaging_context(), NULL,
857                                      MSG_WINBIND_NEW_TRUSTED_DOMAIN,
858                                      wb_imsg_new_trusted_domain);
859         if (!NT_STATUS_IS_OK(status)) {
860                 DEBUG(0, ("imessaging_register(MSG_WINBIND_NEW_TRUSTED_DOMAIN) - %s\n",
861                           nt_errstr(status)));
862                 return false;
863         }
864
865         return True;
866 }
867
868 /**
869  * Given a domain name, return the struct winbindd domain info for it
870  *
871  * @note Do *not* pass lp_workgroup() to this function.  domain_list
872  *       may modify it's value, and free that pointer.  Instead, our local
873  *       domain may be found by calling find_our_domain().
874  *       directly.
875  *
876  *
877  * @return The domain structure for the named domain, if it is working.
878  */
879
880 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
881 {
882         struct winbindd_domain *domain;
883
884         /* Search through list */
885
886         for (domain = domain_list(); domain != NULL; domain = domain->next) {
887                 if (strequal(domain_name, domain->name) ||
888                     (domain->alt_name != NULL &&
889                      strequal(domain_name, domain->alt_name))) {
890                         return domain;
891                 }
892         }
893
894         /* Not found */
895
896         return NULL;
897 }
898
899 struct winbindd_domain *find_domain_from_name(const char *domain_name)
900 {
901         struct winbindd_domain *domain;
902
903         domain = find_domain_from_name_noinit(domain_name);
904
905         if (domain == NULL)
906                 return NULL;
907
908         if (!domain->initialized)
909                 init_dc_connection(domain, false);
910
911         return domain;
912 }
913
914 /* Given a domain sid, return the struct winbindd domain info for it */
915
916 struct winbindd_domain *find_domain_from_sid_noinit(const struct dom_sid *sid)
917 {
918         struct winbindd_domain *domain;
919
920         /* Search through list */
921
922         for (domain = domain_list(); domain != NULL; domain = domain->next) {
923                 if (dom_sid_compare_domain(sid, &domain->sid) == 0)
924                         return domain;
925         }
926
927         /* Not found */
928
929         return NULL;
930 }
931
932 /* Given a domain sid, return the struct winbindd domain info for it */
933
934 struct winbindd_domain *find_domain_from_sid(const struct dom_sid *sid)
935 {
936         struct winbindd_domain *domain;
937
938         domain = find_domain_from_sid_noinit(sid);
939
940         if (domain == NULL)
941                 return NULL;
942
943         if (!domain->initialized)
944                 init_dc_connection(domain, false);
945
946         return domain;
947 }
948
949 struct winbindd_domain *find_our_domain(void)
950 {
951         struct winbindd_domain *domain;
952
953         /* Search through list */
954
955         for (domain = domain_list(); domain != NULL; domain = domain->next) {
956                 if (domain->primary)
957                         return domain;
958         }
959
960         smb_panic("Could not find our domain");
961         return NULL;
962 }
963
964 struct winbindd_domain *find_root_domain(void)
965 {
966         struct winbindd_domain *ours = find_our_domain();
967
968         if (ours->forest_name == NULL) {
969                 return NULL;
970         }
971
972         return find_domain_from_name( ours->forest_name );
973 }
974
975 struct winbindd_domain *find_builtin_domain(void)
976 {
977         struct winbindd_domain *domain;
978
979         domain = find_domain_from_sid(&global_sid_Builtin);
980         if (domain == NULL) {
981                 smb_panic("Could not find BUILTIN domain");
982         }
983
984         return domain;
985 }
986
987 /* Find the appropriate domain to lookup a name or SID */
988
989 struct winbindd_domain *find_lookup_domain_from_sid(const struct dom_sid *sid)
990 {
991         /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */
992
993         if ( sid_check_is_in_unix_groups(sid) ||
994              sid_check_is_unix_groups(sid) ||
995              sid_check_is_in_unix_users(sid) ||
996              sid_check_is_unix_users(sid) )
997         {
998                 return find_domain_from_sid(get_global_sam_sid());
999         }
1000
1001         /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
1002          * one to contact the external DC's. On member servers the internal
1003          * domains are different: These are part of the local SAM. */
1004
1005         DEBUG(10, ("find_lookup_domain_from_sid(%s)\n", sid_string_dbg(sid)));
1006
1007         if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
1008                 DEBUG(10, ("calling find_domain_from_sid\n"));
1009                 return find_domain_from_sid(sid);
1010         }
1011
1012         /* On a member server a query for SID or name can always go to our
1013          * primary DC. */
1014
1015         DEBUG(10, ("calling find_our_domain\n"));
1016         return find_our_domain();
1017 }
1018
1019 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
1020 {
1021         if ( strequal(domain_name, unix_users_domain_name() ) ||
1022              strequal(domain_name, unix_groups_domain_name() ) )
1023         {
1024                 /*
1025                  * The "Unix User" and "Unix Group" domain our handled by
1026                  * passdb
1027                  */
1028                 return find_domain_from_name_noinit( get_global_sam_name() );
1029         }
1030
1031         if (IS_DC || strequal(domain_name, "BUILTIN") ||
1032             strequal(domain_name, get_global_sam_name()))
1033                 return find_domain_from_name_noinit(domain_name);
1034
1035
1036         return find_our_domain();
1037 }
1038
1039 /* Is this a domain which we may assume no DOMAIN\ prefix? */
1040
1041 static bool assume_domain(const char *domain)
1042 {
1043         /* never assume the domain on a standalone server */
1044
1045         if ( lp_server_role() == ROLE_STANDALONE )
1046                 return False;
1047
1048         /* domain member servers may possibly assume for the domain name */
1049
1050         if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
1051                 if ( !strequal(lp_workgroup(), domain) )
1052                         return False;
1053
1054                 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
1055                         return True;
1056         }
1057
1058         /* only left with a domain controller */
1059
1060         if ( strequal(get_global_sam_name(), domain) )  {
1061                 return True;
1062         }
1063
1064         return False;
1065 }
1066
1067 /* Parse a string of the form DOMAIN\user into a domain and a user */
1068
1069 bool parse_domain_user(const char *domuser, fstring domain, fstring user)
1070 {
1071         char *p = strchr(domuser,*lp_winbind_separator());
1072
1073         if ( !p ) {
1074                 fstrcpy(user, domuser);
1075
1076                 if ( assume_domain(lp_workgroup())) {
1077                         fstrcpy(domain, lp_workgroup());
1078                 } else if ((p = strchr(domuser, '@')) != NULL) {
1079                         fstrcpy(domain, p + 1);
1080                         user[PTR_DIFF(p, domuser)] = 0;
1081                 } else {
1082                         return False;
1083                 }
1084         } else {
1085                 fstrcpy(user, p+1);
1086                 fstrcpy(domain, domuser);
1087                 domain[PTR_DIFF(p, domuser)] = 0;
1088         }
1089
1090         return strupper_m(domain);
1091 }
1092
1093 bool parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
1094                               char **domain, char **user)
1095 {
1096         fstring fstr_domain, fstr_user;
1097         if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
1098                 return False;
1099         }
1100         *domain = talloc_strdup(mem_ctx, fstr_domain);
1101         *user = talloc_strdup(mem_ctx, fstr_user);
1102         return ((*domain != NULL) && (*user != NULL));
1103 }
1104
1105 /* Ensure an incoming username from NSS is fully qualified. Replace the
1106    incoming fstring with DOMAIN <separator> user. Returns the same
1107    values as parse_domain_user() but also replaces the incoming username.
1108    Used to ensure all names are fully qualified within winbindd.
1109    Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
1110    The protocol definitions of auth_crap, chng_pswd_auth_crap
1111    really should be changed to use this instead of doing things
1112    by hand. JRA. */
1113
1114 bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
1115 {
1116         if (!parse_domain_user(username_inout, domain, user)) {
1117                 return False;
1118         }
1119         slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
1120                  domain, *lp_winbind_separator(),
1121                  user);
1122         return True;
1123 }
1124
1125 /*
1126     Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
1127     'winbind separator' options.
1128     This means:
1129         - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
1130         lp_workgroup()
1131
1132     If we are a PDC or BDC, and this is for our domain, do likewise.
1133
1134     Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
1135     username is then unqualified in unix
1136
1137     On an AD DC we always fill DOMAIN\\USERNAME.
1138
1139     We always canonicalize as UPPERCASE DOMAIN, lowercase username.
1140 */
1141 void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume)
1142 {
1143         fstring tmp_user;
1144
1145         if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
1146                 can_assume = false;
1147         }
1148
1149         fstrcpy(tmp_user, user);
1150         (void)strlower_m(tmp_user);
1151
1152         if (can_assume && assume_domain(domain)) {
1153                 strlcpy(name, tmp_user, sizeof(fstring));
1154         } else {
1155                 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
1156                          domain, *lp_winbind_separator(),
1157                          tmp_user);
1158         }
1159 }
1160
1161 /**
1162  * talloc version of fill_domain_username()
1163  * return NULL on talloc failure.
1164  */
1165 char *fill_domain_username_talloc(TALLOC_CTX *mem_ctx,
1166                                   const char *domain,
1167                                   const char *user,
1168                                   bool can_assume)
1169 {
1170         char *tmp_user, *name;
1171
1172         if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
1173                 can_assume = false;
1174         }
1175
1176         tmp_user = talloc_strdup(mem_ctx, user);
1177         if (!strlower_m(tmp_user)) {
1178                 TALLOC_FREE(tmp_user);
1179                 return NULL;
1180         }
1181
1182         if (can_assume && assume_domain(domain)) {
1183                 name = tmp_user;
1184         } else {
1185                 name = talloc_asprintf(mem_ctx, "%s%c%s",
1186                                        domain,
1187                                        *lp_winbind_separator(),
1188                                        tmp_user);
1189                 TALLOC_FREE(tmp_user);
1190         }
1191
1192         return name;
1193 }
1194
1195 /*
1196  * Client list accessor functions
1197  */
1198
1199 static struct winbindd_cli_state *_client_list;
1200 static int _num_clients;
1201
1202 /* Return list of all connected clients */
1203
1204 struct winbindd_cli_state *winbindd_client_list(void)
1205 {
1206         return _client_list;
1207 }
1208
1209 /* Return list-tail of all connected clients */
1210
1211 struct winbindd_cli_state *winbindd_client_list_tail(void)
1212 {
1213         return DLIST_TAIL(_client_list);
1214 }
1215
1216 /* Return previous (read:newer) client in list */
1217
1218 struct winbindd_cli_state *
1219 winbindd_client_list_prev(struct winbindd_cli_state *cli)
1220 {
1221         return DLIST_PREV(cli);
1222 }
1223
1224 /* Add a connection to the list */
1225
1226 void winbindd_add_client(struct winbindd_cli_state *cli)
1227 {
1228         DLIST_ADD(_client_list, cli);
1229         _num_clients++;
1230 }
1231
1232 /* Remove a client from the list */
1233
1234 void winbindd_remove_client(struct winbindd_cli_state *cli)
1235 {
1236         DLIST_REMOVE(_client_list, cli);
1237         _num_clients--;
1238 }
1239
1240 /* Move a client to head or list */
1241
1242 void winbindd_promote_client(struct winbindd_cli_state *cli)
1243 {
1244         DLIST_PROMOTE(_client_list, cli);
1245 }
1246
1247 /* Return number of open clients */
1248
1249 int winbindd_num_clients(void)
1250 {
1251         return _num_clients;
1252 }
1253
1254 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1255                                   TALLOC_CTX *mem_ctx,
1256                                   const struct dom_sid *user_sid,
1257                                   uint32_t *p_num_groups, struct dom_sid **user_sids)
1258 {
1259         struct netr_SamInfo3 *info3 = NULL;
1260         NTSTATUS status = NT_STATUS_NO_MEMORY;
1261         uint32_t num_groups = 0;
1262
1263         DEBUG(3,(": lookup_usergroups_cached\n"));
1264
1265         *user_sids = NULL;
1266         *p_num_groups = 0;
1267
1268         info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1269
1270         if (info3 == NULL) {
1271                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1272         }
1273
1274         if (info3->base.groups.count == 0) {
1275                 TALLOC_FREE(info3);
1276                 return NT_STATUS_UNSUCCESSFUL;
1277         }
1278
1279         /*
1280          * Before bug #7843 the "Domain Local" groups were added with a
1281          * lookupuseraliases call, but this isn't done anymore for our domain
1282          * so we need to resolve resource groups here.
1283          *
1284          * When to use Resource Groups:
1285          * http://technet.microsoft.com/en-us/library/cc753670%28v=WS.10%29.aspx
1286          */
1287         status = sid_array_from_info3(mem_ctx, info3,
1288                                       user_sids,
1289                                       &num_groups,
1290                                       false);
1291
1292         if (!NT_STATUS_IS_OK(status)) {
1293                 TALLOC_FREE(info3);
1294                 return status;
1295         }
1296
1297         TALLOC_FREE(info3);
1298         *p_num_groups = num_groups;
1299         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1300
1301         DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1302
1303         return status;
1304 }
1305
1306 /*********************************************************************
1307  We use this to remove spaces from user and group names
1308 ********************************************************************/
1309
1310 NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
1311                              struct winbindd_domain *domain,
1312                              const char *name,
1313                              char **normalized)
1314 {
1315         NTSTATUS nt_status;
1316
1317         if (!name || !normalized) {
1318                 return NT_STATUS_INVALID_PARAMETER;
1319         }
1320
1321         if (!lp_winbind_normalize_names()) {
1322                 return NT_STATUS_PROCEDURE_NOT_FOUND;
1323         }
1324
1325         /* Alias support and whitespace replacement are mutually
1326            exclusive */
1327
1328         nt_status = resolve_username_to_alias(mem_ctx, domain,
1329                                               name, normalized );
1330         if (NT_STATUS_IS_OK(nt_status)) {
1331                 /* special return code to let the caller know we
1332                    mapped to an alias */
1333                 return NT_STATUS_FILE_RENAMED;
1334         }
1335
1336         /* check for an unreachable domain */
1337
1338         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1339                 DEBUG(5,("normalize_name_map: Setting domain %s offline\n",
1340                          domain->name));
1341                 set_domain_offline(domain);
1342                 return nt_status;
1343         }
1344
1345         /* deal with whitespace */
1346
1347         *normalized = talloc_strdup(mem_ctx, name);
1348         if (!(*normalized)) {
1349                 return NT_STATUS_NO_MEMORY;
1350         }
1351
1352         all_string_sub( *normalized, " ", "_", 0 );
1353
1354         return NT_STATUS_OK;
1355 }
1356
1357 /*********************************************************************
1358  We use this to do the inverse of normalize_name_map()
1359 ********************************************************************/
1360
1361 NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
1362                               char *name,
1363                               char **normalized)
1364 {
1365         NTSTATUS nt_status;
1366         struct winbindd_domain *domain = find_our_domain();
1367
1368         if (!name || !normalized) {
1369                 return NT_STATUS_INVALID_PARAMETER;
1370         }
1371
1372         if (!lp_winbind_normalize_names()) {
1373                 return NT_STATUS_PROCEDURE_NOT_FOUND;
1374         }
1375
1376         /* Alias support and whitespace replacement are mutally
1377            exclusive */
1378
1379         /* When mapping from an alias to a username, we don't know the
1380            domain.  But we only need a domain structure to cache
1381            a successful lookup , so just our own domain structure for
1382            the seqnum. */
1383
1384         nt_status = resolve_alias_to_username(mem_ctx, domain,
1385                                               name, normalized);
1386         if (NT_STATUS_IS_OK(nt_status)) {
1387                 /* Special return code to let the caller know we mapped
1388                    from an alias */
1389                 return NT_STATUS_FILE_RENAMED;
1390         }
1391
1392         /* check for an unreachable domain */
1393
1394         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1395                 DEBUG(5,("normalize_name_unmap: Setting domain %s offline\n",
1396                          domain->name));
1397                 set_domain_offline(domain);
1398                 return nt_status;
1399         }
1400
1401         /* deal with whitespace */
1402
1403         *normalized = talloc_strdup(mem_ctx, name);
1404         if (!(*normalized)) {
1405                 return NT_STATUS_NO_MEMORY;
1406         }
1407
1408         all_string_sub(*normalized, "_", " ", 0);
1409
1410         return NT_STATUS_OK;
1411 }
1412
1413 /*********************************************************************
1414  ********************************************************************/
1415
1416 bool winbindd_can_contact_domain(struct winbindd_domain *domain)
1417 {
1418         struct winbindd_tdc_domain *tdc = NULL;
1419         TALLOC_CTX *frame = talloc_stackframe();
1420         bool ret = false;
1421
1422         /* We can contact the domain if it is our primary domain */
1423
1424         if (domain->primary) {
1425                 ret = true;
1426                 goto done;
1427         }
1428
1429         /* Trust the TDC cache and not the winbindd_domain flags */
1430
1431         if ((tdc = wcache_tdc_fetch_domain(frame, domain->name)) == NULL) {
1432                 DEBUG(10,("winbindd_can_contact_domain: %s not found in cache\n",
1433                           domain->name));
1434                 ret = false;
1435                 goto done;
1436         }
1437
1438         /* Can always contact a domain that is in out forest */
1439
1440         if (tdc->trust_flags & NETR_TRUST_FLAG_IN_FOREST) {
1441                 ret = true;
1442                 goto done;
1443         }
1444
1445         /*
1446          * On a _member_ server, we cannot contact the domain if it
1447          * is running AD and we have no inbound trust.
1448          */
1449
1450         if (!IS_DC &&
1451              domain->active_directory &&
1452             ((tdc->trust_flags & NETR_TRUST_FLAG_INBOUND) != NETR_TRUST_FLAG_INBOUND))
1453         {
1454                 DEBUG(10, ("winbindd_can_contact_domain: %s is an AD domain "
1455                            "and we have no inbound trust.\n", domain->name));
1456                 goto done;
1457         }
1458
1459         /* Assume everything else is ok (probably not true but what
1460            can you do?) */
1461
1462         ret = true;
1463
1464 done:
1465         talloc_destroy(frame);
1466
1467         return ret;
1468 }
1469
1470 /*********************************************************************
1471  ********************************************************************/
1472
1473 bool winbindd_internal_child(struct winbindd_child *child)
1474 {
1475         if ((child == idmap_child()) || (child == locator_child())) {
1476                 return True;
1477         }
1478
1479         return False;
1480 }
1481
1482 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1483
1484 /*********************************************************************
1485  ********************************************************************/
1486
1487 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1488 {
1489         char *var = NULL;
1490         char addr[INET6_ADDRSTRLEN];
1491         const char *kdc = NULL;
1492         int lvl = 11;
1493
1494         if (!domain || !domain->alt_name || !*domain->alt_name) {
1495                 return;
1496         }
1497
1498         if (domain->initialized && !domain->active_directory) {
1499                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1500                         domain->alt_name));
1501                 return;
1502         }
1503
1504         print_sockaddr(addr, sizeof(addr), &domain->dcaddr);
1505         kdc = addr;
1506         if (!*kdc) {
1507                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1508                         domain->alt_name));
1509                 kdc = domain->dcname;
1510         }
1511
1512         if (!kdc || !*kdc) {
1513                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1514                         domain->alt_name));
1515                 return;
1516         }
1517
1518         if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1519                                 domain->alt_name) == -1) {
1520                 return;
1521         }
1522
1523         DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1524                 var, kdc));
1525
1526         setenv(var, kdc, 1);
1527         free(var);
1528 }
1529
1530 /*********************************************************************
1531  ********************************************************************/
1532
1533 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1534 {
1535         struct winbindd_domain *our_dom = find_our_domain();
1536
1537         winbindd_set_locator_kdc_env(domain);
1538
1539         if (domain != our_dom) {
1540                 winbindd_set_locator_kdc_env(our_dom);
1541         }
1542 }
1543
1544 /*********************************************************************
1545  ********************************************************************/
1546
1547 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1548 {
1549         char *var = NULL;
1550
1551         if (!domain || !domain->alt_name || !*domain->alt_name) {
1552                 return;
1553         }
1554
1555         if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1556                                 domain->alt_name) == -1) {
1557                 return;
1558         }
1559
1560         unsetenv(var);
1561         free(var);
1562 }
1563 #else
1564
1565 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1566 {
1567         return;
1568 }
1569
1570 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1571 {
1572         return;
1573 }
1574
1575 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */
1576
1577 void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
1578 {
1579         resp->data.auth.nt_status = NT_STATUS_V(result);
1580         fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
1581
1582         /* we might have given a more useful error above */
1583         if (*resp->data.auth.error_string == '\0')
1584                 fstrcpy(resp->data.auth.error_string,
1585                         get_friendly_nt_error_msg(result));
1586         resp->data.auth.pam_error = nt_status_to_pam(result);
1587 }
1588
1589 bool is_domain_offline(const struct winbindd_domain *domain)
1590 {
1591         if (!lp_winbind_offline_logon()) {
1592                 return false;
1593         }
1594         if (get_global_winbindd_state_offline()) {
1595                 return true;
1596         }
1597         return !domain->online;
1598 }
1599
1600 bool is_domain_online(const struct winbindd_domain *domain)
1601 {
1602         return !is_domain_offline(domain);
1603 }
1604
1605 /**
1606  * Parse an char array into a list of sids.
1607  *
1608  * The input sidstr should consist of 0-terminated strings
1609  * representing sids, separated by newline characters '\n'.
1610  * The list is terminated by an empty string, i.e.
1611  * character '\0' directly following a character '\n'
1612  * (or '\0' right at the start of sidstr).
1613  */
1614 bool parse_sidlist(TALLOC_CTX *mem_ctx, const char *sidstr,
1615                    struct dom_sid **sids, uint32_t *num_sids)
1616 {
1617         const char *p;
1618
1619         p = sidstr;
1620         if (p == NULL)
1621                 return False;
1622
1623         while (p[0] != '\0') {
1624                 struct dom_sid sid;
1625                 const char *q = NULL;
1626
1627                 if (!dom_sid_parse_endp(p, &sid, &q)) {
1628                         DEBUG(1, ("Could not parse sid %s\n", p));
1629                         return false;
1630                 }
1631                 if ((q == NULL) || (q[0] != '\n')) {
1632                         DEBUG(1, ("Got invalid sidstr: %s\n", p));
1633                         return false;
1634                 }
1635                 if (!NT_STATUS_IS_OK(add_sid_to_array(mem_ctx, &sid, sids,
1636                                                       num_sids)))
1637                 {
1638                         return False;
1639                 }
1640                 p = q+1;
1641         }
1642         return True;
1643 }