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