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