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