Initial import
[samba] / source / nsswitch / winbindd_pam.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - pam auth funcions
5
6    Copyright (C) Andrew Tridgell 2000
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett 2001-2002
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30
31 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx, 
32                                     struct winbindd_cli_state *state, 
33                                     NET_USER_INFO_3 *info3) 
34 {
35         prs_struct ps;
36         uint32 size;
37         if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) {
38                 return NT_STATUS_NO_MEMORY;
39         }
40         if (!net_io_user_info3("", info3, &ps, 1, 3, False)) {
41                 prs_mem_free(&ps);
42                 return NT_STATUS_UNSUCCESSFUL;
43         }
44
45         size = prs_data_size(&ps);
46         state->response.extra_data = SMB_MALLOC(size);
47         if (!state->response.extra_data) {
48                 prs_mem_free(&ps);
49                 return NT_STATUS_NO_MEMORY;
50         }
51         memset( state->response.extra_data, '\0', size );
52         prs_copy_all_data_out(state->response.extra_data, &ps);
53         state->response.length += size;
54         prs_mem_free(&ps);
55         return NT_STATUS_OK;
56 }
57
58 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx, 
59                                      NET_USER_INFO_3 *info3,
60                                      const char *group_sid) 
61 {
62         DOM_SID require_membership_of_sid;
63         DOM_SID *all_sids;
64         size_t num_all_sids = (2 + info3->num_groups2 + info3->num_other_sids);
65         size_t i, j = 0;
66
67         /* Parse the 'required group' SID */
68         
69         if (!group_sid || !group_sid[0]) {
70                 /* NO sid supplied, all users may access */
71                 return NT_STATUS_OK;
72         }
73         
74         if (!string_to_sid(&require_membership_of_sid, group_sid)) {
75                 DEBUG(0, ("check_info3_in_group: could not parse %s as a SID!", 
76                           group_sid));
77
78                 return NT_STATUS_INVALID_PARAMETER;
79         }
80
81         all_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, num_all_sids);
82         if (!all_sids)
83                 return NT_STATUS_NO_MEMORY;
84
85         /* and create (by appending rids) the 'domain' sids */
86         
87         sid_copy(&all_sids[0], &(info3->dom_sid.sid));
88         
89         if (!sid_append_rid(&all_sids[0], info3->user_rid)) {
90                 DEBUG(3,("could not append user's primary RID 0x%x\n",
91                          info3->user_rid));                     
92                 
93                 return NT_STATUS_INVALID_PARAMETER;
94         }
95         j++;
96
97         sid_copy(&all_sids[1], &(info3->dom_sid.sid));
98                 
99         if (!sid_append_rid(&all_sids[1], info3->group_rid)) {
100                 DEBUG(3,("could not append additional group rid 0x%x\n",
101                          info3->group_rid));                    
102                 
103                 return NT_STATUS_INVALID_PARAMETER;
104         }
105         j++;    
106
107         for (i = 0; i < info3->num_groups2; i++) {
108         
109                 sid_copy(&all_sids[j], &(info3->dom_sid.sid));
110                 
111                 if (!sid_append_rid(&all_sids[j], info3->gids[i].g_rid)) {
112                         DEBUG(3,("could not append additional group rid 0x%x\n",
113                                 info3->gids[i].g_rid));                 
114                                 
115                         return NT_STATUS_INVALID_PARAMETER;
116                 }
117                 j++;
118         }
119
120         /* Copy 'other' sids.  We need to do sid filtering here to
121            prevent possible elevation of privileges.  See:
122
123            http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
124          */
125
126         for (i = 0; i < info3->num_other_sids; i++) {
127                 sid_copy(&all_sids[info3->num_groups2 + i + 2],
128                          &info3->other_sids[i].sid);
129                 j++;
130         }
131
132         for (i = 0; i < j; i++) {
133                 fstring sid1, sid2;
134                 DEBUG(10, ("User has SID: %s\n", 
135                            sid_to_string(sid1, &all_sids[i])));
136                 if (sid_equal(&require_membership_of_sid, &all_sids[i])) {
137                         DEBUG(10, ("SID %s matches %s - user permitted to authenticate!\n", 
138                                    sid_to_string(sid1, &require_membership_of_sid), sid_to_string(sid2, &all_sids[i])));
139                         return NT_STATUS_OK;
140                 }
141         }
142         
143         /* Do not distinguish this error from a wrong username/pw */
144
145         return NT_STATUS_LOGON_FAILURE;
146 }
147
148 static struct winbindd_domain *find_auth_domain(const char *domain_name)
149 {
150         struct winbindd_domain *domain;
151
152         if (IS_DC) {
153                 domain = find_domain_from_name_noinit(domain_name);
154                 if (domain == NULL) {
155                         DEBUG(3, ("Authentication for domain [%s] "
156                                   "as it is not a trusted domain\n", 
157                                   domain_name));
158                 }
159                 return domain;
160         }
161
162         if (is_myname(domain_name)) {
163                 DEBUG(3, ("Authentication for domain %s (local domain "
164                           "to this server) not supported at this "
165                           "stage\n", domain_name));
166                 return NULL;
167         }
168
169         return find_our_domain();
170 }
171
172 static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
173 {
174         resp->data.auth.nt_status = NT_STATUS_V(result);
175         fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
176
177         /* we might have given a more useful error above */
178         if (*resp->data.auth.error_string == '\0') 
179                 fstrcpy(resp->data.auth.error_string,
180                         get_friendly_nt_error_msg(result));
181         resp->data.auth.pam_error = nt_status_to_pam(result);
182 }
183
184 /**********************************************************************
185  Authenticate a user with a clear text password
186 **********************************************************************/
187
188 void winbindd_pam_auth(struct winbindd_cli_state *state)
189 {
190         struct winbindd_domain *domain;
191         fstring name_domain, name_user;
192
193         /* Ensure null termination */
194         state->request.data.auth.user
195                 [sizeof(state->request.data.auth.user)-1]='\0';
196
197         /* Ensure null termination */
198         state->request.data.auth.pass
199                 [sizeof(state->request.data.auth.pass)-1]='\0';
200
201         DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
202                   state->request.data.auth.user));
203
204         /* Parse domain and username */
205         
206         parse_domain_user(state->request.data.auth.user,
207                           name_domain, name_user);
208
209         domain = find_auth_domain(name_domain);
210
211         if (domain == NULL) {
212                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
213                 DEBUG(5, ("Plain text authentication for %s returned %s "
214                           "(PAM: %d)\n",
215                           state->request.data.auth.user, 
216                           state->response.data.auth.nt_status_string,
217                           state->response.data.auth.pam_error));
218                 request_error(state);
219                 return;
220         }
221
222         sendto_domain(state, domain);
223 }
224
225 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
226                                             struct winbindd_cli_state *state) 
227 {
228         NTSTATUS result;
229         fstring name_domain, name_user;
230         NET_USER_INFO_3 info3;
231         struct rpc_pipe_client *netlogon_pipe;
232         uchar chal[8];
233         DATA_BLOB lm_resp;
234         DATA_BLOB nt_resp;
235         int attempts = 0;
236         unsigned char local_lm_response[24];
237         unsigned char local_nt_response[24];
238         struct winbindd_domain *contact_domain;
239         BOOL retry;
240
241         /* Ensure null termination */
242         state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
243
244         /* Ensure null termination */
245         state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
246
247         DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
248                   state->request.data.auth.user));
249
250         /* Parse domain and username */
251         
252         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
253
254         /* do password magic */
255         
256
257         generate_random_buffer(chal, 8);
258         if (lp_client_ntlmv2_auth()) {
259                 DATA_BLOB server_chal;
260                 DATA_BLOB names_blob;
261                 DATA_BLOB nt_response;
262                 DATA_BLOB lm_response;
263                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8); 
264                 
265                 /* note that the 'workgroup' here is a best guess - we don't know
266                    the server's domain at this point.  The 'server name' is also
267                    dodgy... 
268                 */
269                 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
270                 
271                 if (!SMBNTLMv2encrypt(name_user, name_domain, 
272                                       state->request.data.auth.pass, 
273                                       &server_chal, 
274                                       &names_blob,
275                                       &lm_response, &nt_response, NULL)) {
276                         data_blob_free(&names_blob);
277                         data_blob_free(&server_chal);
278                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
279                         result = NT_STATUS_NO_MEMORY;
280                         goto done;
281                 }
282                 data_blob_free(&names_blob);
283                 data_blob_free(&server_chal);
284                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
285                                            lm_response.length);
286                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
287                                            nt_response.length);
288                 data_blob_free(&lm_response);
289                 data_blob_free(&nt_response);
290
291         } else {
292                 if (lp_client_lanman_auth() 
293                     && SMBencrypt(state->request.data.auth.pass, 
294                                   chal, 
295                                   local_lm_response)) {
296                         lm_resp = data_blob_talloc(state->mem_ctx, 
297                                                    local_lm_response, 
298                                                    sizeof(local_lm_response));
299                 } else {
300                         lm_resp = data_blob(NULL, 0);
301                 }
302                 SMBNTencrypt(state->request.data.auth.pass, 
303                              chal,
304                              local_nt_response);
305
306                 nt_resp = data_blob_talloc(state->mem_ctx, 
307                                            local_nt_response, 
308                                            sizeof(local_nt_response));
309         }
310         
311         /* what domain should we contact? */
312         
313         if ( IS_DC ) {
314                 if (!(contact_domain = find_domain_from_name(name_domain))) {
315                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
316                                   state->request.data.auth.user, name_domain, name_user, name_domain)); 
317                         result = NT_STATUS_NO_SUCH_USER;
318                         goto done;
319                 }
320                 
321         } else {
322                 if (is_myname(name_domain)) {
323                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
324                         result =  NT_STATUS_NO_SUCH_USER;
325                         goto done;
326                 }
327
328                 contact_domain = find_our_domain();
329         }
330
331         /* check authentication loop */
332
333         do {
334
335                 ZERO_STRUCT(info3);
336                 retry = False;
337
338                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
339
340                 if (!NT_STATUS_IS_OK(result)) {
341                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
342                         goto done;
343                 }
344
345                 result = rpccli_netlogon_sam_network_logon(netlogon_pipe,
346                                                            state->mem_ctx,
347                                                            0,
348                                                            contact_domain->dcname, /* server name */
349                                                            name_user,              /* user name */
350                                                            name_domain,            /* target domain */
351                                                            global_myname(),        /* workstation */
352                                                            chal,
353                                                            lm_resp,
354                                                            nt_resp,
355                                                            &info3);
356                 attempts += 1;
357
358                 /* We have to try a second time as cm_connect_netlogon
359                    might not yet have noticed that the DC has killed
360                    our connection. */
361
362                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
363                         retry = True;
364                         continue;
365                 }
366                 
367                 /* if we get access denied, a possible cause was that we had
368                    and open connection to the DC, but someone changed our
369                    machine account password out from underneath us using 'net
370                    rpc changetrustpw' */
371                    
372                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
373                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
374                                  "ACCESS_DENIED.  Maybe the trust account "
375                                 "password was changed and we didn't know it. "
376                                  "Killing connections to domain %s\n",
377                                 name_domain));
378                         invalidate_cm_connection(&contact_domain->conn);
379                         retry = True;
380                 } 
381                 
382         } while ( (attempts < 2) && retry );
383
384         if (NT_STATUS_IS_OK(result)) {
385                 netsamlogon_cache_store(name_user, &info3);
386                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
387
388                 /* Check if the user is in the right group */
389
390                 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3,
391                                         state->request.data.auth.require_membership_of_sid))) {
392                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
393                                   state->request.data.auth.user, 
394                                   state->request.data.auth.require_membership_of_sid));
395                 }
396         }
397
398 done:
399
400         /* give us a more useful (more correct?) error code */
401         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
402                                 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
403                 result = NT_STATUS_NO_LOGON_SERVERS;
404         }
405         
406         state->response.data.auth.nt_status = NT_STATUS_V(result);
407         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
408
409         /* we might have given a more useful error above */
410         if (!*state->response.data.auth.error_string) 
411                 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
412         state->response.data.auth.pam_error = nt_status_to_pam(result);
413
414         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n", 
415               state->request.data.auth.user, 
416               state->response.data.auth.nt_status_string,
417               state->response.data.auth.pam_error));          
418
419         if ( NT_STATUS_IS_OK(result) &&
420              (state->request.flags & WBFLAG_PAM_AFS_TOKEN) ) {
421
422                 char *afsname = talloc_strdup(state->mem_ctx,
423                                               lp_afs_username_map());
424                 char *cell;
425
426                 if (afsname == NULL) {
427                         goto no_token;
428                 }
429
430                 afsname = talloc_string_sub(state->mem_ctx,
431                                             lp_afs_username_map(),
432                                             "%D", name_domain);
433                 afsname = talloc_string_sub(state->mem_ctx, afsname,
434                                             "%u", name_user);
435                 afsname = talloc_string_sub(state->mem_ctx, afsname,
436                                             "%U", name_user);
437
438                 {
439                         DOM_SID user_sid;
440                         fstring sidstr;
441
442                         sid_copy(&user_sid, &info3.dom_sid.sid);
443                         sid_append_rid(&user_sid, info3.user_rid);
444                         sid_to_string(sidstr, &user_sid);
445                         afsname = talloc_string_sub(state->mem_ctx, afsname,
446                                                     "%s", sidstr);
447                 }
448
449                 if (afsname == NULL) {
450                         goto no_token;
451                 }
452
453                 strlower_m(afsname);
454
455                 DEBUG(10, ("Generating token for user %s\n", afsname));
456
457                 cell = strchr(afsname, '@');
458
459                 if (cell == NULL) {
460                         goto no_token;
461                 }
462
463                 *cell = '\0';
464                 cell += 1;
465
466                 /* Append an AFS token string */
467                 state->response.extra_data =
468                         afs_createtoken_str(afsname, cell);
469
470                 if (state->response.extra_data != NULL)
471                         state->response.length +=
472                                 strlen(state->response.extra_data)+1;
473
474         no_token:
475                 talloc_free(afsname);
476         }
477                 
478         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
479 }
480
481 /**********************************************************************
482  Challenge Response Authentication Protocol 
483 **********************************************************************/
484
485 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
486 {
487         struct winbindd_domain *domain = NULL;
488         const char *domain_name = NULL;
489         NTSTATUS result;
490
491         if (!state->privileged) {
492                 char *error_string = NULL;
493                 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
494                           "denied.  !\n"));
495                 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
496                              "on %s are set correctly.\n",
497                              get_winbind_priv_pipe_dir()));
498                 /* send a better message than ACCESS_DENIED */
499                 error_string = talloc_asprintf(state->mem_ctx,
500                                                "winbind client not authorized "
501                                                "to use winbindd_pam_auth_crap."
502                                                " Ensure permissions on %s "
503                                                "are set correctly.",
504                                                get_winbind_priv_pipe_dir());
505                 fstrcpy(state->response.data.auth.error_string, error_string);
506                 result = NT_STATUS_ACCESS_DENIED;
507                 goto done;
508         }
509
510         /* Ensure null termination */
511         state->request.data.auth_crap.user
512                 [sizeof(state->request.data.auth_crap.user)-1]=0;
513         state->request.data.auth_crap.domain
514                 [sizeof(state->request.data.auth_crap.domain)-1]=0;
515
516         DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
517                   (unsigned long)state->pid,
518                   state->request.data.auth_crap.domain,
519                   state->request.data.auth_crap.user));
520
521         if (*state->request.data.auth_crap.domain != '\0') {
522                 domain_name = state->request.data.auth_crap.domain;
523         } else if (lp_winbind_use_default_domain()) {
524                 domain_name = lp_workgroup();
525         }
526
527         if (domain_name != NULL)
528                 domain = find_auth_domain(domain_name);
529
530         if (domain != NULL) {
531                 sendto_domain(state, domain);
532                 return;
533         }
534
535         result = NT_STATUS_NO_SUCH_USER;
536
537  done:
538         set_auth_errors(&state->response, result);
539         DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
540                   state->request.data.auth_crap.domain,
541                   state->request.data.auth_crap.user, 
542                   state->response.data.auth.nt_status_string,
543                   state->response.data.auth.pam_error));
544         request_error(state);
545         return;
546 }
547
548
549 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
550                                                  struct winbindd_cli_state *state) 
551 {
552         NTSTATUS result;
553         NET_USER_INFO_3 info3;
554         struct rpc_pipe_client *netlogon_pipe;
555         const char *name_user = NULL;
556         const char *name_domain = NULL;
557         const char *workstation;
558         struct winbindd_domain *contact_domain;
559         int attempts = 0;
560         BOOL retry;
561
562         DATA_BLOB lm_resp, nt_resp;
563
564         /* This is child-only, so no check for privileged access is needed
565            anymore */
566
567         /* Ensure null termination */
568         state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
569         state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
570
571         name_user = state->request.data.auth_crap.user;
572
573         if (*state->request.data.auth_crap.domain) {
574                 name_domain = state->request.data.auth_crap.domain;
575         } else if (lp_winbind_use_default_domain()) {
576                 name_domain = lp_workgroup();
577         } else {
578                 DEBUG(5,("no domain specified with username (%s) - failing auth\n", 
579                          name_user));
580                 result = NT_STATUS_NO_SUCH_USER;
581                 goto done;
582         }
583
584         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
585                   name_domain, name_user));
586            
587         if (*state->request.data.auth_crap.workstation) {
588                 workstation = state->request.data.auth_crap.workstation;
589         } else {
590                 workstation = global_myname();
591         }
592
593         if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
594                 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
595                 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", 
596                           state->request.data.auth_crap.lm_resp_len, 
597                           state->request.data.auth_crap.nt_resp_len));
598                 result = NT_STATUS_INVALID_PARAMETER;
599                 goto done;
600         }
601
602         lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
603                                         state->request.data.auth_crap.lm_resp_len);
604         nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
605                                         state->request.data.auth_crap.nt_resp_len);
606
607         /* what domain should we contact? */
608         
609         if ( IS_DC ) {
610                 if (!(contact_domain = find_domain_from_name(name_domain))) {
611                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
612                                   state->request.data.auth_crap.user, name_domain, name_user, name_domain)); 
613                         result = NT_STATUS_NO_SUCH_USER;
614                         goto done;
615                 }
616         } else {
617                 if (is_myname(name_domain)) {
618                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
619                         result =  NT_STATUS_NO_SUCH_USER;
620                         goto done;
621                 }
622                 contact_domain = find_our_domain();
623         }
624
625         do {
626                 ZERO_STRUCT(info3);
627                 retry = False;
628
629                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
630
631                 if (!NT_STATUS_IS_OK(result)) {
632                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
633                                   nt_errstr(result)));
634                         goto done;
635                 }
636
637                 result = rpccli_netlogon_sam_network_logon(netlogon_pipe,
638                                                            state->mem_ctx,
639                                                            state->request.data.auth_crap.logon_parameters,
640                                                            contact_domain->dcname,
641                                                            name_user,
642                                                            name_domain, 
643                                                                         /* Bug #3248 - found by Stefan Burkei. */
644                                                            workstation, /* We carefully set this above so use it... */
645                                                            state->request.data.auth_crap.chal,
646                                                            lm_resp,
647                                                            nt_resp,
648                                                            &info3);
649
650                 attempts += 1;
651
652                 /* We have to try a second time as cm_connect_netlogon
653                    might not yet have noticed that the DC has killed
654                    our connection. */
655
656                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
657                         retry = True;
658                         continue;
659                 }
660
661                 /* if we get access denied, a possible cause was that we had and open
662                    connection to the DC, but someone changed our machine account password
663                    out from underneath us using 'net rpc changetrustpw' */
664                    
665                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
666                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
667                                  "ACCESS_DENIED.  Maybe the trust account "
668                                 "password was changed and we didn't know it. "
669                                  "Killing connections to domain %s\n",
670                                 name_domain));
671                         invalidate_cm_connection(&contact_domain->conn);
672                         retry = True;
673                 } 
674
675         } while ( (attempts < 2) && retry );
676
677         if (NT_STATUS_IS_OK(result)) {
678                 netsamlogon_cache_store(name_user, &info3);
679                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
680
681                 /* Check if the user is in the right group */
682
683                 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3,
684                                                         state->request.data.auth_crap.require_membership_of_sid))) {
685                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
686                                   state->request.data.auth_crap.user, 
687                                   state->request.data.auth_crap.require_membership_of_sid));
688                         goto done;
689                 }
690
691                 if (state->request.flags & WBFLAG_PAM_INFO3_NDR) {
692                         result = append_info3_as_ndr(state->mem_ctx, state, &info3);
693                 } else if (state->request.flags & WBFLAG_PAM_UNIX_NAME) {
694                         /* ntlm_auth should return the unix username, per 
695                            'winbind use default domain' settings and the like */
696
697                         fstring username_out;
698                         const char *nt_username, *nt_domain;
699                         if (!(nt_username = unistr2_tdup(state->mem_ctx, &(info3.uni_user_name)))) {
700                                 /* If the server didn't give us one, just use the one we sent them */
701                                 nt_username = name_user;
702                         }
703
704                         if (!(nt_domain = unistr2_tdup(state->mem_ctx, &(info3.uni_logon_dom)))) {
705                                 /* If the server didn't give us one, just use the one we sent them */
706                                 nt_domain = name_domain;
707                         }
708
709                         fill_domain_username(username_out, nt_domain, nt_username);
710
711                         DEBUG(5, ("Setting unix username to [%s]\n", username_out));
712
713                         state->response.extra_data = SMB_STRDUP(username_out);
714                         if (!state->response.extra_data) {
715                                 result = NT_STATUS_NO_MEMORY;
716                                 goto done;
717                         }
718                         state->response.length +=  strlen(state->response.extra_data)+1;
719                 }
720                 
721                 if (state->request.flags & WBFLAG_PAM_USER_SESSION_KEY) {
722                         memcpy(state->response.data.auth.user_session_key, info3.user_sess_key,
723                                         sizeof(state->response.data.auth.user_session_key) /* 16 */);
724                 }
725                 if (state->request.flags & WBFLAG_PAM_LMKEY) {
726                         memcpy(state->response.data.auth.first_8_lm_hash, info3.lm_sess_key,
727                                         sizeof(state->response.data.auth.first_8_lm_hash) /* 8 */);
728                 }
729         }
730
731 done:
732
733         /* give us a more useful (more correct?) error code */
734         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
735                                 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
736                 result = NT_STATUS_NO_LOGON_SERVERS;
737         }
738
739         if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
740                 result = nt_status_squash(result);
741         }
742
743         state->response.data.auth.nt_status = NT_STATUS_V(result);
744         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
745
746         /* we might have given a more useful error above */
747         if (!*state->response.data.auth.error_string) {
748                 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
749         }
750         state->response.data.auth.pam_error = nt_status_to_pam(result);
751
752         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
753               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n", 
754                name_domain,
755                name_user,
756                state->response.data.auth.nt_status_string,
757                state->response.data.auth.pam_error));         
758
759         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
760 }
761
762 /* Change a user password */
763
764 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
765 {
766         NTSTATUS result;
767         char *oldpass, *newpass;
768         fstring domain, user;
769         POLICY_HND dom_pol;
770         struct winbindd_domain *contact_domain;
771         struct rpc_pipe_client *cli;
772
773         DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
774                 state->request.data.chauthtok.user));
775
776         /* Setup crap */
777
778         parse_domain_user(state->request.data.chauthtok.user, domain, user);
779
780         if (!(contact_domain = find_domain_from_name(domain))) {
781                 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n", 
782                           state->request.data.chauthtok.user, domain, user, domain)); 
783                 result = NT_STATUS_NO_SUCH_USER;
784                 goto done;
785         }
786
787         /* Change password */
788
789         oldpass = state->request.data.chauthtok.oldpass;
790         newpass = state->request.data.chauthtok.newpass;
791
792         /* Get sam handle */
793
794         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
795                                 &dom_pol);
796         if (!NT_STATUS_IS_OK(result)) {
797                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
798                 goto done;
799         }
800
801         result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass,
802                                             oldpass);
803
804 done:    
805         state->response.data.auth.nt_status = NT_STATUS_V(result);
806         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
807         fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
808         state->response.data.auth.pam_error = nt_status_to_pam(result);
809
810         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
811               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", 
812                domain,
813                user,
814                state->response.data.auth.nt_status_string,
815                state->response.data.auth.pam_error));         
816
817         if (NT_STATUS_IS_OK(result))
818                 request_ok(state);
819         else
820                 request_error(state);
821 }