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