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