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