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