e1422e5cced124d0496ec375815b74e9e1e20f81
[kai/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 */
997
998         /* failure of this is not critical */
999         result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1000         if (!NT_STATUS_IS_OK(result)) {
1001                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1002                           "Won't be able to honour account lockout policies\n"));
1003         }
1004
1005         /* increase counter */
1006         my_info3->base.bad_password_count++;
1007
1008         if (max_allowed_bad_attempts == 0) {
1009                 goto failed;
1010         }
1011
1012         /* lockout user */
1013         if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1014
1015                 uint32 password_properties;
1016
1017                 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1018                 if (!NT_STATUS_IS_OK(result)) {
1019                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1020                 }
1021
1022                 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1023                     (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1024                         my_info3->base.acct_flags |= ACB_AUTOLOCK;
1025                 }
1026         }
1027
1028 failed:
1029         result = winbindd_update_creds_by_info3(domain,
1030                                                 state->request->data.auth.user,
1031                                                 NULL,
1032                                                 my_info3);
1033
1034         if (!NT_STATUS_IS_OK(result)) {
1035                 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1036                         nt_errstr(result)));
1037         }
1038
1039         return NT_STATUS_LOGON_FAILURE;
1040 }
1041
1042 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1043                                                 struct winbindd_cli_state *state,
1044                                                 struct netr_SamInfo3 **info3)
1045 {
1046         struct winbindd_domain *contact_domain;
1047         fstring name_domain, name_user;
1048         NTSTATUS result;
1049
1050         DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1051
1052         /* Parse domain and username */
1053
1054         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1055
1056         /* what domain should we contact? */
1057
1058         if ( IS_DC ) {
1059                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1060                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1061                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1062                         result = NT_STATUS_NO_SUCH_USER;
1063                         goto done;
1064                 }
1065
1066         } else {
1067                 if (is_myname(name_domain)) {
1068                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1069                         result =  NT_STATUS_NO_SUCH_USER;
1070                         goto done;
1071                 }
1072
1073                 contact_domain = find_domain_from_name(name_domain);
1074                 if (contact_domain == NULL) {
1075                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1076                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1077
1078                         contact_domain = find_our_domain();
1079                 }
1080         }
1081
1082         if (contact_domain->initialized &&
1083             contact_domain->active_directory) {
1084                 goto try_login;
1085         }
1086
1087         if (!contact_domain->initialized) {
1088                 init_dc_connection(contact_domain);
1089         }
1090
1091         if (!contact_domain->active_directory) {
1092                 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1093                 return NT_STATUS_INVALID_LOGON_TYPE;
1094         }
1095 try_login:
1096         result = winbindd_raw_kerberos_login(
1097                 state->mem_ctx, contact_domain,
1098                 state->request->data.auth.user,
1099                 state->request->data.auth.pass,
1100                 state->request->data.auth.krb5_cc_type,
1101                 get_uid_from_request(state->request),
1102                 info3, state->response->data.auth.krb5ccname);
1103 done:
1104         return result;
1105 }
1106
1107 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1108                                           const char *domain, const char *user,
1109                                           const DATA_BLOB *challenge,
1110                                           const DATA_BLOB *lm_resp,
1111                                           const DATA_BLOB *nt_resp,
1112                                           struct netr_SamInfo3 **pinfo3)
1113 {
1114         struct auth_usersupplied_info *user_info = NULL;
1115         NTSTATUS status;
1116
1117         status = make_user_info(&user_info, user, user, domain, domain,
1118                                 global_myname(), lm_resp, nt_resp, NULL, NULL,
1119                                 NULL, AUTH_PASSWORD_RESPONSE);
1120         if (!NT_STATUS_IS_OK(status)) {
1121                 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1122                 return status;
1123         }
1124
1125         /* We don't want any more mapping of the username */
1126         user_info->mapped_state = True;
1127
1128         status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1129                                           pinfo3);
1130         free_user_info(&user_info);
1131         DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1132                    user, nt_errstr(status)));
1133         return status;
1134 }
1135
1136 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1137                                             TALLOC_CTX *mem_ctx,
1138                                             uint32_t logon_parameters,
1139                                             const char *server,
1140                                             const char *username,
1141                                             const char *domainname,
1142                                             const char *workstation,
1143                                             const uint8_t chal[8],
1144                                             DATA_BLOB lm_response,
1145                                             DATA_BLOB nt_response,
1146                                             struct netr_SamInfo3 **info3)
1147 {
1148         int attempts = 0;
1149         bool retry = false;
1150         NTSTATUS result;
1151
1152         do {
1153                 struct rpc_pipe_client *netlogon_pipe;
1154                 const struct pipe_auth_data *auth;
1155                 uint32_t neg_flags = 0;
1156
1157                 ZERO_STRUCTP(info3);
1158                 retry = false;
1159
1160                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1161
1162                 if (!NT_STATUS_IS_OK(result)) {
1163                         DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1164                                   nt_errstr(result)));
1165                         return result;
1166                 }
1167                 auth = netlogon_pipe->auth;
1168                 if (netlogon_pipe->dc) {
1169                         neg_flags = netlogon_pipe->dc->negotiate_flags;
1170                 }
1171
1172                 /* It is really important to try SamLogonEx here,
1173                  * because in a clustered environment, we want to use
1174                  * one machine account from multiple physical
1175                  * computers.
1176                  *
1177                  * With a normal SamLogon call, we must keep the
1178                  * credentials chain updated and intact between all
1179                  * users of the machine account (which would imply
1180                  * cross-node communication for every NTLM logon).
1181                  *
1182                  * (The credentials chain is not per NETLOGON pipe
1183                  * connection, but globally on the server/client pair
1184                  * by machine name).
1185                  *
1186                  * When using SamLogonEx, the credentials are not
1187                  * supplied, but the session key is implied by the
1188                  * wrapping SamLogon context.
1189                  *
1190                  *  -- abartlet 21 April 2008
1191                  *
1192                  * It's also important to use NetlogonValidationSamInfo4 (6),
1193                  * because it relies on the rpc transport encryption
1194                  * and avoids using the global netlogon schannel
1195                  * session key to en/decrypt secret information
1196                  * like the user_session_key for network logons.
1197                  *
1198                  * [MS-APDS] 3.1.5.2 NTLM Network Logon
1199                  * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1200                  * NETLOGON_NEG_AUTHENTICATED_RPC set together
1201                  * are the indication that the server supports
1202                  * NetlogonValidationSamInfo4 (6). And it must only
1203                  * be used if "SealSecureChannel" is used.
1204                  *
1205                  * -- metze 4 February 2011
1206                  */
1207
1208                 if (auth == NULL) {
1209                         domain->can_do_validation6 = false;
1210                 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1211                         domain->can_do_validation6 = false;
1212                 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1213                         domain->can_do_validation6 = false;
1214                 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1215                         domain->can_do_validation6 = false;
1216                 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1217                         domain->can_do_validation6 = false;
1218                 }
1219
1220                 if (domain->can_do_samlogon_ex) {
1221                         result = rpccli_netlogon_sam_network_logon_ex(
1222                                         netlogon_pipe,
1223                                         mem_ctx,
1224                                         0,
1225                                         server,         /* server name */
1226                                         username,       /* user name */
1227                                         domainname,     /* target domain */
1228                                         workstation,    /* workstation */
1229                                         chal,
1230                                         domain->can_do_validation6 ? 6 : 3,
1231                                         lm_response,
1232                                         nt_response,
1233                                         info3);
1234                 } else {
1235                         result = rpccli_netlogon_sam_network_logon(
1236                                         netlogon_pipe,
1237                                         mem_ctx,
1238                                         0,
1239                                         server,         /* server name */
1240                                         username,       /* user name */
1241                                         domainname,     /* target domain */
1242                                         workstation,    /* workstation */
1243                                         chal,
1244                                         domain->can_do_validation6 ? 6 : 3,
1245                                         lm_response,
1246                                         nt_response,
1247                                         info3);
1248                 }
1249
1250                 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)
1251                     && domain->can_do_samlogon_ex) {
1252                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1253                                   "retrying with NetSamLogon\n"));
1254                         domain->can_do_samlogon_ex = false;
1255                         /*
1256                          * It's likely that the server also does not support
1257                          * validation level 6
1258                          */
1259                         domain->can_do_validation6 = false;
1260                         retry = true;
1261                         continue;
1262                 }
1263
1264                 if (domain->can_do_validation6 &&
1265                     (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1266                      NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1267                      NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1268                         DEBUG(3,("Got a DC that can not do validation level 6, "
1269                                   "retrying with level 3\n"));
1270                         domain->can_do_validation6 = false;
1271                         retry = true;
1272                         continue;
1273                 }
1274
1275                 /*
1276                  * we increment this after the "feature negotiation"
1277                  * for can_do_samlogon_ex and can_do_validation6
1278                  */
1279                 attempts += 1;
1280
1281                 /* We have to try a second time as cm_connect_netlogon
1282                    might not yet have noticed that the DC has killed
1283                    our connection. */
1284
1285                 if (!rpccli_is_connected(netlogon_pipe)) {
1286                         retry = true;
1287                         continue;
1288                 }
1289
1290                 /* if we get access denied, a possible cause was that we had
1291                    and open connection to the DC, but someone changed our
1292                    machine account password out from underneath us using 'net
1293                    rpc changetrustpw' */
1294
1295                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1296                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1297                                  "ACCESS_DENIED.  Maybe the trust account "
1298                                 "password was changed and we didn't know it. "
1299                                  "Killing connections to domain %s\n",
1300                                 domainname));
1301                         invalidate_cm_connection(&domain->conn);
1302                         retry = true;
1303                 }
1304
1305         } while ( (attempts < 2) && retry );
1306
1307         return result;
1308 }
1309
1310 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1311                                                 struct winbindd_domain *domain,
1312                                                 const char *user,
1313                                                 const char *pass,
1314                                                 uint32_t request_flags,
1315                                                 struct netr_SamInfo3 **info3)
1316 {
1317
1318         uchar chal[8];
1319         DATA_BLOB lm_resp;
1320         DATA_BLOB nt_resp;
1321         unsigned char local_nt_response[24];
1322         fstring name_domain, name_user;
1323         NTSTATUS result;
1324         struct netr_SamInfo3 *my_info3 = NULL;
1325
1326         *info3 = NULL;
1327
1328         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1329
1330         /* Parse domain and username */
1331
1332         parse_domain_user(user, name_domain, name_user);
1333
1334         /* do password magic */
1335
1336         generate_random_buffer(chal, sizeof(chal));
1337
1338         if (lp_client_ntlmv2_auth()) {
1339                 DATA_BLOB server_chal;
1340                 DATA_BLOB names_blob;
1341                 server_chal = data_blob_const(chal, 8);
1342
1343                 /* note that the 'workgroup' here is for the local
1344                    machine.  The 'server name' must match the
1345                    'workstation' passed to the actual SamLogon call.
1346                 */
1347                 names_blob = NTLMv2_generate_names_blob(
1348                         mem_ctx, global_myname(), lp_workgroup());
1349
1350                 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1351                                       pass,
1352                                       &server_chal,
1353                                       &names_blob,
1354                                       &lm_resp, &nt_resp, NULL, NULL)) {
1355                         data_blob_free(&names_blob);
1356                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1357                         result = NT_STATUS_NO_MEMORY;
1358                         goto done;
1359                 }
1360                 data_blob_free(&names_blob);
1361         } else {
1362                 lm_resp = data_blob_null;
1363                 SMBNTencrypt(pass, chal, local_nt_response);
1364
1365                 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1366                                            sizeof(local_nt_response));
1367         }
1368
1369         if (strequal(name_domain, get_global_sam_name())) {
1370                 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1371
1372                 result = winbindd_dual_auth_passdb(
1373                         mem_ctx, name_domain, name_user,
1374                         &chal_blob, &lm_resp, &nt_resp, info3);
1375                 goto done;
1376         }
1377
1378         /* check authentication loop */
1379
1380         result = winbind_samlogon_retry_loop(domain,
1381                                              mem_ctx,
1382                                              0,
1383                                              domain->dcname,
1384                                              name_user,
1385                                              name_domain,
1386                                              global_myname(),
1387                                              chal,
1388                                              lm_resp,
1389                                              nt_resp,
1390                                              &my_info3);
1391         if (!NT_STATUS_IS_OK(result)) {
1392                 goto done;
1393         }
1394
1395         /* handle the case where a NT4 DC does not fill in the acct_flags in
1396          * the samlogon reply info3. When accurate info3 is required by the
1397          * caller, we look up the account flags ourselve - gd */
1398
1399         if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1400             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1401
1402                 struct rpc_pipe_client *samr_pipe;
1403                 struct policy_handle samr_domain_handle, user_pol;
1404                 union samr_UserInfo *info = NULL;
1405                 NTSTATUS status_tmp, result_tmp;
1406                 uint32 acct_flags;
1407                 struct dcerpc_binding_handle *b;
1408
1409                 status_tmp = cm_connect_sam(domain, mem_ctx,
1410                                             &samr_pipe, &samr_domain_handle);
1411
1412                 if (!NT_STATUS_IS_OK(status_tmp)) {
1413                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1414                                 nt_errstr(status_tmp)));
1415                         goto done;
1416                 }
1417
1418                 b = samr_pipe->binding_handle;
1419
1420                 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1421                                                   &samr_domain_handle,
1422                                                   MAXIMUM_ALLOWED_ACCESS,
1423                                                   my_info3->base.rid,
1424                                                   &user_pol,
1425                                                   &result_tmp);
1426
1427                 if (!NT_STATUS_IS_OK(status_tmp)) {
1428                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1429                                 nt_errstr(status_tmp)));
1430                         goto done;
1431                 }
1432                 if (!NT_STATUS_IS_OK(result_tmp)) {
1433                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1434                                 nt_errstr(result_tmp)));
1435                         goto done;
1436                 }
1437
1438                 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1439                                                        &user_pol,
1440                                                        16,
1441                                                        &info,
1442                                                        &result_tmp);
1443
1444                 if (!NT_STATUS_IS_OK(status_tmp)) {
1445                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1446                                 nt_errstr(status_tmp)));
1447                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1448                         goto done;
1449                 }
1450                 if (!NT_STATUS_IS_OK(result_tmp)) {
1451                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1452                                 nt_errstr(result_tmp)));
1453                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1454                         goto done;
1455                 }
1456
1457                 acct_flags = info->info16.acct_flags;
1458
1459                 if (acct_flags == 0) {
1460                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1461                         goto done;
1462                 }
1463
1464                 my_info3->base.acct_flags = acct_flags;
1465
1466                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1467
1468                 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1469         }
1470
1471         *info3 = my_info3;
1472 done:
1473         return result;
1474 }
1475
1476 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1477                                             struct winbindd_cli_state *state)
1478 {
1479         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1480         NTSTATUS krb5_result = NT_STATUS_OK;
1481         fstring name_domain, name_user;
1482         char *mapped_user;
1483         fstring domain_user;
1484         struct netr_SamInfo3 *info3 = NULL;
1485         NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1486
1487         /* Ensure null termination */
1488         state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1489
1490         /* Ensure null termination */
1491         state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1492
1493         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1494                   state->request->data.auth.user));
1495
1496         /* Parse domain and username */
1497
1498         name_map_status = normalize_name_unmap(state->mem_ctx,
1499                                                state->request->data.auth.user,
1500                                                &mapped_user);
1501
1502         /* If the name normalization didnt' actually do anything,
1503            just use the original name */
1504
1505         if (!NT_STATUS_IS_OK(name_map_status) &&
1506             !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1507         {
1508                 mapped_user = state->request->data.auth.user;
1509         }
1510
1511         parse_domain_user(mapped_user, name_domain, name_user);
1512
1513         if ( mapped_user != state->request->data.auth.user ) {
1514                 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1515                         *lp_winbind_separator(),
1516                         name_user );
1517                 strlcpy( state->request->data.auth.user, domain_user,
1518                              sizeof(state->request->data.auth.user));
1519         }
1520
1521         if (!domain->online) {
1522                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1523                 if (domain->startup) {
1524                         /* Logons are very important to users. If we're offline and
1525                            we get a request within the first 30 seconds of startup,
1526                            try very hard to find a DC and go online. */
1527
1528                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1529                                 "request in startup mode.\n", domain->name ));
1530
1531                         winbindd_flush_negative_conn_cache(domain);
1532                         result = init_dc_connection(domain);
1533                 }
1534         }
1535
1536         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1537
1538         /* Check for Kerberos authentication */
1539         if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1540
1541                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1542                 /* save for later */
1543                 krb5_result = result;
1544
1545
1546                 if (NT_STATUS_IS_OK(result)) {
1547                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1548                         goto process_result;
1549                 } else {
1550                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1551                 }
1552
1553                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1554                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1555                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1556                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1557                         set_domain_offline( domain );
1558                         goto cached_logon;
1559                 }
1560
1561                 /* there are quite some NT_STATUS errors where there is no
1562                  * point in retrying with a samlogon, we explictly have to take
1563                  * care not to increase the bad logon counter on the DC */
1564
1565                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1566                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1567                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1568                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1569                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1570                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1571                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1572                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1573                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1574                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1575                         goto done;
1576                 }
1577
1578                 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1579                         DEBUG(3,("falling back to samlogon\n"));
1580                         goto sam_logon;
1581                 } else {
1582                         goto cached_logon;
1583                 }
1584         }
1585
1586 sam_logon:
1587         /* Check for Samlogon authentication */
1588         if (domain->online) {
1589                 result = winbindd_dual_pam_auth_samlogon(
1590                         state->mem_ctx, domain,
1591                         state->request->data.auth.user,
1592                         state->request->data.auth.pass,
1593                         state->request->flags,
1594                         &info3);
1595
1596                 if (NT_STATUS_IS_OK(result)) {
1597                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1598                         /* add the Krb5 err if we have one */
1599                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1600                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1601                         }
1602                         goto process_result;
1603                 }
1604
1605                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1606                           nt_errstr(result)));
1607
1608                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1609                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1610                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1611                 {
1612                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1613                         set_domain_offline( domain );
1614                         goto cached_logon;
1615                 }
1616
1617                         if (domain->online) {
1618                                 /* We're still online - fail. */
1619                                 goto done;
1620                         }
1621         }
1622
1623 cached_logon:
1624         /* Check for Cached logons */
1625         if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1626             lp_winbind_offline_logon()) {
1627
1628                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1629
1630                 if (NT_STATUS_IS_OK(result)) {
1631                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1632                         goto process_result;
1633                 } else {
1634                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1635                         goto done;
1636                 }
1637         }
1638
1639 process_result:
1640
1641         if (NT_STATUS_IS_OK(result)) {
1642
1643                 struct dom_sid user_sid;
1644
1645                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1646                    been initialized. */
1647                 if (!info3) {
1648                         result = NT_STATUS_INTERNAL_ERROR;
1649                         goto done;
1650                 }
1651
1652                 sid_compose(&user_sid, info3->base.domain_sid,
1653                             info3->base.rid);
1654
1655                 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1656                                            &user_sid);
1657                 netsamlogon_cache_store(name_user, info3);
1658
1659                 /* save name_to_sid info as early as possible (only if
1660                    this is our primary domain so we don't invalidate
1661                    the cache entry by storing the seq_num for the wrong
1662                    domain). */
1663                 if ( domain->primary ) {
1664                         cache_name2sid(domain, name_domain, name_user,
1665                                        SID_NAME_USER, &user_sid);
1666                 }
1667
1668                 /* Check if the user is in the right group */
1669
1670                 result = check_info3_in_group(
1671                         info3,
1672                         state->request->data.auth.require_membership_of_sid);
1673                 if (!NT_STATUS_IS_OK(result)) {
1674                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1675                                   state->request->data.auth.user,
1676                                   state->request->data.auth.require_membership_of_sid));
1677                         goto done;
1678                 }
1679
1680                 result = append_auth_data(state->mem_ctx, state->response,
1681                                           state->request->flags, info3,
1682                                           name_domain, name_user);
1683                 if (!NT_STATUS_IS_OK(result)) {
1684                         goto done;
1685                 }
1686
1687                 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1688                     && lp_winbind_offline_logon()) {
1689
1690                         result = winbindd_store_creds(domain,
1691                                                       state->request->data.auth.user,
1692                                                       state->request->data.auth.pass,
1693                                                       info3);
1694                 }
1695
1696                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1697                         struct winbindd_domain *our_domain = find_our_domain();
1698
1699                         /* This is not entirely correct I believe, but it is
1700                            consistent.  Only apply the password policy settings
1701                            too warn users for our own domain.  Cannot obtain these
1702                            from trusted DCs all the  time so don't do it at all.
1703                            -- jerry */
1704
1705                         result = NT_STATUS_NOT_SUPPORTED;
1706                         if (our_domain == domain ) {
1707                                 result = fillup_password_policy(
1708                                         our_domain, state->response);
1709                         }
1710
1711                         if (!NT_STATUS_IS_OK(result)
1712                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1713                         {
1714                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1715                                           domain->name, nt_errstr(result)));
1716                                 goto done;
1717                         }
1718                 }
1719
1720                 result = NT_STATUS_OK;
1721         }
1722
1723 done:
1724         /* give us a more useful (more correct?) error code */
1725         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1726             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1727                 result = NT_STATUS_NO_LOGON_SERVERS;
1728         }
1729
1730         set_auth_errors(state->response, result);
1731
1732         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1733               state->request->data.auth.user,
1734               state->response->data.auth.nt_status_string,
1735               state->response->data.auth.pam_error));
1736
1737         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1738 }
1739
1740 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1741                                                  struct winbindd_cli_state *state)
1742 {
1743         NTSTATUS result;
1744         struct netr_SamInfo3 *info3 = NULL;
1745         const char *name_user = NULL;
1746         const char *name_domain = NULL;
1747         const char *workstation;
1748
1749         DATA_BLOB lm_resp, nt_resp;
1750
1751         /* This is child-only, so no check for privileged access is needed
1752            anymore */
1753
1754         /* Ensure null termination */
1755         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1756         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1757
1758         name_user = state->request->data.auth_crap.user;
1759         name_domain = state->request->data.auth_crap.domain;
1760         workstation = state->request->data.auth_crap.workstation;
1761
1762         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1763                   name_domain, name_user));
1764
1765         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1766                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1767                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1768                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1769                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1770                                   state->request->data.auth_crap.lm_resp_len,
1771                                   state->request->data.auth_crap.nt_resp_len));
1772                         result = NT_STATUS_INVALID_PARAMETER;
1773                         goto done;
1774                 }
1775         }
1776
1777         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1778                                         state->request->data.auth_crap.lm_resp_len);
1779
1780         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1781                 nt_resp = data_blob_talloc(state->mem_ctx,
1782                                            state->request->extra_data.data,
1783                                            state->request->data.auth_crap.nt_resp_len);
1784         } else {
1785                 nt_resp = data_blob_talloc(state->mem_ctx,
1786                                            state->request->data.auth_crap.nt_resp,
1787                                            state->request->data.auth_crap.nt_resp_len);
1788         }
1789
1790         if (strequal(name_domain, get_global_sam_name())) {
1791                 DATA_BLOB chal_blob = data_blob_const(
1792                         state->request->data.auth_crap.chal,
1793                         sizeof(state->request->data.auth_crap.chal));
1794
1795                 result = winbindd_dual_auth_passdb(
1796                         state->mem_ctx, name_domain, name_user,
1797                         &chal_blob, &lm_resp, &nt_resp, &info3);
1798                 goto process_result;
1799         }
1800
1801         result = winbind_samlogon_retry_loop(domain,
1802                                              state->mem_ctx,
1803                                              state->request->data.auth_crap.logon_parameters,
1804                                              domain->dcname,
1805                                              name_user,
1806                                              name_domain,
1807                                              /* Bug #3248 - found by Stefan Burkei. */
1808                                              workstation, /* We carefully set this above so use it... */
1809                                              state->request->data.auth_crap.chal,
1810                                              lm_resp,
1811                                              nt_resp,
1812                                              &info3);
1813         if (!NT_STATUS_IS_OK(result)) {
1814                 goto done;
1815         }
1816
1817 process_result:
1818
1819         if (NT_STATUS_IS_OK(result)) {
1820                 struct dom_sid user_sid;
1821
1822                 sid_compose(&user_sid, info3->base.domain_sid,
1823                             info3->base.rid);
1824                 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1825                                            &user_sid);
1826                 netsamlogon_cache_store(name_user, info3);
1827
1828                 /* Check if the user is in the right group */
1829
1830                 result = check_info3_in_group(
1831                         info3,
1832                         state->request->data.auth_crap.require_membership_of_sid);
1833                 if (!NT_STATUS_IS_OK(result)) {
1834                         DEBUG(3, ("User %s is not in the required group (%s), so "
1835                                   "crap authentication is rejected\n",
1836                                   state->request->data.auth_crap.user,
1837                                   state->request->data.auth_crap.require_membership_of_sid));
1838                         goto done;
1839                 }
1840
1841                 result = append_auth_data(state->mem_ctx, state->response,
1842                                           state->request->flags, info3,
1843                                           name_domain, name_user);
1844                 if (!NT_STATUS_IS_OK(result)) {
1845                         goto done;
1846                 }
1847         }
1848
1849 done:
1850
1851         /* give us a more useful (more correct?) error code */
1852         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1853             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1854                 result = NT_STATUS_NO_LOGON_SERVERS;
1855         }
1856
1857         if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1858                 result = nt_status_squash(result);
1859         }
1860
1861         set_auth_errors(state->response, result);
1862
1863         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1864               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1865                name_domain,
1866                name_user,
1867                state->response->data.auth.nt_status_string,
1868                state->response->data.auth.pam_error));
1869
1870         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1871 }
1872
1873 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1874                                                  struct winbindd_cli_state *state)
1875 {
1876         char *oldpass;
1877         char *newpass = NULL;
1878         struct policy_handle dom_pol;
1879         struct rpc_pipe_client *cli = NULL;
1880         bool got_info = false;
1881         struct samr_DomInfo1 *info = NULL;
1882         struct userPwdChangeFailureInformation *reject = NULL;
1883         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1884         fstring domain, user;
1885         struct dcerpc_binding_handle *b = NULL;
1886
1887         ZERO_STRUCT(dom_pol);
1888
1889         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1890                   state->request->data.auth.user));
1891
1892         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1893                 goto done;
1894         }
1895
1896         /* Change password */
1897
1898         oldpass = state->request->data.chauthtok.oldpass;
1899         newpass = state->request->data.chauthtok.newpass;
1900
1901         /* Initialize reject reason */
1902         state->response->data.auth.reject_reason = Undefined;
1903
1904         /* Get sam handle */
1905
1906         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1907                                 &dom_pol);
1908         if (!NT_STATUS_IS_OK(result)) {
1909                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1910                 goto done;
1911         }
1912
1913         b = cli->binding_handle;
1914
1915         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1916                                              user,
1917                                              newpass,
1918                                              oldpass,
1919                                              &info,
1920                                              &reject);
1921
1922         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1923
1924         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1925
1926                 fill_in_password_policy(state->response, info);
1927
1928                 state->response->data.auth.reject_reason =
1929                         reject->extendedFailureReason;
1930
1931                 got_info = true;
1932         }
1933
1934         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1935          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1936          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1937          * short to comply with the samr_ChangePasswordUser3 idl - gd */
1938
1939         /* only fallback when the chgpasswd_user3 call is not supported */
1940         if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
1941             NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
1942             NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
1943             NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1944
1945                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1946                         nt_errstr(result)));
1947
1948                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1949
1950                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1951                    Map to the same status code as Windows 2003. */
1952
1953                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1954                         result = NT_STATUS_PASSWORD_RESTRICTION;
1955                 }
1956         }
1957
1958 done:
1959
1960         if (NT_STATUS_IS_OK(result)
1961             && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1962             && lp_winbind_offline_logon()) {
1963                 result = winbindd_update_creds_by_name(contact_domain, user,
1964                                                        newpass);
1965                 /* Again, this happens when we login from gdm or xdm
1966                  * and the password expires, *BUT* cached crendentials
1967                  * doesn't exist. winbindd_update_creds_by_name()
1968                  * returns NT_STATUS_NO_SUCH_USER.
1969                  * This is not a failure.
1970                  * --- BoYang
1971                  * */
1972                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1973                         result = NT_STATUS_OK;
1974                 }
1975
1976                 if (!NT_STATUS_IS_OK(result)) {
1977                         DEBUG(10, ("Failed to store creds: %s\n",
1978                                    nt_errstr(result)));
1979                         goto process_result;
1980                 }
1981         }
1982
1983         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1984
1985                 NTSTATUS policy_ret;
1986
1987                 policy_ret = fillup_password_policy(
1988                         contact_domain, state->response);
1989
1990                 /* failure of this is non critical, it will just provide no
1991                  * additional information to the client why the change has
1992                  * failed - Guenther */
1993
1994                 if (!NT_STATUS_IS_OK(policy_ret)) {
1995                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1996                         goto process_result;
1997                 }
1998         }
1999
2000 process_result:
2001
2002         if (strequal(contact_domain->name, get_global_sam_name())) {
2003                 /* FIXME: internal rpc pipe does not cache handles yet */
2004                 if (b) {
2005                         if (is_valid_policy_hnd(&dom_pol)) {
2006                                 NTSTATUS _result;
2007                                 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2008                         }
2009                         TALLOC_FREE(cli);
2010                 }
2011         }
2012
2013         set_auth_errors(state->response, result);
2014
2015         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2016               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2017                domain,
2018                user,
2019                state->response->data.auth.nt_status_string,
2020                state->response->data.auth.pam_error));
2021
2022         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2023 }
2024
2025 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2026                                               struct winbindd_cli_state *state)
2027 {
2028         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2029
2030         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2031                 state->request->data.logoff.user));
2032
2033         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2034                 result = NT_STATUS_OK;
2035                 goto process_result;
2036         }
2037
2038         if (state->request->data.logoff.krb5ccname[0] == '\0') {
2039                 result = NT_STATUS_OK;
2040                 goto process_result;
2041         }
2042
2043 #ifdef HAVE_KRB5
2044
2045         if (state->request->data.logoff.uid < 0) {
2046                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2047                 goto process_result;
2048         }
2049
2050         /* what we need here is to find the corresponding krb5 ccache name *we*
2051          * created for a given username and destroy it */
2052
2053         if (!ccache_entry_exists(state->request->data.logoff.user)) {
2054                 result = NT_STATUS_OK;
2055                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2056                 goto process_result;
2057         }
2058
2059         if (!ccache_entry_identical(state->request->data.logoff.user,
2060                                         state->request->data.logoff.uid,
2061                                         state->request->data.logoff.krb5ccname)) {
2062                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2063                 goto process_result;
2064         }
2065
2066         result = remove_ccache(state->request->data.logoff.user);
2067         if (!NT_STATUS_IS_OK(result)) {
2068                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2069                         nt_errstr(result)));
2070                 goto process_result;
2071         }
2072
2073 #else
2074         result = NT_STATUS_NOT_SUPPORTED;
2075 #endif
2076
2077 process_result:
2078
2079
2080         set_auth_errors(state->response, result);
2081
2082         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2083 }
2084
2085 /* Change user password with auth crap*/
2086
2087 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2088 {
2089         NTSTATUS result;
2090         DATA_BLOB new_nt_password;
2091         DATA_BLOB old_nt_hash_enc;
2092         DATA_BLOB new_lm_password;
2093         DATA_BLOB old_lm_hash_enc;
2094         fstring  domain,user;
2095         struct policy_handle dom_pol;
2096         struct winbindd_domain *contact_domain = domainSt;
2097         struct rpc_pipe_client *cli = NULL;
2098         struct dcerpc_binding_handle *b = NULL;
2099
2100         ZERO_STRUCT(dom_pol);
2101
2102         /* Ensure null termination */
2103         state->request->data.chng_pswd_auth_crap.user[
2104                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2105         state->request->data.chng_pswd_auth_crap.domain[
2106                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2107         *domain = 0;
2108         *user = 0;
2109
2110         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2111                   (unsigned long)state->pid,
2112                   state->request->data.chng_pswd_auth_crap.domain,
2113                   state->request->data.chng_pswd_auth_crap.user));
2114
2115         if (lp_winbind_offline_logon()) {
2116                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2117                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2118                 result = NT_STATUS_ACCESS_DENIED;
2119                 goto done;
2120         }
2121
2122         if (*state->request->data.chng_pswd_auth_crap.domain) {
2123                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2124         } else {
2125                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2126                                   domain, user);
2127
2128                 if(!*domain) {
2129                         DEBUG(3,("no domain specified with username (%s) - "
2130                                  "failing auth\n",
2131                                  state->request->data.chng_pswd_auth_crap.user));
2132                         result = NT_STATUS_NO_SUCH_USER;
2133                         goto done;
2134                 }
2135         }
2136
2137         if (!*domain && lp_winbind_use_default_domain()) {
2138                 fstrcpy(domain,lp_workgroup());
2139         }
2140
2141         if(!*user) {
2142                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2143         }
2144
2145         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2146                   (unsigned long)state->pid, domain, user));
2147
2148         /* Change password */
2149         new_nt_password = data_blob_const(
2150                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2151                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2152
2153         old_nt_hash_enc = data_blob_const(
2154                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2155                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2156
2157         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2158                 new_lm_password = data_blob_const(
2159                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2160                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2161
2162                 old_lm_hash_enc = data_blob_const(
2163                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2164                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2165         } else {
2166                 new_lm_password.length = 0;
2167                 old_lm_hash_enc.length = 0;
2168         }
2169
2170         /* Get sam handle */
2171
2172         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2173         if (!NT_STATUS_IS_OK(result)) {
2174                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2175                 goto done;
2176         }
2177
2178         b = cli->binding_handle;
2179
2180         result = rpccli_samr_chng_pswd_auth_crap(
2181                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2182                 new_lm_password, old_lm_hash_enc);
2183
2184  done:
2185
2186         if (strequal(contact_domain->name, get_global_sam_name())) {
2187                 /* FIXME: internal rpc pipe does not cache handles yet */
2188                 if (b) {
2189                         if (is_valid_policy_hnd(&dom_pol)) {
2190                                 NTSTATUS _result;
2191                                 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2192                         }
2193                         TALLOC_FREE(cli);
2194                 }
2195         }
2196
2197         set_auth_errors(state->response, result);
2198
2199         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2200               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2201                domain, user,
2202                state->response->data.auth.nt_status_string,
2203                state->response->data.auth.pam_error));
2204
2205         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2206 }