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