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