05bde613045482afe9d5e94501a8da6d58d08b26
[samba.git] / 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    Copyright (C) Guenther Deschner 2005
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
30
31 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx, 
32                                     struct winbindd_cli_state *state, 
33                                     NET_USER_INFO_3 *info3) 
34 {
35         fstring str_sid;
36
37         state->response.data.auth.info3.logon_time = 
38                 nt_time_to_unix(info3->logon_time);
39         state->response.data.auth.info3.logoff_time = 
40                 nt_time_to_unix(info3->logoff_time);
41         state->response.data.auth.info3.kickoff_time = 
42                 nt_time_to_unix(info3->kickoff_time);
43         state->response.data.auth.info3.pass_last_set_time = 
44                 nt_time_to_unix(info3->pass_last_set_time);
45         state->response.data.auth.info3.pass_can_change_time = 
46                 nt_time_to_unix(info3->pass_can_change_time);
47         state->response.data.auth.info3.pass_must_change_time = 
48                 nt_time_to_unix(info3->pass_must_change_time);
49
50         state->response.data.auth.info3.logon_count = info3->logon_count;
51         state->response.data.auth.info3.bad_pw_count = info3->bad_pw_count;
52
53         state->response.data.auth.info3.user_rid = info3->user_rid;
54         state->response.data.auth.info3.group_rid = info3->group_rid;
55         sid_to_string(str_sid, &(info3->dom_sid.sid));
56         fstrcpy(state->response.data.auth.info3.dom_sid, str_sid);
57
58         state->response.data.auth.info3.num_groups = info3->num_groups;
59         state->response.data.auth.info3.user_flgs = info3->user_flgs;
60
61         state->response.data.auth.info3.acct_flags = info3->acct_flags;
62         state->response.data.auth.info3.num_other_sids = info3->num_other_sids;
63
64         unistr2_to_ascii(state->response.data.auth.info3.user_name, 
65                 &info3->uni_user_name, -1);
66         unistr2_to_ascii(state->response.data.auth.info3.full_name, 
67                 &info3->uni_full_name, -1);
68         unistr2_to_ascii(state->response.data.auth.info3.logon_script, 
69                 &info3->uni_logon_script, -1);
70         unistr2_to_ascii(state->response.data.auth.info3.profile_path, 
71                 &info3->uni_profile_path, -1);
72         unistr2_to_ascii(state->response.data.auth.info3.home_dir, 
73                 &info3->uni_home_dir, -1);
74         unistr2_to_ascii(state->response.data.auth.info3.dir_drive, 
75                 &info3->uni_dir_drive, -1);
76
77         unistr2_to_ascii(state->response.data.auth.info3.logon_srv, 
78                 &info3->uni_logon_srv, -1);
79         unistr2_to_ascii(state->response.data.auth.info3.logon_dom, 
80                 &info3->uni_logon_dom, -1);
81
82         return NT_STATUS_OK;
83 }
84
85 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx, 
86                                     struct winbindd_cli_state *state, 
87                                     NET_USER_INFO_3 *info3) 
88 {
89         prs_struct ps;
90         uint32 size;
91         if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) {
92                 return NT_STATUS_NO_MEMORY;
93         }
94         if (!net_io_user_info3("", info3, &ps, 1, 3, False)) {
95                 prs_mem_free(&ps);
96                 return NT_STATUS_UNSUCCESSFUL;
97         }
98
99         size = prs_data_size(&ps);
100         SAFE_FREE(state->response.extra_data.data);
101         state->response.extra_data.data = SMB_MALLOC(size);
102         if (!state->response.extra_data.data) {
103                 prs_mem_free(&ps);
104                 return NT_STATUS_NO_MEMORY;
105         }
106         memset( state->response.extra_data.data, '\0', size );
107         prs_copy_all_data_out((char *)state->response.extra_data.data, &ps);
108         state->response.length += size;
109         prs_mem_free(&ps);
110         return NT_STATUS_OK;
111 }
112
113 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx, 
114                                      NET_USER_INFO_3 *info3,
115                                      const char *group_sid) 
116 /**
117  * Check whether a user belongs to a group or list of groups.
118  *
119  * @param mem_ctx talloc memory context.
120  * @param info3 user information, including group membership info.
121  * @param group_sid One or more groups , separated by commas.
122  *
123  * @return NT_STATUS_OK on success,
124  *    NT_STATUS_LOGON_FAILURE if the user does not belong,
125  *    or other NT_STATUS_IS_ERR(status) for other kinds of failure.
126  */
127 {
128         DOM_SID *require_membership_of_sid;
129         size_t num_require_membership_of_sid;
130         fstring req_sid;
131         const char *p;
132         DOM_SID sid;
133         size_t i;
134         struct nt_user_token *token;
135         NTSTATUS status;
136
137         /* Parse the 'required group' SID */
138         
139         if (!group_sid || !group_sid[0]) {
140                 /* NO sid supplied, all users may access */
141                 return NT_STATUS_OK;
142         }
143
144         if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
145                 DEBUG(0, ("talloc failed\n"));
146                 return NT_STATUS_NO_MEMORY;
147         }
148
149         num_require_membership_of_sid = 0;
150         require_membership_of_sid = NULL;
151
152         p = group_sid;
153
154         while (next_token(&p, req_sid, ",", sizeof(req_sid))) {
155                 if (!string_to_sid(&sid, req_sid)) {
156                         DEBUG(0, ("check_info3_in_group: could not parse %s "
157                                   "as a SID!", req_sid));
158                         return NT_STATUS_INVALID_PARAMETER;
159                 }
160
161                 if (!add_sid_to_array(mem_ctx, &sid,
162                                       &require_membership_of_sid,
163                                       &num_require_membership_of_sid)) {
164                         DEBUG(0, ("add_sid_to_array failed\n"));
165                         return NT_STATUS_NO_MEMORY;
166                 }
167         }
168
169         if (!sid_compose(&sid, &(info3->dom_sid.sid),
170                          info3->user_rid)
171             || !add_sid_to_array(mem_ctx, &sid,
172                                  &token->user_sids, &token->num_sids)) {
173                 DEBUG(3,("could not add user SID from rid 0x%x\n",
174                          info3->user_rid));                     
175                 return NT_STATUS_INVALID_PARAMETER;
176         }
177
178         if (!sid_compose(&sid, &(info3->dom_sid.sid),
179                          info3->group_rid)
180             || !add_sid_to_array(mem_ctx, &sid, 
181                                  &token->user_sids, &token->num_sids)) {
182                 DEBUG(3,("could not append additional group rid 0x%x\n",
183                          info3->group_rid));                    
184                 
185                 return NT_STATUS_INVALID_PARAMETER;
186         }
187
188         for (i = 0; i < info3->num_groups2; i++) {
189                 if (!sid_compose(&sid, &(info3->dom_sid.sid),
190                                  info3->gids[i].g_rid)
191                     || !add_sid_to_array(mem_ctx, &sid,
192                                          &token->user_sids, &token->num_sids)) {
193                         DEBUG(3,("could not append additional group rid 0x%x\n",
194                                  info3->gids[i].g_rid));        
195                         return NT_STATUS_INVALID_PARAMETER;
196                 }
197         }
198
199         /* Copy 'other' sids.  We need to do sid filtering here to
200            prevent possible elevation of privileges.  See:
201
202            http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
203          */
204
205         for (i = 0; i < info3->num_other_sids; i++) {
206                 if (!add_sid_to_array(mem_ctx, &info3->other_sids[i].sid,
207                                       &token->user_sids, &token->num_sids)) {
208                         DEBUG(3, ("could not add SID to array: %s\n",
209                                   sid_string_static(&info3->other_sids[i].sid)));
210                         return NT_STATUS_NO_MEMORY;
211                 }
212         }
213
214         if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
215                                                   token))
216             || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
217                                                      token))) {
218                 DEBUG(3, ("could not add aliases: %s\n",
219                           nt_errstr(status)));
220                 return status;
221         }
222
223         debug_nt_user_token(DBGC_CLASS, 10, token);
224
225         for (i=0; i<num_require_membership_of_sid; i++) {
226                 DEBUG(10, ("Checking SID %s\n", sid_string_static(
227                                    &require_membership_of_sid[i])));
228                 if (nt_token_check_sid(&require_membership_of_sid[i],
229                                        token)) {
230                         DEBUG(10, ("Access ok\n"));
231                         return NT_STATUS_OK;
232                 }
233         }
234         
235         /* Do not distinguish this error from a wrong username/pw */
236
237         return NT_STATUS_LOGON_FAILURE;
238 }
239
240 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state, 
241                                         const char *domain_name)
242 {
243         struct winbindd_domain *domain;
244
245         if (IS_DC) {
246                 domain = find_domain_from_name_noinit(domain_name);
247                 if (domain == NULL) {
248                         DEBUG(3, ("Authentication for domain [%s] refused"
249                                   "as it is not a trusted domain\n", 
250                                   domain_name));
251                 }
252                 return domain;
253         }
254
255         if (is_myname(domain_name)) {
256                 DEBUG(3, ("Authentication for domain %s (local domain "
257                           "to this server) not supported at this "
258                           "stage\n", domain_name));
259                 return NULL;
260         }
261
262         /* we can auth against trusted domains */
263         if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
264                 domain = find_domain_from_name_noinit(domain_name);
265                 if (domain == NULL) {
266                         DEBUG(3, ("Authentication for domain [%s] skipped " 
267                                   "as it is not a trusted domain\n", 
268                                   domain_name));
269                 } else {
270                         return domain;
271                 } 
272         }
273
274         return find_our_domain();
275 }
276
277 static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
278 {
279         resp->data.auth.nt_status = NT_STATUS_V(result);
280         fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
281
282         /* we might have given a more useful error above */
283         if (*resp->data.auth.error_string == '\0') 
284                 fstrcpy(resp->data.auth.error_string,
285                         get_friendly_nt_error_msg(result));
286         resp->data.auth.pam_error = nt_status_to_pam(result);
287 }
288
289 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
290                                        struct winbindd_cli_state *state)
291 {
292         struct winbindd_methods *methods;
293         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
294         SAM_UNK_INFO_1 password_policy;
295
296         if ( !winbindd_can_contact_domain( domain ) ) {
297                 DEBUG(5,("fillup_password_policy: No inbound trust to "
298                          "contact domain %s\n", domain->name));         
299                 return NT_STATUS_NOT_SUPPORTED;
300         }       
301
302         methods = domain->methods;
303
304         status = methods->password_policy(domain, state->mem_ctx, &password_policy);
305         if (NT_STATUS_IS_ERR(status)) {
306                 return status;
307         }
308
309         state->response.data.auth.policy.min_length_password =
310                 password_policy.min_length_password;
311         state->response.data.auth.policy.password_history =
312                 password_policy.password_history;
313         state->response.data.auth.policy.password_properties =
314                 password_policy.password_properties;
315         state->response.data.auth.policy.expire =
316                 nt_time_to_unix_abs(&(password_policy.expire));
317         state->response.data.auth.policy.min_passwordage = 
318                 nt_time_to_unix_abs(&(password_policy.min_passwordage));
319
320         return NT_STATUS_OK;
321 }
322
323 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain, 
324                                                          TALLOC_CTX *mem_ctx, 
325                                                          uint16 *max_allowed_bad_attempts)
326 {
327         struct winbindd_methods *methods;
328         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
329         SAM_UNK_INFO_12 lockout_policy;
330
331         *max_allowed_bad_attempts = 0;
332
333         methods = domain->methods;
334
335         status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
336         if (NT_STATUS_IS_ERR(status)) {
337                 return status;
338         }
339
340         *max_allowed_bad_attempts = lockout_policy.bad_attempt_lockout;
341
342         return NT_STATUS_OK;
343 }
344
345 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain, 
346                                    TALLOC_CTX *mem_ctx, 
347                                    uint32 *password_properties)
348 {
349         struct winbindd_methods *methods;
350         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
351         SAM_UNK_INFO_1 password_policy;
352
353         *password_properties = 0;
354
355         methods = domain->methods;
356
357         status = methods->password_policy(domain, mem_ctx, &password_policy);
358         if (NT_STATUS_IS_ERR(status)) {
359                 return status;
360         }
361
362         *password_properties = password_policy.password_properties;
363
364         return NT_STATUS_OK;
365 }
366
367 #ifdef HAVE_KRB5
368
369 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx, 
370                                         const char *type,
371                                         uid_t uid,
372                                         BOOL *internal_ccache)
373 {
374         /* accept FILE and WRFILE as krb5_cc_type from the client and then
375          * build the full ccname string based on the user's uid here -
376          * Guenther*/
377
378         const char *gen_cc = NULL;
379
380         *internal_ccache = True;
381
382         if (uid == -1) {
383                 goto memory_ccache;
384         }
385
386         if (!type || type[0] == '\0') {
387                 goto memory_ccache;
388         }
389
390         if (strequal(type, "FILE")) {
391                 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
392         } else if (strequal(type, "WRFILE")) {
393                 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
394         } else {
395                 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
396                 goto memory_ccache;
397         }
398
399         *internal_ccache = False;
400         goto done;
401
402   memory_ccache:
403         gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
404
405   done:
406         if (gen_cc == NULL) {
407                 DEBUG(0,("out of memory\n"));
408                 return NULL;
409         }
410
411         DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
412
413         return gen_cc;
414 }
415
416 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
417 {
418         const char *type = state->request.data.auth.krb5_cc_type;
419
420         state->response.data.auth.krb5ccname[0] = '\0';
421
422         if (type[0] == '\0') {
423                 return;
424         }
425
426         if (!strequal(type, "FILE") &&
427             !strequal(type, "WRFILE")) {
428                 DEBUG(10,("won't return krbccname for a %s type ccache\n", 
429                         type));
430                 return;
431         }
432         
433         fstrcpy(state->response.data.auth.krb5ccname, cc);
434 }
435
436 #endif
437
438 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
439 {
440         uid_t uid = -1;
441
442         uid = state->request.data.auth.uid;
443
444         if (uid < 0) {
445                 DEBUG(1,("invalid uid: '%d'\n", uid));
446                 return -1;
447         }
448         return uid;
449 }
450
451 /**********************************************************************
452  Authenticate a user with a clear text password using Kerberos and fill up
453  ccache if required
454  **********************************************************************/
455
456 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
457                                             struct winbindd_cli_state *state,
458                                             NET_USER_INFO_3 **info3)
459 {
460 #ifdef HAVE_KRB5
461         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
462         krb5_error_code krb5_ret;
463         DATA_BLOB tkt, session_key_krb5;
464         DATA_BLOB ap_rep, session_key;
465         PAC_DATA *pac_data = NULL;
466         PAC_LOGON_INFO *logon_info = NULL;
467         char *client_princ = NULL;
468         char *client_princ_out = NULL;
469         char *local_service = NULL;
470         const char *cc = NULL;
471         const char *principal_s = NULL;
472         const char *service = NULL;
473         char *realm = NULL;
474         fstring name_domain, name_user;
475         time_t ticket_lifetime = 0;
476         time_t renewal_until = 0;
477         uid_t uid = -1;
478         ADS_STRUCT *ads;
479         time_t time_offset = 0;
480         BOOL internal_ccache = True;
481
482         ZERO_STRUCT(session_key);
483         ZERO_STRUCT(session_key_krb5);
484         ZERO_STRUCT(tkt);
485         ZERO_STRUCT(ap_rep);
486
487         ZERO_STRUCTP(info3);
488
489         *info3 = NULL;
490         
491         /* 1st step: 
492          * prepare a krb5_cc_cache string for the user */
493
494         uid = get_uid_from_state(state);
495         if (uid == -1) {
496                 DEBUG(0,("no valid uid\n"));
497         }
498
499         cc = generate_krb5_ccache(state->mem_ctx,
500                                   state->request.data.auth.krb5_cc_type,
501                                   state->request.data.auth.uid, 
502                                   &internal_ccache);
503         if (cc == NULL) {
504                 return NT_STATUS_NO_MEMORY;
505         }
506
507
508         /* 2nd step: 
509          * get kerberos properties */
510         
511         if (domain->private_data) {
512                 ads = (ADS_STRUCT *)domain->private_data;
513                 time_offset = ads->auth.time_offset; 
514         }
515
516
517         /* 3rd step: 
518          * do kerberos auth and setup ccache as the user */
519
520         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
521
522         realm = domain->alt_name;
523         strupper_m(realm);
524         
525         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm); 
526         if (principal_s == NULL) {
527                 return NT_STATUS_NO_MEMORY;
528         }
529
530         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
531         if (service == NULL) {
532                 return NT_STATUS_NO_MEMORY;
533         }
534
535         /* if this is a user ccache, we need to act as the user to let the krb5
536          * library handle the chown, etc. */
537
538         /************************ NON-ROOT **********************/
539
540         if (!internal_ccache) {
541
542                 set_effective_uid(uid);
543                 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
544         }
545
546         krb5_ret = kerberos_kinit_password_ext(principal_s, 
547                                                state->request.data.auth.pass, 
548                                                time_offset, 
549                                                &ticket_lifetime,
550                                                &renewal_until,
551                                                cc, 
552                                                True,
553                                                True,
554                                                WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
555                                                &result);
556
557         if (krb5_ret) {
558                 DEBUG(1,("winbindd_raw_kerberos_login: kinit failed for '%s' with: %s (%d)\n", 
559                         principal_s, error_message(krb5_ret), krb5_ret));
560                 goto failed;
561         }
562
563         /* does http_timestring use heimdals libroken strftime?? - Guenther */
564         DEBUG(10,("got TGT for %s in %s (valid until: %s (%d), renewable till: %s (%d))\n", 
565                 principal_s, cc, 
566                 http_timestring(ticket_lifetime), (int)ticket_lifetime, 
567                 http_timestring(renewal_until), (int)renewal_until));
568
569         /* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set,
570          * in that case fallback to NTLM - gd */ 
571
572         if ((ticket_lifetime == 0) && (renewal_until == 0)) {
573                 result = NT_STATUS_INVALID_LOGON_TYPE;
574                 goto failed;
575         }
576
577         client_princ = talloc_strdup(state->mem_ctx, global_myname());
578         if (client_princ == NULL) {
579                 result = NT_STATUS_NO_MEMORY;
580                 goto failed;
581         }
582         strlower_m(client_princ);
583
584         local_service = talloc_asprintf(state->mem_ctx, "%s$@%s", client_princ, lp_realm());
585         if (local_service == NULL) {
586                 DEBUG(0,("winbindd_raw_kerberos_login: out of memory\n"));
587                 result = NT_STATUS_NO_MEMORY;
588                 goto failed;
589         }
590
591         krb5_ret = cli_krb5_get_ticket(local_service, 
592                                        time_offset, 
593                                        &tkt, 
594                                        &session_key_krb5, 
595                                        0, 
596                                        cc,
597                                        NULL);
598         if (krb5_ret) {
599                 DEBUG(1,("winbindd_raw_kerberos_login: failed to get ticket for %s: %s\n", 
600                         local_service, error_message(krb5_ret)));
601                 result = krb5_to_nt_status(krb5_ret);
602                 goto failed;
603         }
604
605         if (!internal_ccache) {
606                 gain_root_privilege();
607         }
608
609         /************************ NON-ROOT **********************/
610
611         result = ads_verify_ticket(state->mem_ctx, 
612                                    lp_realm(), 
613                                    time_offset,
614                                    &tkt, 
615                                    &client_princ_out, 
616                                    &pac_data, 
617                                    &ap_rep, 
618                                    &session_key, False);
619         if (!NT_STATUS_IS_OK(result)) {
620                 DEBUG(0,("winbindd_raw_kerberos_login: ads_verify_ticket failed: %s\n", 
621                         nt_errstr(result)));
622                 goto failed;
623         }
624
625         if (!pac_data) {
626                 DEBUG(3,("winbindd_raw_kerberos_login: no pac data\n"));
627                 result = NT_STATUS_INVALID_PARAMETER;
628                 goto failed;
629         }
630                         
631         logon_info = get_logon_info_from_pac(pac_data);
632         if (logon_info == NULL) {
633                 DEBUG(1,("winbindd_raw_kerberos_login: no logon info\n"));
634                 result = NT_STATUS_INVALID_PARAMETER;
635                 goto failed;
636         }
637
638         DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n", 
639                 local_service));
640
641
642         /* last step: 
643          * put results together */
644
645         *info3 = &logon_info->info3;
646
647         /* if we had a user's ccache then return that string for the pam
648          * environment */
649
650         if (!internal_ccache) {
651                 
652                 setup_return_cc_name(state, cc);
653
654                 result = add_ccache_to_list(principal_s,
655                                             cc,
656                                             service,
657                                             state->request.data.auth.user,
658                                             realm,
659                                             uid,
660                                             time(NULL),
661                                             ticket_lifetime,
662                                             renewal_until, 
663                                             False);
664
665                 if (!NT_STATUS_IS_OK(result)) {
666                         DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n", 
667                                 nt_errstr(result)));
668                 }
669         } else {
670
671                 /* need to delete the memory cred cache, it is not used anymore */
672
673                 krb5_ret = ads_kdestroy(cc);
674                 if (krb5_ret) {
675                         DEBUG(3,("winbindd_raw_kerberos_login: "
676                                  "could not destroy krb5 credential cache: "
677                                  "%s\n", error_message(krb5_ret)));
678                 }
679
680         }
681
682         result = NT_STATUS_OK;
683
684         goto done;
685
686 failed:
687
688         /* we could have created a new credential cache with a valid tgt in it
689          * but we werent able to get or verify the service ticket for this
690          * local host and therefor didn't get the PAC, we need to remove that
691          * cache entirely now */
692
693         krb5_ret = ads_kdestroy(cc);
694         if (krb5_ret) {
695                 DEBUG(3,("winbindd_raw_kerberos_login: "
696                          "could not destroy krb5 credential cache: "
697                          "%s\n", error_message(krb5_ret)));
698         }
699
700         if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
701                 DEBUG(3,("winbindd_raw_kerberos_login: "
702                           "could not remove ccache for user %s\n",
703                         state->request.data.auth.user));
704         }
705
706 done:
707         data_blob_free(&session_key);
708         data_blob_free(&session_key_krb5);
709         data_blob_free(&ap_rep);
710         data_blob_free(&tkt);
711
712         SAFE_FREE(client_princ_out);
713
714         if (!internal_ccache) {
715                 gain_root_privilege();
716         }
717
718         return result;
719 #else 
720         return NT_STATUS_NOT_SUPPORTED;
721 #endif /* HAVE_KRB5 */
722 }
723
724 void winbindd_pam_auth(struct winbindd_cli_state *state)
725 {
726         struct winbindd_domain *domain;
727         fstring name_domain, name_user;
728
729         /* Ensure null termination */
730         state->request.data.auth.user
731                 [sizeof(state->request.data.auth.user)-1]='\0';
732
733         /* Ensure null termination */
734         state->request.data.auth.pass
735                 [sizeof(state->request.data.auth.pass)-1]='\0';
736
737         DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
738                   state->request.data.auth.user));
739
740         /* Parse domain and username */
741         
742         ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
743
744         if (!canonicalize_username(state->request.data.auth.user,
745                                name_domain, name_user)) {
746                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
747                 DEBUG(5, ("Plain text authentication for %s returned %s "
748                           "(PAM: %d)\n",
749                           state->request.data.auth.user, 
750                           state->response.data.auth.nt_status_string,
751                           state->response.data.auth.pam_error));
752                 request_error(state);
753                 return;
754         }
755
756         domain = find_auth_domain(state, name_domain);
757
758         if (domain == NULL) {
759                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
760                 DEBUG(5, ("Plain text authentication for %s returned %s "
761                           "(PAM: %d)\n",
762                           state->request.data.auth.user, 
763                           state->response.data.auth.nt_status_string,
764                           state->response.data.auth.pam_error));
765                 request_error(state);
766                 return;
767         }
768
769         sendto_domain(state, domain);
770 }
771
772 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
773                                        struct winbindd_cli_state *state,
774                                        NET_USER_INFO_3 **info3)
775 {
776         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
777         uint16 max_allowed_bad_attempts; 
778         fstring name_domain, name_user;
779         DOM_SID sid;
780         enum lsa_SidType type;
781         uchar new_nt_pass[NT_HASH_LEN];
782         const uint8 *cached_nt_pass;
783         const uint8 *cached_salt;
784         NET_USER_INFO_3 *my_info3;
785         time_t kickoff_time, must_change_time;
786         BOOL password_good = False;
787 #ifdef HAVE_KRB5
788         struct winbindd_tdc_domain *tdc_domain = NULL;
789 #endif
790
791         *info3 = NULL;
792
793         ZERO_STRUCTP(info3);
794
795         DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
796
797         /* Parse domain and username */
798         
799         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
800
801
802         if (!lookup_cached_name(state->mem_ctx,
803                                 name_domain,
804                                 name_user,
805                                 &sid,
806                                 &type)) {
807                 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
808                 return NT_STATUS_NO_SUCH_USER;
809         }
810
811         if (type != SID_NAME_USER) {
812                 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
813                 return NT_STATUS_LOGON_FAILURE;
814         }
815
816         result = winbindd_get_creds(domain, 
817                                     state->mem_ctx, 
818                                     &sid, 
819                                     &my_info3, 
820                                     &cached_nt_pass,
821                                     &cached_salt);
822         if (!NT_STATUS_IS_OK(result)) {
823                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
824                 return result;
825         }
826
827         *info3 = my_info3;
828
829         E_md4hash(state->request.data.auth.pass, new_nt_pass);
830
831 #if DEBUG_PASSWORD
832         dump_data(100, new_nt_pass, NT_HASH_LEN);
833         dump_data(100, cached_nt_pass, NT_HASH_LEN);
834         if (cached_salt) {
835                 dump_data(100, cached_salt, NT_HASH_LEN);
836         }
837 #endif
838
839         if (cached_salt) {
840                 /* In this case we didn't store the nt_hash itself,
841                    but the MD5 combination of salt + nt_hash. */
842                 uchar salted_hash[NT_HASH_LEN];
843                 E_md5hash(cached_salt, new_nt_pass, salted_hash);
844
845                 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
846                         True : False;
847         } else {
848                 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
849                 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
850                         True : False;
851         }
852
853         if (password_good) {
854
855                 /* User *DOES* know the password, update logon_time and reset
856                  * bad_pw_count */
857         
858                 my_info3->user_flgs |= LOGON_CACHED_ACCOUNT;
859         
860                 if (my_info3->acct_flags & ACB_AUTOLOCK) {
861                         return NT_STATUS_ACCOUNT_LOCKED_OUT;
862                 }
863         
864                 if (my_info3->acct_flags & ACB_DISABLED) {
865                         return NT_STATUS_ACCOUNT_DISABLED;
866                 }
867         
868                 if (my_info3->acct_flags & ACB_WSTRUST) {
869                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
870                 }
871         
872                 if (my_info3->acct_flags & ACB_SVRTRUST) {
873                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
874                 }
875         
876                 if (my_info3->acct_flags & ACB_DOMTRUST) {
877                         return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
878                 }
879
880                 if (!(my_info3->acct_flags & ACB_NORMAL)) {
881                         DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n", 
882                                 my_info3->acct_flags));
883                         return NT_STATUS_LOGON_FAILURE;
884                 }
885
886                 kickoff_time = nt_time_to_unix(my_info3->kickoff_time);
887                 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
888                         return NT_STATUS_ACCOUNT_EXPIRED;
889                 }
890
891                 must_change_time = nt_time_to_unix(my_info3->pass_must_change_time);
892                 if (must_change_time != 0 && must_change_time < time(NULL)) {
893                         /* we allow grace logons when the password has expired */
894                         my_info3->user_flgs |= LOGON_GRACE_LOGON;
895                         /* return NT_STATUS_PASSWORD_EXPIRED; */
896                         goto success;
897                 }
898         
899 #ifdef HAVE_KRB5
900                 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
901                     ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
902                     (tdc_domain->trust_type & DS_DOMAIN_TRUST_TYPE_UPLEVEL)) {
903
904                         uid_t uid = -1;
905                         const char *cc = NULL;
906                         char *realm = NULL;
907                         const char *principal_s = NULL;
908                         const char *service = NULL;
909                         BOOL internal_ccache = False;
910
911                         uid = get_uid_from_state(state);
912                         if (uid == -1) {
913                                 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
914                                 return NT_STATUS_INVALID_PARAMETER;
915                         }
916
917                         cc = generate_krb5_ccache(state->mem_ctx,
918                                                 state->request.data.auth.krb5_cc_type,
919                                                 state->request.data.auth.uid,
920                                                 &internal_ccache);
921                         if (cc == NULL) {
922                                 return NT_STATUS_NO_MEMORY;
923                         }
924
925                         realm = domain->alt_name;
926                         strupper_m(realm);
927
928                         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
929                         if (principal_s == NULL) {
930                                 return NT_STATUS_NO_MEMORY;
931                         }
932
933                         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
934                         if (service == NULL) {
935                                 return NT_STATUS_NO_MEMORY;
936                         }
937
938                         if (!internal_ccache) {
939
940                                 setup_return_cc_name(state, cc);
941
942                                 result = add_ccache_to_list(principal_s,
943                                                             cc,
944                                                             service,
945                                                             state->request.data.auth.user,
946                                                             domain->alt_name,
947                                                             uid,
948                                                             time(NULL),
949                                                             time(NULL) + lp_winbind_cache_time(),
950                                                             time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
951                                                             True);
952
953                                 if (!NT_STATUS_IS_OK(result)) {
954                                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
955                                                 "to add ccache to list: %s\n",
956                                                 nt_errstr(result)));
957                                 }
958                         }
959                 }
960 #endif /* HAVE_KRB5 */
961  success:
962                 /* FIXME: we possibly should handle logon hours as well (does xp when
963                  * offline?) see auth/auth_sam.c:sam_account_ok for details */
964
965                 unix_to_nt_time(&my_info3->logon_time, time(NULL));
966                 my_info3->bad_pw_count = 0;
967
968                 result = winbindd_update_creds_by_info3(domain,
969                                                         state->mem_ctx,
970                                                         state->request.data.auth.user,
971                                                         state->request.data.auth.pass,
972                                                         my_info3);
973                 if (!NT_STATUS_IS_OK(result)) {
974                         DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
975                                 nt_errstr(result)));
976                         return result;
977                 }
978
979                 return NT_STATUS_OK;
980
981         }
982
983         /* User does *NOT* know the correct password, modify info3 accordingly */
984
985         /* failure of this is not critical */
986         result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
987         if (!NT_STATUS_IS_OK(result)) {
988                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
989                           "Won't be able to honour account lockout policies\n"));
990         }
991
992         /* increase counter */
993         my_info3->bad_pw_count++;
994
995         if (max_allowed_bad_attempts == 0) {
996                 goto failed;
997         }
998
999         /* lockout user */
1000         if (my_info3->bad_pw_count >= max_allowed_bad_attempts) {
1001
1002                 uint32 password_properties;
1003
1004                 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1005                 if (!NT_STATUS_IS_OK(result)) {
1006                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1007                 }
1008
1009                 if ((my_info3->user_rid != DOMAIN_USER_RID_ADMIN) || 
1010                     (password_properties & DOMAIN_LOCKOUT_ADMINS)) {
1011                         my_info3->acct_flags |= ACB_AUTOLOCK;
1012                 }
1013         }
1014
1015 failed:
1016         result = winbindd_update_creds_by_info3(domain,
1017                                                 state->mem_ctx,
1018                                                 state->request.data.auth.user,
1019                                                 NULL,
1020                                                 my_info3);
1021
1022         if (!NT_STATUS_IS_OK(result)) {
1023                 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n", 
1024                         nt_errstr(result)));
1025         }
1026
1027         return NT_STATUS_LOGON_FAILURE;
1028 }
1029
1030 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1031                                          struct winbindd_cli_state *state, 
1032                                          NET_USER_INFO_3 **info3)
1033 {
1034         struct winbindd_domain *contact_domain;
1035         fstring name_domain, name_user;
1036         NTSTATUS result;
1037
1038         DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1039         
1040         /* Parse domain and username */
1041         
1042         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1043
1044         /* what domain should we contact? */
1045         
1046         if ( IS_DC ) {
1047                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1048                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
1049                                   state->request.data.auth.user, name_domain, name_user, name_domain)); 
1050                         result = NT_STATUS_NO_SUCH_USER;
1051                         goto done;
1052                 }
1053                 
1054         } else {
1055                 if (is_myname(name_domain)) {
1056                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1057                         result =  NT_STATUS_NO_SUCH_USER;
1058                         goto done;
1059                 }
1060                 
1061                 contact_domain = find_domain_from_name(name_domain);
1062                 if (contact_domain == NULL) {
1063                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
1064                                   state->request.data.auth.user, name_domain, name_user, name_domain)); 
1065
1066                         contact_domain = find_our_domain();
1067                 }
1068         }
1069
1070         if (contact_domain->initialized && 
1071             contact_domain->active_directory) {
1072                 goto try_login;
1073         }
1074
1075         if (!contact_domain->initialized) {
1076                 init_dc_connection(contact_domain);
1077         }
1078
1079         if (!contact_domain->active_directory) {
1080                 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1081                 return NT_STATUS_INVALID_LOGON_TYPE;
1082         }
1083 try_login:
1084         result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1085 done:
1086         return result;
1087 }
1088
1089 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1090                                          struct winbindd_cli_state *state,
1091                                          NET_USER_INFO_3 **info3)
1092 {
1093
1094         struct rpc_pipe_client *netlogon_pipe;
1095         uchar chal[8];
1096         DATA_BLOB lm_resp;
1097         DATA_BLOB nt_resp;
1098         int attempts = 0;
1099         unsigned char local_lm_response[24];
1100         unsigned char local_nt_response[24];
1101         struct winbindd_domain *contact_domain;
1102         fstring name_domain, name_user;
1103         BOOL retry;
1104         NTSTATUS result;
1105         NET_USER_INFO_3 *my_info3;
1106
1107         ZERO_STRUCTP(info3);
1108
1109         *info3 = NULL;
1110
1111         my_info3 = TALLOC_ZERO_P(state->mem_ctx, NET_USER_INFO_3);
1112         if (my_info3 == NULL) {
1113                 return NT_STATUS_NO_MEMORY;
1114         }
1115
1116
1117         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1118         
1119         /* Parse domain and username */
1120         
1121         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1122
1123         /* do password magic */
1124         
1125
1126         generate_random_buffer(chal, 8);
1127         if (lp_client_ntlmv2_auth()) {
1128                 DATA_BLOB server_chal;
1129                 DATA_BLOB names_blob;
1130                 DATA_BLOB nt_response;
1131                 DATA_BLOB lm_response;
1132                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8); 
1133                 
1134                 /* note that the 'workgroup' here is a best guess - we don't know
1135                    the server's domain at this point.  The 'server name' is also
1136                    dodgy... 
1137                 */
1138                 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1139                 
1140                 if (!SMBNTLMv2encrypt(name_user, name_domain, 
1141                                       state->request.data.auth.pass, 
1142                                       &server_chal, 
1143                                       &names_blob,
1144                                       &lm_response, &nt_response, NULL)) {
1145                         data_blob_free(&names_blob);
1146                         data_blob_free(&server_chal);
1147                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1148                         result = NT_STATUS_NO_MEMORY;
1149                         goto done;
1150                 }
1151                 data_blob_free(&names_blob);
1152                 data_blob_free(&server_chal);
1153                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1154                                            lm_response.length);
1155                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1156                                            nt_response.length);
1157                 data_blob_free(&lm_response);
1158                 data_blob_free(&nt_response);
1159
1160         } else {
1161                 if (lp_client_lanman_auth() 
1162                     && SMBencrypt(state->request.data.auth.pass, 
1163                                   chal, 
1164                                   local_lm_response)) {
1165                         lm_resp = data_blob_talloc(state->mem_ctx, 
1166                                                    local_lm_response, 
1167                                                    sizeof(local_lm_response));
1168                 } else {
1169                         lm_resp = data_blob_null;
1170                 }
1171                 SMBNTencrypt(state->request.data.auth.pass, 
1172                              chal,
1173                              local_nt_response);
1174
1175                 nt_resp = data_blob_talloc(state->mem_ctx, 
1176                                            local_nt_response, 
1177                                            sizeof(local_nt_response));
1178         }
1179         
1180         /* what domain should we contact? */
1181         
1182         if ( IS_DC ) {
1183                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1184                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
1185                                   state->request.data.auth.user, name_domain, name_user, name_domain)); 
1186                         result = NT_STATUS_NO_SUCH_USER;
1187                         goto done;
1188                 }
1189                 
1190         } else {
1191                 if (is_myname(name_domain)) {
1192                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1193                         result =  NT_STATUS_NO_SUCH_USER;
1194                         goto done;
1195                 }
1196
1197                 contact_domain = find_our_domain();
1198         }
1199
1200         /* check authentication loop */
1201
1202         do {
1203                 NTSTATUS (*logon_fn)(struct rpc_pipe_client
1204                                      *cli, TALLOC_CTX *mem_ctx,
1205                                      uint32 logon_parameters,
1206                                      const char *server,
1207                                      const char *username,
1208                                      const char *domain,
1209                                      const char *workstation, 
1210                                      const uint8 chal[8], 
1211                                      DATA_BLOB lm_response,
1212                                      DATA_BLOB nt_response,
1213                                      NET_USER_INFO_3 *info3);
1214
1215                 ZERO_STRUCTP(my_info3);
1216                 retry = False;
1217
1218                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1219
1220                 if (!NT_STATUS_IS_OK(result)) {
1221                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1222                         goto done;
1223                 }
1224
1225                 logon_fn = contact_domain->can_do_samlogon_ex
1226                         ? rpccli_netlogon_sam_network_logon_ex
1227                         : rpccli_netlogon_sam_network_logon;
1228
1229                 result = logon_fn(netlogon_pipe,
1230                                                            state->mem_ctx,
1231                                                            0,
1232                                                            contact_domain->dcname, /* server name */
1233                                                            name_user,              /* user name */
1234                                                            name_domain,            /* target domain */
1235                                                            global_myname(),        /* workstation */
1236                                                            chal,
1237                                                            lm_resp,
1238                                                            nt_resp,
1239                                                            my_info3);
1240
1241                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1242                     && contact_domain->can_do_samlogon_ex) {
1243                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1244                                   "retrying with NetSamLogon\n"));
1245                         contact_domain->can_do_samlogon_ex = False;
1246                         retry = True;
1247                         continue;
1248                 }
1249                         
1250                 attempts += 1;
1251
1252                 /* We have to try a second time as cm_connect_netlogon
1253                    might not yet have noticed that the DC has killed
1254                    our connection. */
1255
1256                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1257                         retry = True;
1258                         continue;
1259                 }
1260                 
1261                 /* if we get access denied, a possible cause was that we had
1262                    and open connection to the DC, but someone changed our
1263                    machine account password out from underneath us using 'net
1264                    rpc changetrustpw' */
1265                    
1266                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1267                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1268                                  "ACCESS_DENIED.  Maybe the trust account "
1269                                 "password was changed and we didn't know it. "
1270                                  "Killing connections to domain %s\n",
1271                                 name_domain));
1272                         invalidate_cm_connection(&contact_domain->conn);
1273                         retry = True;
1274                 } 
1275                 
1276         } while ( (attempts < 2) && retry );
1277
1278         /* handle the case where a NT4 DC does not fill in the acct_flags in
1279          * the samlogon reply info3. When accurate info3 is required by the
1280          * caller, we look up the account flags ourselve - gd */
1281
1282         if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) && 
1283             (my_info3->acct_flags == 0) && NT_STATUS_IS_OK(result)) {
1284
1285                 struct rpc_pipe_client *samr_pipe;
1286                 POLICY_HND samr_domain_handle, user_pol;
1287                 SAM_USERINFO_CTR *user_ctr;
1288                 NTSTATUS status_tmp;
1289                 uint32 acct_flags;
1290
1291                 ZERO_STRUCT(user_ctr);
1292
1293                 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx, 
1294                                             &samr_pipe, &samr_domain_handle);
1295
1296                 if (!NT_STATUS_IS_OK(status_tmp)) {
1297                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n", 
1298                                 nt_errstr(status_tmp)));
1299                         goto done;
1300                 }
1301
1302                 status_tmp = rpccli_samr_open_user(samr_pipe, state->mem_ctx, 
1303                                                    &samr_domain_handle,
1304                                                    MAXIMUM_ALLOWED_ACCESS,
1305                                                    my_info3->user_rid, &user_pol);
1306
1307                 if (!NT_STATUS_IS_OK(status_tmp)) {
1308                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1309                                 nt_errstr(status_tmp)));
1310                         goto done;
1311                 }
1312
1313                 status_tmp = rpccli_samr_query_userinfo(samr_pipe, state->mem_ctx, 
1314                                                         &user_pol, 16, &user_ctr);
1315
1316                 if (!NT_STATUS_IS_OK(status_tmp)) {
1317                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1318                                 nt_errstr(status_tmp)));
1319                         rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol);
1320                         goto done;
1321                 }
1322
1323                 acct_flags = user_ctr->info.id16->acb_info;
1324
1325                 if (acct_flags == 0) {
1326                         rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol);
1327                         goto done;
1328                 }
1329
1330                 my_info3->acct_flags = acct_flags;
1331
1332                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1333
1334                 rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol);
1335         }
1336
1337         *info3 = my_info3;
1338 done:
1339         return result;
1340 }
1341
1342 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1343                                             struct winbindd_cli_state *state) 
1344 {
1345         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1346         NTSTATUS krb5_result = NT_STATUS_OK;    
1347         fstring name_domain, name_user;
1348         NET_USER_INFO_3 *info3 = NULL;
1349         
1350         /* Ensure null termination */
1351         state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1352
1353         /* Ensure null termination */
1354         state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1355
1356         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1357                   state->request.data.auth.user));
1358
1359         /* Parse domain and username */
1360         
1361         ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1362
1363         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1364
1365         if (domain->online == False) {
1366                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1367                 if (domain->startup) {
1368                         /* Logons are very important to users. If we're offline and
1369                            we get a request within the first 30 seconds of startup,
1370                            try very hard to find a DC and go online. */
1371
1372                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1373                                 "request in startup mode.\n", domain->name ));
1374
1375                         winbindd_flush_negative_conn_cache(domain);
1376                         result = init_dc_connection(domain);
1377                 }
1378         }
1379
1380         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1381
1382         /* Check for Kerberos authentication */
1383         if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1384         
1385                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1386                 /* save for later */
1387                 krb5_result = result;
1388                 
1389
1390                 if (NT_STATUS_IS_OK(result)) {
1391                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1392                         goto process_result;
1393                 } else {
1394                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1395                 }
1396
1397                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1398                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1399                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1400                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1401                         set_domain_offline( domain );
1402                         goto cached_logon;                      
1403                 }
1404
1405                 /* there are quite some NT_STATUS errors where there is no
1406                  * point in retrying with a samlogon, we explictly have to take
1407                  * care not to increase the bad logon counter on the DC */
1408
1409                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1410                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1411                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1412                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1413                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1414                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1415                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1416                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1417                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1418                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1419                         goto process_result;
1420                 }
1421                 
1422                 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1423                         DEBUG(3,("falling back to samlogon\n"));
1424                         goto sam_logon;
1425                 } else {
1426                         goto cached_logon;
1427                 }
1428         }
1429
1430 sam_logon:
1431         /* Check for Samlogon authentication */
1432         if (domain->online) {
1433                 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1434         
1435                 if (NT_STATUS_IS_OK(result)) {
1436                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1437                         /* add the Krb5 err if we have one */
1438                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1439                                 info3->user_flgs |= LOGON_KRB5_FAIL_CLOCK_SKEW;                         
1440                         }
1441                         goto process_result;
1442                 } 
1443
1444                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n", 
1445                           nt_errstr(result)));
1446
1447                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1448                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1449                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) 
1450                 {
1451                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1452                         set_domain_offline( domain );
1453                         goto cached_logon;                      
1454                 }
1455
1456                         if (domain->online) {
1457                                 /* We're still online - fail. */
1458                                 goto done;
1459                         }
1460         }
1461
1462 cached_logon:
1463         /* Check for Cached logons */
1464         if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) && 
1465             lp_winbind_offline_logon()) {
1466         
1467                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1468
1469                 if (NT_STATUS_IS_OK(result)) {
1470                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1471                         goto process_result;
1472                 } else {
1473                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1474                         goto done;
1475                 }
1476         }
1477
1478 process_result:
1479
1480         if (NT_STATUS_IS_OK(result)) {
1481         
1482                 DOM_SID user_sid;
1483
1484                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1485                    been initialized. */
1486                 if (!info3) {
1487                         result = NT_STATUS_INTERNAL_ERROR;
1488                         goto done;
1489                 }
1490
1491                 netsamlogon_cache_store(name_user, info3);
1492                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1493
1494                 /* save name_to_sid info as early as possible (only if
1495                    this is our primary domain so we don't invalidate
1496                    the cache entry by storing the seq_num for the wrong
1497                    domain). */
1498                 if ( domain->primary ) {                        
1499                         sid_compose(&user_sid, &info3->dom_sid.sid, 
1500                                     info3->user_rid);
1501                         cache_name2sid(domain, name_domain, name_user, 
1502                                        SID_NAME_USER, &user_sid);
1503                 }
1504                 
1505                 /* Check if the user is in the right group */
1506
1507                 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1508                                         state->request.data.auth.require_membership_of_sid))) {
1509                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1510                                   state->request.data.auth.user, 
1511                                   state->request.data.auth.require_membership_of_sid));
1512                         goto done;
1513                 }
1514
1515                 if (state->request.flags & WBFLAG_PAM_INFO3_NDR) {
1516                         result = append_info3_as_ndr(state->mem_ctx, state, info3);
1517                         if (!NT_STATUS_IS_OK(result)) {
1518                                 DEBUG(10,("Failed to append INFO3 (NDR): %s\n", nt_errstr(result)));
1519                                 goto done;
1520                         }
1521                 }
1522
1523                 if (state->request.flags & WBFLAG_PAM_INFO3_TEXT) {
1524                         result = append_info3_as_txt(state->mem_ctx, state, info3);
1525                         if (!NT_STATUS_IS_OK(result)) {
1526                                 DEBUG(10,("Failed to append INFO3 (TXT): %s\n", nt_errstr(result)));
1527                                 goto done;
1528                         }
1529
1530                 }
1531
1532                 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1533
1534                         /* Store in-memory creds for single-signon using ntlm_auth. */
1535                         result = winbindd_add_memory_creds(state->request.data.auth.user,
1536                                                         get_uid_from_state(state),
1537                                                         state->request.data.auth.pass);
1538
1539                         if (!NT_STATUS_IS_OK(result)) {
1540                                 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1541                                 goto done;
1542                         }
1543
1544                         if (lp_winbind_offline_logon()) {
1545                                 result = winbindd_store_creds(domain,
1546                                                       state->mem_ctx,
1547                                                       state->request.data.auth.user,
1548                                                       state->request.data.auth.pass,
1549                                                       info3, NULL);
1550                                 if (!NT_STATUS_IS_OK(result)) {
1551
1552                                         /* Release refcount. */
1553                                         winbindd_delete_memory_creds(state->request.data.auth.user);
1554
1555                                         DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1556                                         goto done;
1557                                 }
1558                         }
1559                 }
1560
1561                 result = fillup_password_policy(domain, state);
1562
1563                 if (!NT_STATUS_IS_OK(result) 
1564                     && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ) 
1565                 {
1566                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result)));
1567                         goto done;
1568                 }
1569
1570                 result = NT_STATUS_OK;          
1571
1572                 if (state->request.flags & WBFLAG_PAM_UNIX_NAME) {
1573                         /* We've been asked to return the unix username, per 
1574                            'winbind use default domain' settings and the like */
1575
1576                         fstring username_out;
1577                         const char *nt_username, *nt_domain;
1578
1579                         if (!(nt_username = unistr2_tdup(state->mem_ctx, &info3->uni_user_name))) {
1580                                 /* If the server didn't give us one, just use the one we sent them */
1581                                 nt_username = name_user;
1582                         }
1583
1584                         if (!(nt_domain = unistr2_tdup(state->mem_ctx, &info3->uni_logon_dom))) {
1585                                 /* If the server didn't give us one, just use the one we sent them */
1586                                 nt_domain = name_domain;
1587                         }
1588
1589                         fill_domain_username(username_out, nt_domain, nt_username, True);
1590
1591                         DEBUG(5, ("Setting unix username to [%s]\n", username_out));
1592
1593                         SAFE_FREE(state->response.extra_data.data);
1594                         state->response.extra_data.data = SMB_STRDUP(username_out);
1595                         if (!state->response.extra_data.data) {
1596                                 result = NT_STATUS_NO_MEMORY;
1597                                 goto done;
1598                         }
1599                         state->response.length +=
1600                                 strlen((const char *)state->response.extra_data.data)+1;
1601                 }
1602         }
1603  
1604
1605 done:
1606         /* give us a more useful (more correct?) error code */
1607         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1608             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1609                 result = NT_STATUS_NO_LOGON_SERVERS;
1610         }
1611         
1612         state->response.data.auth.nt_status = NT_STATUS_V(result);
1613         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1614
1615         /* we might have given a more useful error above */
1616         if (!*state->response.data.auth.error_string) 
1617                 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1618         state->response.data.auth.pam_error = nt_status_to_pam(result);
1619
1620         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n", 
1621               state->request.data.auth.user, 
1622               state->response.data.auth.nt_status_string,
1623               state->response.data.auth.pam_error));          
1624
1625         if ( NT_STATUS_IS_OK(result) && info3 &&
1626              (state->request.flags & WBFLAG_PAM_AFS_TOKEN) ) {
1627
1628                 char *afsname = talloc_strdup(state->mem_ctx,
1629                                               lp_afs_username_map());
1630                 char *cell;
1631
1632                 if (afsname == NULL) {
1633                         goto no_token;
1634                 }
1635
1636                 afsname = talloc_string_sub(state->mem_ctx,
1637                                             lp_afs_username_map(),
1638                                             "%D", name_domain);
1639                 afsname = talloc_string_sub(state->mem_ctx, afsname,
1640                                             "%u", name_user);
1641                 afsname = talloc_string_sub(state->mem_ctx, afsname,
1642                                             "%U", name_user);
1643
1644                 {
1645                         DOM_SID user_sid;
1646                         fstring sidstr;
1647
1648                         sid_copy(&user_sid, &info3->dom_sid.sid);
1649                         sid_append_rid(&user_sid, info3->user_rid);
1650                         sid_to_string(sidstr, &user_sid);
1651                         afsname = talloc_string_sub(state->mem_ctx, afsname,
1652                                                     "%s", sidstr);
1653                 }
1654
1655                 if (afsname == NULL) {
1656                         goto no_token;
1657                 }
1658
1659                 strlower_m(afsname);
1660
1661                 DEBUG(10, ("Generating token for user %s\n", afsname));
1662
1663                 cell = strchr(afsname, '@');
1664
1665                 if (cell == NULL) {
1666                         goto no_token;
1667                 }
1668
1669                 *cell = '\0';
1670                 cell += 1;
1671
1672                 /* Append an AFS token string */
1673                 SAFE_FREE(state->response.extra_data.data);
1674                 state->response.extra_data.data =
1675                         afs_createtoken_str(afsname, cell);
1676
1677                 if (state->response.extra_data.data != NULL) {
1678                         state->response.length +=
1679                                 strlen((const char *)state->response.extra_data.data)+1;
1680                 }
1681
1682         no_token:
1683                 TALLOC_FREE(afsname);
1684         }
1685         
1686         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1687 }
1688
1689
1690 /**********************************************************************
1691  Challenge Response Authentication Protocol 
1692 **********************************************************************/
1693
1694 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1695 {
1696         struct winbindd_domain *domain = NULL;
1697         const char *domain_name = NULL;
1698         NTSTATUS result;
1699
1700         if (!state->privileged) {
1701                 char *error_string = NULL;
1702                 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1703                           "denied.  !\n"));
1704                 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1705                              "on %s are set correctly.\n",
1706                              get_winbind_priv_pipe_dir()));
1707                 /* send a better message than ACCESS_DENIED */
1708                 error_string = talloc_asprintf(state->mem_ctx,
1709                                                "winbind client not authorized "
1710                                                "to use winbindd_pam_auth_crap."
1711                                                " Ensure permissions on %s "
1712                                                "are set correctly.",
1713                                                get_winbind_priv_pipe_dir());
1714                 fstrcpy(state->response.data.auth.error_string, error_string);
1715                 result = NT_STATUS_ACCESS_DENIED;
1716                 goto done;
1717         }
1718
1719         /* Ensure null termination */
1720         state->request.data.auth_crap.user
1721                 [sizeof(state->request.data.auth_crap.user)-1]=0;
1722         state->request.data.auth_crap.domain
1723                 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1724
1725         DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1726                   (unsigned long)state->pid,
1727                   state->request.data.auth_crap.domain,
1728                   state->request.data.auth_crap.user));
1729
1730         if (*state->request.data.auth_crap.domain != '\0') {
1731                 domain_name = state->request.data.auth_crap.domain;
1732         } else if (lp_winbind_use_default_domain()) {
1733                 domain_name = lp_workgroup();
1734         }
1735
1736         if (domain_name != NULL)
1737                 domain = find_auth_domain(state, domain_name);
1738
1739         if (domain != NULL) {
1740                 sendto_domain(state, domain);
1741                 return;
1742         }
1743
1744         result = NT_STATUS_NO_SUCH_USER;
1745
1746  done:
1747         set_auth_errors(&state->response, result);
1748         DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1749                   state->request.data.auth_crap.domain,
1750                   state->request.data.auth_crap.user, 
1751                   state->response.data.auth.nt_status_string,
1752                   state->response.data.auth.pam_error));
1753         request_error(state);
1754         return;
1755 }
1756
1757
1758 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1759                                                  struct winbindd_cli_state *state) 
1760 {
1761         NTSTATUS result;
1762         NET_USER_INFO_3 info3;
1763         struct rpc_pipe_client *netlogon_pipe;
1764         const char *name_user = NULL;
1765         const char *name_domain = NULL;
1766         const char *workstation;
1767         struct winbindd_domain *contact_domain;
1768         int attempts = 0;
1769         BOOL retry;
1770
1771         DATA_BLOB lm_resp, nt_resp;
1772
1773         /* This is child-only, so no check for privileged access is needed
1774            anymore */
1775
1776         /* Ensure null termination */
1777         state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1778         state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1779
1780         name_user = state->request.data.auth_crap.user;
1781
1782         if (*state->request.data.auth_crap.domain) {
1783                 name_domain = state->request.data.auth_crap.domain;
1784         } else if (lp_winbind_use_default_domain()) {
1785                 name_domain = lp_workgroup();
1786         } else {
1787                 DEBUG(5,("no domain specified with username (%s) - failing auth\n", 
1788                          name_user));
1789                 result = NT_STATUS_NO_SUCH_USER;
1790                 goto done;
1791         }
1792
1793         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1794                   name_domain, name_user));
1795            
1796         if (*state->request.data.auth_crap.workstation) {
1797                 workstation = state->request.data.auth_crap.workstation;
1798         } else {
1799                 workstation = global_myname();
1800         }
1801
1802         if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1803                 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1804                 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", 
1805                           state->request.data.auth_crap.lm_resp_len, 
1806                           state->request.data.auth_crap.nt_resp_len));
1807                 result = NT_STATUS_INVALID_PARAMETER;
1808                 goto done;
1809         }
1810
1811         lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1812                                         state->request.data.auth_crap.lm_resp_len);
1813         nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
1814                                         state->request.data.auth_crap.nt_resp_len);
1815
1816         /* what domain should we contact? */
1817         
1818         if ( IS_DC ) {
1819                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1820                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
1821                                   state->request.data.auth_crap.user, name_domain, name_user, name_domain)); 
1822                         result = NT_STATUS_NO_SUCH_USER;
1823                         goto done;
1824                 }
1825         } else {
1826                 if (is_myname(name_domain)) {
1827                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1828                         result =  NT_STATUS_NO_SUCH_USER;
1829                         goto done;
1830                 }
1831                 contact_domain = find_our_domain();
1832         }
1833
1834         do {
1835                 NTSTATUS (*logon_fn)(struct rpc_pipe_client
1836                                      *cli, TALLOC_CTX *mem_ctx,
1837                                      uint32 logon_parameters,
1838                                      const char *server,
1839                                      const char *username,
1840                                      const char *domain,
1841                                      const char *workstation, 
1842                                      const uint8 chal[8], 
1843                                      DATA_BLOB lm_response,
1844                                      DATA_BLOB nt_response,
1845                                      NET_USER_INFO_3 *info3);
1846
1847                 ZERO_STRUCT(info3);
1848                 retry = False;
1849
1850                 netlogon_pipe = NULL;
1851                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1852
1853                 if (!NT_STATUS_IS_OK(result)) {
1854                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1855                                   nt_errstr(result)));
1856                         goto done;
1857                 }
1858
1859                 logon_fn = contact_domain->can_do_samlogon_ex
1860                         ? rpccli_netlogon_sam_network_logon_ex
1861                         : rpccli_netlogon_sam_network_logon;
1862
1863                 result = logon_fn(netlogon_pipe,
1864                                                            state->mem_ctx,
1865                                                            state->request.data.auth_crap.logon_parameters,
1866                                                            contact_domain->dcname,
1867                                                            name_user,
1868                                                            name_domain, 
1869                                                                         /* Bug #3248 - found by Stefan Burkei. */
1870                                                            workstation, /* We carefully set this above so use it... */
1871                                                            state->request.data.auth_crap.chal,
1872                                                            lm_resp,
1873                                                            nt_resp,
1874                                                            &info3);
1875
1876                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1877                     && contact_domain->can_do_samlogon_ex) {
1878                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1879                                   "retrying with NetSamLogon\n"));
1880                         contact_domain->can_do_samlogon_ex = False;
1881                         retry = True;
1882                         continue;
1883                 }
1884                         
1885                 attempts += 1;
1886
1887                 /* We have to try a second time as cm_connect_netlogon
1888                    might not yet have noticed that the DC has killed
1889                    our connection. */
1890
1891                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1892                         retry = True;
1893                         continue;
1894                 }
1895
1896                 /* if we get access denied, a possible cause was that we had and open
1897                    connection to the DC, but someone changed our machine account password
1898                    out from underneath us using 'net rpc changetrustpw' */
1899                    
1900                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1901                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1902                                  "ACCESS_DENIED.  Maybe the trust account "
1903                                 "password was changed and we didn't know it. "
1904                                  "Killing connections to domain %s\n",
1905                                 name_domain));
1906                         invalidate_cm_connection(&contact_domain->conn);
1907                         retry = True;
1908                 } 
1909
1910         } while ( (attempts < 2) && retry );
1911
1912         if (NT_STATUS_IS_OK(result)) {
1913
1914                 netsamlogon_cache_store(name_user, &info3);
1915                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
1916
1917                 /* Check if the user is in the right group */
1918
1919                 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3,
1920                                                         state->request.data.auth_crap.require_membership_of_sid))) {
1921                         DEBUG(3, ("User %s is not in the required group (%s), so "
1922                                   "crap authentication is rejected\n",
1923                                   state->request.data.auth_crap.user, 
1924                                   state->request.data.auth_crap.require_membership_of_sid));
1925                         goto done;
1926                 }
1927
1928                 if (state->request.flags & WBFLAG_PAM_INFO3_NDR) {
1929                         result = append_info3_as_ndr(state->mem_ctx, state, &info3);
1930                 } else if (state->request.flags & WBFLAG_PAM_UNIX_NAME) {
1931                         /* ntlm_auth should return the unix username, per 
1932                            'winbind use default domain' settings and the like */
1933
1934                         fstring username_out;
1935                         const char *nt_username, *nt_domain;
1936                         if (!(nt_username = unistr2_tdup(state->mem_ctx, &(info3.uni_user_name)))) {
1937                                 /* If the server didn't give us one, just use the one we sent them */
1938                                 nt_username = name_user;
1939                         }
1940
1941                         if (!(nt_domain = unistr2_tdup(state->mem_ctx, &(info3.uni_logon_dom)))) {
1942                                 /* If the server didn't give us one, just use the one we sent them */
1943                                 nt_domain = name_domain;
1944                         }
1945
1946                         fill_domain_username(username_out, nt_domain, nt_username, True);
1947
1948                         DEBUG(5, ("Setting unix username to [%s]\n", username_out));
1949
1950                         SAFE_FREE(state->response.extra_data.data);
1951                         state->response.extra_data.data = SMB_STRDUP(username_out);
1952                         if (!state->response.extra_data.data) {
1953                                 result = NT_STATUS_NO_MEMORY;
1954                                 goto done;
1955                         }
1956                         state->response.length +=
1957                                 strlen((const char *)state->response.extra_data.data)+1;
1958                 }
1959                 
1960                 if (state->request.flags & WBFLAG_PAM_USER_SESSION_KEY) {
1961                         memcpy(state->response.data.auth.user_session_key, info3.user_sess_key,
1962                                         sizeof(state->response.data.auth.user_session_key) /* 16 */);
1963                 }
1964                 if (state->request.flags & WBFLAG_PAM_LMKEY) {
1965                         memcpy(state->response.data.auth.first_8_lm_hash, info3.lm_sess_key,
1966                                         sizeof(state->response.data.auth.first_8_lm_hash) /* 8 */);
1967                 }
1968         }
1969
1970 done:
1971
1972         /* give us a more useful (more correct?) error code */
1973         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1974             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1975                 result = NT_STATUS_NO_LOGON_SERVERS;
1976         }
1977
1978         if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1979                 result = nt_status_squash(result);
1980         }
1981
1982         state->response.data.auth.nt_status = NT_STATUS_V(result);
1983         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1984
1985         /* we might have given a more useful error above */
1986         if (!*state->response.data.auth.error_string) {
1987                 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1988         }
1989         state->response.data.auth.pam_error = nt_status_to_pam(result);
1990
1991         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
1992               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n", 
1993                name_domain,
1994                name_user,
1995                state->response.data.auth.nt_status_string,
1996                state->response.data.auth.pam_error));         
1997
1998         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1999 }
2000
2001 /* Change a user password */
2002
2003 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2004 {
2005         fstring domain, user;
2006         struct winbindd_domain *contact_domain;
2007
2008         DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2009                 state->request.data.chauthtok.user));
2010
2011         /* Setup crap */
2012
2013         ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
2014
2015         if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
2016                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2017                 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2018                           "(PAM: %d)\n",
2019                           state->request.data.auth.user, 
2020                           state->response.data.auth.nt_status_string,
2021                           state->response.data.auth.pam_error));
2022                 request_error(state);
2023                 return;
2024         }
2025
2026         contact_domain = find_domain_from_name(domain);
2027         if (!contact_domain) {
2028                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2029                 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n", 
2030                           state->request.data.chauthtok.user, domain, user, domain)); 
2031                 request_error(state);
2032                 return;
2033         }
2034
2035         sendto_domain(state, contact_domain);
2036 }
2037
2038 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2039                                                  struct winbindd_cli_state *state)
2040 {
2041         char *oldpass;
2042         char *newpass = NULL;
2043         POLICY_HND dom_pol;
2044         struct rpc_pipe_client *cli;
2045         BOOL got_info = False;
2046         SAM_UNK_INFO_1 info;
2047         SAMR_CHANGE_REJECT reject;
2048         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2049         fstring domain, user;
2050
2051         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2052                   state->request.data.auth.user));
2053
2054         if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2055                 goto done;
2056         }
2057
2058         /* Change password */
2059
2060         oldpass = state->request.data.chauthtok.oldpass;
2061         newpass = state->request.data.chauthtok.newpass;
2062
2063         /* Initialize reject reason */
2064         state->response.data.auth.reject_reason = Undefined;
2065
2066         /* Get sam handle */
2067
2068         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2069                                 &dom_pol);
2070         if (!NT_STATUS_IS_OK(result)) {
2071                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2072                 goto done;
2073         }
2074
2075         result = rpccli_samr_chgpasswd3(cli, state->mem_ctx, user, newpass, oldpass, &info, &reject);
2076
2077         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2078
2079         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2080                 state->response.data.auth.policy.min_length_password = 
2081                         info.min_length_password;
2082                 state->response.data.auth.policy.password_history = 
2083                         info.password_history;
2084                 state->response.data.auth.policy.password_properties = 
2085                         info.password_properties;
2086                 state->response.data.auth.policy.expire = 
2087                         nt_time_to_unix_abs(&info.expire);
2088                 state->response.data.auth.policy.min_passwordage = 
2089                         nt_time_to_unix_abs(&info.min_passwordage);
2090
2091                 state->response.data.auth.reject_reason = 
2092                         reject.reject_reason;
2093
2094                 got_info = True;
2095         }
2096
2097         /* only fallback when the chgpasswd3 call is not supported */
2098         if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2099                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2100                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2101
2102                 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n", 
2103                         nt_errstr(result)));
2104                 
2105                 result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass);
2106
2107                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2108                    Map to the same status code as Windows 2003. */
2109
2110                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2111                         result = NT_STATUS_PASSWORD_RESTRICTION;                        
2112                 }
2113         }
2114
2115 done: 
2116
2117         if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2118                 
2119                 /* Update the single sign-on memory creds. */
2120                 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2121                                                         newpass);
2122
2123                 if (!NT_STATUS_IS_OK(result)) {
2124                         DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2125                         goto process_result;
2126                 }
2127
2128                 if (lp_winbind_offline_logon()) {
2129                         result = winbindd_update_creds_by_name(contact_domain,
2130                                                          state->mem_ctx, user,
2131                                                          newpass);
2132                         if (!NT_STATUS_IS_OK(result)) {
2133                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2134                                 goto process_result;
2135                         }
2136                 }
2137         }               
2138
2139         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2140
2141                 NTSTATUS policy_ret;
2142                 
2143                 policy_ret = fillup_password_policy(contact_domain, state);
2144
2145                 /* failure of this is non critical, it will just provide no
2146                  * additional information to the client why the change has
2147                  * failed - Guenther */
2148
2149                 if (!NT_STATUS_IS_OK(policy_ret)) {
2150                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2151                         goto process_result;
2152                 }
2153         }
2154
2155 process_result:
2156
2157         state->response.data.auth.nt_status = NT_STATUS_V(result);
2158         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2159         fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2160         state->response.data.auth.pam_error = nt_status_to_pam(result);
2161
2162         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
2163               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", 
2164                domain,
2165                user,
2166                state->response.data.auth.nt_status_string,
2167                state->response.data.auth.pam_error));         
2168
2169         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2170 }
2171
2172 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2173 {
2174         struct winbindd_domain *domain;
2175         fstring name_domain, user;
2176         uid_t caller_uid = (uid_t)-1;
2177         uid_t request_uid = state->request.data.logoff.uid;
2178
2179         DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2180                 state->request.data.logoff.user));
2181
2182         /* Ensure null termination */
2183         state->request.data.logoff.user
2184                 [sizeof(state->request.data.logoff.user)-1]='\0';
2185
2186         state->request.data.logoff.krb5ccname
2187                 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2188
2189         if (request_uid == (gid_t)-1) {
2190                 goto failed;
2191         }
2192
2193         if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2194                 goto failed;
2195         }
2196
2197         if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2198                 goto failed;
2199         }
2200
2201         if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2202                 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n", 
2203                         strerror(errno)));
2204                 goto failed;
2205         }
2206
2207         switch (caller_uid) {
2208                 case -1:
2209                         goto failed;
2210                 case 0:
2211                         /* root must be able to logoff any user - gd */
2212                         state->request.data.logoff.uid = request_uid;
2213                         break;
2214                 default:
2215                         if (caller_uid != request_uid) {
2216                                 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2217                                 goto failed;
2218                         }
2219                         state->request.data.logoff.uid = caller_uid;
2220                         break;
2221         }
2222
2223         sendto_domain(state, domain);
2224         return;
2225
2226  failed:
2227         set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2228         DEBUG(5, ("Pam Logoff for %s returned %s "
2229                   "(PAM: %d)\n",
2230                   state->request.data.logoff.user,
2231                   state->response.data.auth.nt_status_string,
2232                   state->response.data.auth.pam_error));
2233         request_error(state);
2234         return;
2235 }
2236
2237 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2238                                               struct winbindd_cli_state *state) 
2239 {
2240         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2241
2242         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2243                 state->request.data.logoff.user));
2244
2245         if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2246                 result = NT_STATUS_OK;
2247                 goto process_result;
2248         }
2249
2250         if (state->request.data.logoff.krb5ccname[0] == '\0') {
2251                 result = NT_STATUS_OK;
2252                 goto process_result;
2253         }
2254
2255 #ifdef HAVE_KRB5
2256         
2257         if (state->request.data.logoff.uid < 0) {
2258                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2259                 goto process_result;
2260         }
2261
2262         /* what we need here is to find the corresponding krb5 ccache name *we*
2263          * created for a given username and destroy it */
2264
2265         if (!ccache_entry_exists(state->request.data.logoff.user)) {
2266                 result = NT_STATUS_OK;
2267                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2268                 goto process_result;
2269         }
2270
2271         if (!ccache_entry_identical(state->request.data.logoff.user, 
2272                                         state->request.data.logoff.uid,
2273                                         state->request.data.logoff.krb5ccname)) {
2274                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2275                 goto process_result;
2276         }
2277
2278         result = remove_ccache(state->request.data.logoff.user);
2279         if (!NT_STATUS_IS_OK(result)) {
2280                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2281                         nt_errstr(result)));
2282                 goto process_result;
2283         }
2284
2285 #else
2286         result = NT_STATUS_NOT_SUPPORTED;
2287 #endif
2288
2289 process_result:
2290
2291         winbindd_delete_memory_creds(state->request.data.logoff.user);
2292
2293         state->response.data.auth.nt_status = NT_STATUS_V(result);
2294         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2295         fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2296         state->response.data.auth.pam_error = nt_status_to_pam(result);
2297
2298         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2299 }
2300
2301 /* Change user password with auth crap*/
2302
2303 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2304 {
2305         struct winbindd_domain *domain = NULL;
2306         const char *domain_name = NULL;
2307
2308         /* Ensure null termination */
2309         state->request.data.chng_pswd_auth_crap.user[
2310                 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2311         state->request.data.chng_pswd_auth_crap.domain[
2312                 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2313         
2314         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2315                   (unsigned long)state->pid,
2316                   state->request.data.chng_pswd_auth_crap.domain,
2317                   state->request.data.chng_pswd_auth_crap.user));
2318         
2319         if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2320                 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2321         } else if (lp_winbind_use_default_domain()) {
2322                 domain_name = lp_workgroup();
2323         }
2324
2325         if (domain_name != NULL)
2326                 domain = find_domain_from_name(domain_name);
2327
2328         if (domain != NULL) {
2329                 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2330                           "%s\n", (unsigned long)state->pid,domain->name));
2331                 sendto_domain(state, domain);
2332                 return;
2333         }
2334
2335         set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2336         DEBUG(5, ("CRAP change password  for %s\\%s returned %s (PAM: %d)\n",
2337                   state->request.data.chng_pswd_auth_crap.domain,
2338                   state->request.data.chng_pswd_auth_crap.user, 
2339                   state->response.data.auth.nt_status_string,
2340                   state->response.data.auth.pam_error));
2341         request_error(state);
2342         return;
2343 }
2344
2345 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2346 {
2347         NTSTATUS result;
2348         DATA_BLOB new_nt_password;
2349         DATA_BLOB old_nt_hash_enc;
2350         DATA_BLOB new_lm_password;
2351         DATA_BLOB old_lm_hash_enc;
2352         fstring  domain,user;
2353         POLICY_HND dom_pol;
2354         struct winbindd_domain *contact_domain = domainSt;
2355         struct rpc_pipe_client *cli;
2356
2357         /* Ensure null termination */
2358         state->request.data.chng_pswd_auth_crap.user[
2359                 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2360         state->request.data.chng_pswd_auth_crap.domain[
2361                 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2362         *domain = 0;
2363         *user = 0;
2364         
2365         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2366                   (unsigned long)state->pid,
2367                   state->request.data.chng_pswd_auth_crap.domain,
2368                   state->request.data.chng_pswd_auth_crap.user));
2369
2370         if (lp_winbind_offline_logon()) {
2371                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2372                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2373                 result = NT_STATUS_ACCESS_DENIED;
2374                 goto done;
2375         }
2376
2377         if (*state->request.data.chng_pswd_auth_crap.domain) {
2378                 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2379         } else {
2380                 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2381                                   domain, user);
2382
2383                 if(!*domain) {
2384                         DEBUG(3,("no domain specified with username (%s) - "
2385                                  "failing auth\n",
2386                                  state->request.data.chng_pswd_auth_crap.user));
2387                         result = NT_STATUS_NO_SUCH_USER;
2388                         goto done;
2389                 }
2390         }
2391
2392         if (!*domain && lp_winbind_use_default_domain()) {
2393                 fstrcpy(domain,(char *)lp_workgroup());
2394         }
2395
2396         if(!*user) {
2397                 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2398         }
2399
2400         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2401                   (unsigned long)state->pid, domain, user));
2402         
2403         /* Change password */
2404         new_nt_password = data_blob_talloc(
2405                 state->mem_ctx,
2406                 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2407                 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2408
2409         old_nt_hash_enc = data_blob_talloc(
2410                 state->mem_ctx,
2411                 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2412                 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2413
2414         if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2415                 new_lm_password = data_blob_talloc(
2416                         state->mem_ctx,
2417                         state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2418                         state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2419
2420                 old_lm_hash_enc = data_blob_talloc(
2421                         state->mem_ctx,
2422                         state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2423                         state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2424         } else {
2425                 new_lm_password.length = 0;
2426                 old_lm_hash_enc.length = 0;
2427         }
2428
2429         /* Get sam handle */
2430
2431         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2432         if (!NT_STATUS_IS_OK(result)) {
2433                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2434                 goto done;
2435         }
2436
2437         result = rpccli_samr_chng_pswd_auth_crap(
2438                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2439                 new_lm_password, old_lm_hash_enc);
2440
2441  done:    
2442         state->response.data.auth.nt_status = NT_STATUS_V(result);
2443         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2444         fstrcpy(state->response.data.auth.error_string,
2445                 get_friendly_nt_error_msg(result));
2446         state->response.data.auth.pam_error = nt_status_to_pam(result);
2447
2448         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
2449               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", 
2450                domain, user,
2451                state->response.data.auth.nt_status_string,
2452                state->response.data.auth.pam_error));         
2453
2454         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2455 }