s3: Simplify the logic in generate_krb5_ccache
[kai/samba.git] / source3 / winbindd / winbindd_pam.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - pam auth funcions
5
6    Copyright (C) Andrew Tridgell 2000
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett 2001-2002
9    Copyright (C) Guenther Deschner 2005
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/cli_samr.h"
29 #include "rpc_client/cli_samr.h"
30 #include "../librpc/gen_ndr/ndr_netlogon.h"
31 #include "rpc_client/cli_netlogon.h"
32 #include "smb_krb5.h"
33 #include "../lib/crypto/arcfour.h"
34 #include "../libcli/security/dom_sid.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_cli_state *state,
45                                     struct netr_SamInfo3 *info3)
46 {
47         char *ex;
48         uint32_t i;
49
50         state->response->data.auth.info3.logon_time =
51                 nt_time_to_unix(info3->base.last_logon);
52         state->response->data.auth.info3.logoff_time =
53                 nt_time_to_unix(info3->base.last_logoff);
54         state->response->data.auth.info3.kickoff_time =
55                 nt_time_to_unix(info3->base.acct_expiry);
56         state->response->data.auth.info3.pass_last_set_time =
57                 nt_time_to_unix(info3->base.last_password_change);
58         state->response->data.auth.info3.pass_can_change_time =
59                 nt_time_to_unix(info3->base.allow_password_change);
60         state->response->data.auth.info3.pass_must_change_time =
61                 nt_time_to_unix(info3->base.force_password_change);
62
63         state->response->data.auth.info3.logon_count = info3->base.logon_count;
64         state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
65
66         state->response->data.auth.info3.user_rid = info3->base.rid;
67         state->response->data.auth.info3.group_rid = info3->base.primary_gid;
68         sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
69
70         state->response->data.auth.info3.num_groups = info3->base.groups.count;
71         state->response->data.auth.info3.user_flgs = info3->base.user_flags;
72
73         state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
74         state->response->data.auth.info3.num_other_sids = info3->sidcount;
75
76         fstrcpy(state->response->data.auth.info3.user_name,
77                 info3->base.account_name.string);
78         fstrcpy(state->response->data.auth.info3.full_name,
79                 info3->base.full_name.string);
80         fstrcpy(state->response->data.auth.info3.logon_script,
81                 info3->base.logon_script.string);
82         fstrcpy(state->response->data.auth.info3.profile_path,
83                 info3->base.profile_path.string);
84         fstrcpy(state->response->data.auth.info3.home_dir,
85                 info3->base.home_directory.string);
86         fstrcpy(state->response->data.auth.info3.dir_drive,
87                 info3->base.home_drive.string);
88
89         fstrcpy(state->response->data.auth.info3.logon_srv,
90                 info3->base.logon_server.string);
91         fstrcpy(state->response->data.auth.info3.logon_dom,
92                 info3->base.domain.string);
93
94         ex = talloc_strdup(state->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         state->response->extra_data.data = ex;
119         state->response->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_cli_state *state,
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         state->response->extra_data.data = blob.data;
139         state->response->length += blob.length;
140
141         return NT_STATUS_OK;
142 }
143
144 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
145                                      struct winbindd_cli_state *state,
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(state->response->data.auth.unix_username,
170                              nt_domain, nt_username, true);
171
172         DEBUG(5,("Setting unix username to [%s]\n",
173                 state->response->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_cli_state *state,
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         state->response->extra_data.data = talloc_strdup(state->mem_ctx,
234                                                          token);
235         if (state->response->extra_data.data == NULL) {
236                 return NT_STATUS_NO_MEMORY;
237         }
238         state->response->length +=
239                 strlen((const char *)state->response->extra_data.data)+1;
240
241         return NT_STATUS_OK;
242 }
243
244 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
245                                      const char *group_sid)
246 /**
247  * Check whether a user belongs to a group or list of groups.
248  *
249  * @param mem_ctx talloc memory context.
250  * @param info3 user information, including group membership info.
251  * @param group_sid One or more groups , separated by commas.
252  *
253  * @return NT_STATUS_OK on success,
254  *    NT_STATUS_LOGON_FAILURE if the user does not belong,
255  *    or other NT_STATUS_IS_ERR(status) for other kinds of failure.
256  */
257 {
258         struct dom_sid *require_membership_of_sid;
259         size_t num_require_membership_of_sid;
260         char *req_sid;
261         const char *p;
262         struct dom_sid sid;
263         size_t i;
264         struct nt_user_token *token;
265         TALLOC_CTX *frame = talloc_stackframe();
266         NTSTATUS status;
267
268         /* Parse the 'required group' SID */
269
270         if (!group_sid || !group_sid[0]) {
271                 /* NO sid supplied, all users may access */
272                 return NT_STATUS_OK;
273         }
274
275         token = talloc_zero(talloc_tos(), struct nt_user_token);
276         if (token == NULL) {
277                 DEBUG(0, ("talloc failed\n"));
278                 TALLOC_FREE(frame);
279                 return NT_STATUS_NO_MEMORY;
280         }
281
282         num_require_membership_of_sid = 0;
283         require_membership_of_sid = NULL;
284
285         p = group_sid;
286
287         while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
288                 if (!string_to_sid(&sid, req_sid)) {
289                         DEBUG(0, ("check_info3_in_group: could not parse %s "
290                                   "as a SID!", req_sid));
291                         TALLOC_FREE(frame);
292                         return NT_STATUS_INVALID_PARAMETER;
293                 }
294
295                 status = add_sid_to_array(talloc_tos(), &sid,
296                                           &require_membership_of_sid,
297                                           &num_require_membership_of_sid);
298                 if (!NT_STATUS_IS_OK(status)) {
299                         DEBUG(0, ("add_sid_to_array failed\n"));
300                         TALLOC_FREE(frame);
301                         return status;
302                 }
303         }
304
305         status = sid_array_from_info3(talloc_tos(), info3,
306                                       &token->sids,
307                                       &token->num_sids,
308                                       true, false);
309         if (!NT_STATUS_IS_OK(status)) {
310                 TALLOC_FREE(frame);
311                 return status;
312         }
313
314         if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
315                                                   token))
316             || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
317                                                      token))) {
318                 DEBUG(3, ("could not add aliases: %s\n",
319                           nt_errstr(status)));
320                 TALLOC_FREE(frame);
321                 return status;
322         }
323
324         debug_nt_user_token(DBGC_CLASS, 10, token);
325
326         for (i=0; i<num_require_membership_of_sid; i++) {
327                 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
328                                    &require_membership_of_sid[i])));
329                 if (nt_token_check_sid(&require_membership_of_sid[i],
330                                        token)) {
331                         DEBUG(10, ("Access ok\n"));
332                         TALLOC_FREE(frame);
333                         return NT_STATUS_OK;
334                 }
335         }
336
337         /* Do not distinguish this error from a wrong username/pw */
338
339         TALLOC_FREE(frame);
340         return NT_STATUS_LOGON_FAILURE;
341 }
342
343 struct winbindd_domain *find_auth_domain(uint8_t flags,
344                                          const char *domain_name)
345 {
346         struct winbindd_domain *domain;
347
348         if (IS_DC) {
349                 domain = find_domain_from_name_noinit(domain_name);
350                 if (domain == NULL) {
351                         DEBUG(3, ("Authentication for domain [%s] refused "
352                                   "as it is not a trusted domain\n",
353                                   domain_name));
354                 }
355                 return domain;
356         }
357
358         if (strequal(domain_name, get_global_sam_name())) {
359                 return find_domain_from_name_noinit(domain_name);
360         }
361
362         /* we can auth against trusted domains */
363         if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
364                 domain = find_domain_from_name_noinit(domain_name);
365                 if (domain == NULL) {
366                         DEBUG(3, ("Authentication for domain [%s] skipped "
367                                   "as it is not a trusted domain\n",
368                                   domain_name));
369                 } else {
370                         return domain;
371                 }
372         }
373
374         return find_our_domain();
375 }
376
377 static void fill_in_password_policy(struct winbindd_response *r,
378                                     const struct samr_DomInfo1 *p)
379 {
380         r->data.auth.policy.min_length_password =
381                 p->min_password_length;
382         r->data.auth.policy.password_history =
383                 p->password_history_length;
384         r->data.auth.policy.password_properties =
385                 p->password_properties;
386         r->data.auth.policy.expire      =
387                 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
388         r->data.auth.policy.min_passwordage =
389                 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
390 }
391
392 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
393                                        struct winbindd_cli_state *state)
394 {
395         struct winbindd_methods *methods;
396         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
397         struct samr_DomInfo1 password_policy;
398
399         if ( !winbindd_can_contact_domain( domain ) ) {
400                 DEBUG(5,("fillup_password_policy: No inbound trust to "
401                          "contact domain %s\n", domain->name));
402                 return NT_STATUS_NOT_SUPPORTED;
403         }
404
405         methods = domain->methods;
406
407         status = methods->password_policy(domain, state->mem_ctx, &password_policy);
408         if (NT_STATUS_IS_ERR(status)) {
409                 return status;
410         }
411
412         fill_in_password_policy(state->response, &password_policy);
413
414         return NT_STATUS_OK;
415 }
416
417 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
418                                                          TALLOC_CTX *mem_ctx,
419                                                          uint16 *lockout_threshold)
420 {
421         struct winbindd_methods *methods;
422         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
423         struct samr_DomInfo12 lockout_policy;
424
425         *lockout_threshold = 0;
426
427         methods = domain->methods;
428
429         status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
430         if (NT_STATUS_IS_ERR(status)) {
431                 return status;
432         }
433
434         *lockout_threshold = lockout_policy.lockout_threshold;
435
436         return NT_STATUS_OK;
437 }
438
439 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
440                                    TALLOC_CTX *mem_ctx,
441                                    uint32 *password_properties)
442 {
443         struct winbindd_methods *methods;
444         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
445         struct samr_DomInfo1 password_policy;
446
447         *password_properties = 0;
448
449         methods = domain->methods;
450
451         status = methods->password_policy(domain, mem_ctx, &password_policy);
452         if (NT_STATUS_IS_ERR(status)) {
453                 return status;
454         }
455
456         *password_properties = password_policy.password_properties;
457
458         return NT_STATUS_OK;
459 }
460
461 #ifdef HAVE_KRB5
462
463 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
464                                         const char *type,
465                                         uid_t uid,
466                                         const char **user_ccache_file)
467 {
468         /* accept FILE and WRFILE as krb5_cc_type from the client and then
469          * build the full ccname string based on the user's uid here -
470          * Guenther*/
471
472         const char *gen_cc = NULL;
473
474         if (uid != -1) {
475                 if (strequal(type, "FILE")) {
476                         gen_cc = talloc_asprintf(
477                                 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
478                 }
479                 if (strequal(type, "WRFILE")) {
480                         gen_cc = talloc_asprintf(
481                                 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
482                 }
483         }
484
485         *user_ccache_file = gen_cc;
486
487         if (gen_cc == NULL) {
488                 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
489         }
490         if (gen_cc == NULL) {
491                 DEBUG(0,("out of memory\n"));
492                 return NULL;
493         }
494
495         DEBUG(10, ("using ccache: %s%s\n", gen_cc,
496                    (*user_ccache_file == NULL) ? " (internal)":""));
497
498         return gen_cc;
499 }
500
501 #endif
502
503 uid_t get_uid_from_request(struct winbindd_request *request)
504 {
505         uid_t uid;
506
507         uid = request->data.auth.uid;
508
509         if (uid < 0) {
510                 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
511                 return -1;
512         }
513         return uid;
514 }
515
516 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
517 {
518         return get_uid_from_request(state->request);
519 }
520
521 /**********************************************************************
522  Authenticate a user with a clear text password using Kerberos and fill up
523  ccache if required
524  **********************************************************************/
525
526 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
527                                             struct winbindd_cli_state *state,
528                                             struct netr_SamInfo3 **info3)
529 {
530 #ifdef HAVE_KRB5
531         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
532         krb5_error_code krb5_ret;
533         const char *cc = NULL;
534         const char *principal_s = NULL;
535         const char *service = NULL;
536         char *realm = NULL;
537         fstring name_domain, name_user;
538         time_t ticket_lifetime = 0;
539         time_t renewal_until = 0;
540         uid_t uid = -1;
541         ADS_STRUCT *ads;
542         time_t time_offset = 0;
543         const char *user_ccache_file;
544         struct PAC_LOGON_INFO *logon_info = NULL;
545
546         *info3 = NULL;
547
548         /* 1st step:
549          * prepare a krb5_cc_cache string for the user */
550
551         uid = get_uid_from_state(state);
552         if (uid == -1) {
553                 DEBUG(0,("no valid uid\n"));
554         }
555
556         cc = generate_krb5_ccache(state->mem_ctx,
557                                   state->request->data.auth.krb5_cc_type,
558                                   state->request->data.auth.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(state->request->data.auth.user, name_domain, name_user);
578
579         realm = domain->alt_name;
580         strupper_m(realm);
581
582         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
583         if (principal_s == NULL) {
584                 return NT_STATUS_NO_MEMORY;
585         }
586
587         service = talloc_asprintf(state->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(state->mem_ctx,
603                                      principal_s,
604                                      state->request->data.auth.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(state->response->data.auth.krb5ccname,
635                         user_ccache_file);
636
637                 result = add_ccache_to_list(principal_s,
638                                             cc,
639                                             service,
640                                             state->request->data.auth.user,
641                                             realm,
642                                             uid,
643                                             time(NULL),
644                                             ticket_lifetime,
645                                             renewal_until,
646                                             false);
647
648                 if (!NT_STATUS_IS_OK(result)) {
649                         DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
650                                 nt_errstr(result)));
651                 }
652         } else {
653
654                 /* need to delete the memory cred cache, it is not used anymore */
655
656                 krb5_ret = ads_kdestroy(cc);
657                 if (krb5_ret) {
658                         DEBUG(3,("winbindd_raw_kerberos_login: "
659                                  "could not destroy krb5 credential cache: "
660                                  "%s\n", error_message(krb5_ret)));
661                 }
662
663         }
664
665         return NT_STATUS_OK;
666
667 failed:
668
669         /* we could have created a new credential cache with a valid tgt in it
670          * but we werent able to get or verify the service ticket for this
671          * local host and therefor didn't get the PAC, we need to remove that
672          * cache entirely now */
673
674         krb5_ret = ads_kdestroy(cc);
675         if (krb5_ret) {
676                 DEBUG(3,("winbindd_raw_kerberos_login: "
677                          "could not destroy krb5 credential cache: "
678                          "%s\n", error_message(krb5_ret)));
679         }
680
681         if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
682                 DEBUG(3,("winbindd_raw_kerberos_login: "
683                           "could not remove ccache for user %s\n",
684                         state->request->data.auth.user));
685         }
686
687         return result;
688 #else
689         return NT_STATUS_NOT_SUPPORTED;
690 #endif /* HAVE_KRB5 */
691 }
692
693 /****************************************************************
694 ****************************************************************/
695
696 bool check_request_flags(uint32_t flags)
697 {
698         uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
699                                WBFLAG_PAM_INFO3_TEXT |
700                                WBFLAG_PAM_INFO3_NDR;
701
702         if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
703              ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
704              ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
705               !(flags & flags_edata) ) {
706                 return true;
707         }
708
709         DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
710                   flags));
711
712         return false;
713 }
714
715 /****************************************************************
716 ****************************************************************/
717
718 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
719                                  struct netr_SamInfo3 *info3,
720                                  const char *name_domain,
721                                  const char *name_user)
722 {
723         NTSTATUS result;
724         uint32_t flags = state->request->flags;
725
726         if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
727                 memcpy(state->response->data.auth.user_session_key,
728                        info3->base.key.key,
729                        sizeof(state->response->data.auth.user_session_key)
730                        /* 16 */);
731         }
732
733         if (flags & WBFLAG_PAM_LMKEY) {
734                 memcpy(state->response->data.auth.first_8_lm_hash,
735                        info3->base.LMSessKey.key,
736                        sizeof(state->response->data.auth.first_8_lm_hash)
737                        /* 8 */);
738         }
739
740         if (flags & WBFLAG_PAM_UNIX_NAME) {
741                 result = append_unix_username(state->mem_ctx, state, info3,
742                                               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 (flags & WBFLAG_PAM_INFO3_NDR) {
753                 result = append_info3_as_ndr(state->mem_ctx, state, 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 (flags & WBFLAG_PAM_INFO3_TEXT) {
762                 result = append_info3_as_txt(state->mem_ctx, state, 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 (flags & WBFLAG_PAM_AFS_TOKEN) {
771                 result = append_afs_token(state->mem_ctx, state, info3,
772                                           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_state(state);
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(contact_domain, state, info3);
1094 done:
1095         return result;
1096 }
1097
1098 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1099                                           const char *domain, const char *user,
1100                                           const DATA_BLOB *challenge,
1101                                           const DATA_BLOB *lm_resp,
1102                                           const DATA_BLOB *nt_resp,
1103                                           struct netr_SamInfo3 **pinfo3)
1104 {
1105         struct auth_usersupplied_info *user_info = NULL;
1106         NTSTATUS status;
1107
1108         status = make_user_info(&user_info, user, user, domain, domain,
1109                                 global_myname(), lm_resp, nt_resp, NULL, NULL,
1110                                 NULL, AUTH_PASSWORD_RESPONSE);
1111         if (!NT_STATUS_IS_OK(status)) {
1112                 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1113                 return status;
1114         }
1115
1116         /* We don't want any more mapping of the username */
1117         user_info->mapped_state = True;
1118
1119         status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1120                                           pinfo3);
1121         free_user_info(&user_info);
1122         DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1123         return NT_STATUS_OK;
1124 }
1125
1126 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1127                                   TALLOC_CTX *mem_ctx,
1128                                   uint32 logon_parameters,
1129                                   const char *server,
1130                                   const char *username,
1131                                   const char *domain,
1132                                   const char *workstation,
1133                                   const uint8 chal[8],
1134                                   DATA_BLOB lm_response,
1135                                   DATA_BLOB nt_response,
1136                                   struct netr_SamInfo3 **info3);
1137
1138 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1139                                                 struct winbindd_cli_state *state,
1140                                                 struct netr_SamInfo3 **info3)
1141 {
1142
1143         struct rpc_pipe_client *netlogon_pipe;
1144         uchar chal[8];
1145         DATA_BLOB lm_resp;
1146         DATA_BLOB nt_resp;
1147         int attempts = 0;
1148         unsigned char local_lm_response[24];
1149         unsigned char local_nt_response[24];
1150         fstring name_domain, name_user;
1151         bool retry;
1152         NTSTATUS result;
1153         struct netr_SamInfo3 *my_info3 = NULL;
1154
1155         *info3 = NULL;
1156
1157         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1158
1159         /* Parse domain and username */
1160
1161         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1162
1163         /* do password magic */
1164
1165         generate_random_buffer(chal, sizeof(chal));
1166
1167         if (lp_client_ntlmv2_auth()) {
1168                 DATA_BLOB server_chal;
1169                 DATA_BLOB names_blob;
1170                 DATA_BLOB nt_response;
1171                 DATA_BLOB lm_response;
1172                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1173
1174                 /* note that the 'workgroup' here is a best guess - we don't know
1175                    the server's domain at this point.  The 'server name' is also
1176                    dodgy...
1177                 */
1178                 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1179
1180                 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1181                                       state->request->data.auth.pass,
1182                                       &server_chal,
1183                                       &names_blob,
1184                                       &lm_response, &nt_response, NULL, NULL)) {
1185                         data_blob_free(&names_blob);
1186                         data_blob_free(&server_chal);
1187                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1188                         result = NT_STATUS_NO_MEMORY;
1189                         goto done;
1190                 }
1191                 data_blob_free(&names_blob);
1192                 data_blob_free(&server_chal);
1193                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1194                                            lm_response.length);
1195                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1196                                            nt_response.length);
1197                 data_blob_free(&lm_response);
1198                 data_blob_free(&nt_response);
1199
1200         } else {
1201                 if (lp_client_lanman_auth()
1202                     && SMBencrypt(state->request->data.auth.pass,
1203                                   chal,
1204                                   local_lm_response)) {
1205                         lm_resp = data_blob_talloc(state->mem_ctx,
1206                                                    local_lm_response,
1207                                                    sizeof(local_lm_response));
1208                 } else {
1209                         lm_resp = data_blob_null;
1210                 }
1211                 SMBNTencrypt(state->request->data.auth.pass,
1212                              chal,
1213                              local_nt_response);
1214
1215                 nt_resp = data_blob_talloc(state->mem_ctx,
1216                                            local_nt_response,
1217                                            sizeof(local_nt_response));
1218         }
1219
1220         if (strequal(name_domain, get_global_sam_name())) {
1221                 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1222
1223                 result = winbindd_dual_auth_passdb(
1224                         state->mem_ctx, name_domain, name_user,
1225                         &chal_blob, &lm_resp, &nt_resp, info3);
1226                 goto done;
1227         }
1228
1229         /* check authentication loop */
1230
1231         do {
1232                 netlogon_fn_t logon_fn;
1233
1234                 ZERO_STRUCTP(my_info3);
1235                 retry = false;
1236
1237                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1238
1239                 if (!NT_STATUS_IS_OK(result)) {
1240                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1241                         goto done;
1242                 }
1243
1244                 /* It is really important to try SamLogonEx here,
1245                  * because in a clustered environment, we want to use
1246                  * one machine account from multiple physical
1247                  * computers.
1248                  *
1249                  * With a normal SamLogon call, we must keep the
1250                  * credentials chain updated and intact between all
1251                  * users of the machine account (which would imply
1252                  * cross-node communication for every NTLM logon).
1253                  *
1254                  * (The credentials chain is not per NETLOGON pipe
1255                  * connection, but globally on the server/client pair
1256                  * by machine name).
1257                  *
1258                  * When using SamLogonEx, the credentials are not
1259                  * supplied, but the session key is implied by the
1260                  * wrapping SamLogon context.
1261                  *
1262                  *  -- abartlet 21 April 2008
1263                  */
1264
1265                 logon_fn = domain->can_do_samlogon_ex
1266                         ? rpccli_netlogon_sam_network_logon_ex
1267                         : rpccli_netlogon_sam_network_logon;
1268
1269                 result = logon_fn(netlogon_pipe,
1270                                   state->mem_ctx,
1271                                   0,
1272                                   domain->dcname,         /* server name */
1273                                   name_user,              /* user name */
1274                                   name_domain,            /* target domain */
1275                                   global_myname(),        /* workstation */
1276                                   chal,
1277                                   lm_resp,
1278                                   nt_resp,
1279                                   &my_info3);
1280                 attempts += 1;
1281
1282                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1283                     && domain->can_do_samlogon_ex) {
1284                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1285                                   "retrying with NetSamLogon\n"));
1286                         domain->can_do_samlogon_ex = false;
1287                         continue;
1288                 }
1289
1290                 /* We have to try a second time as cm_connect_netlogon
1291                    might not yet have noticed that the DC has killed
1292                    our connection. */
1293
1294                 if (!rpccli_is_connected(netlogon_pipe)) {
1295                         continue;
1296                 }
1297
1298                 /* if we get access denied, a possible cause was that we had
1299                    and open connection to the DC, but someone changed our
1300                    machine account password out from underneath us using 'net
1301                    rpc changetrustpw' */
1302
1303                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1304                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1305                                  "ACCESS_DENIED.  Maybe the trust account "
1306                                 "password was changed and we didn't know it. "
1307                                  "Killing connections to domain %s\n",
1308                                 name_domain));
1309                         invalidate_cm_connection(&domain->conn);
1310                         retry = true;
1311                 }
1312
1313         } while ( (attempts < 2) && retry );
1314
1315         /* handle the case where a NT4 DC does not fill in the acct_flags in
1316          * the samlogon reply info3. When accurate info3 is required by the
1317          * caller, we look up the account flags ourselve - gd */
1318
1319         if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1320             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1321
1322                 struct rpc_pipe_client *samr_pipe;
1323                 struct policy_handle samr_domain_handle, user_pol;
1324                 union samr_UserInfo *info = NULL;
1325                 NTSTATUS status_tmp;
1326                 uint32 acct_flags;
1327
1328                 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1329                                             &samr_pipe, &samr_domain_handle);
1330
1331                 if (!NT_STATUS_IS_OK(status_tmp)) {
1332                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1333                                 nt_errstr(status_tmp)));
1334                         goto done;
1335                 }
1336
1337                 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1338                                                   &samr_domain_handle,
1339                                                   MAXIMUM_ALLOWED_ACCESS,
1340                                                   my_info3->base.rid,
1341                                                   &user_pol);
1342
1343                 if (!NT_STATUS_IS_OK(status_tmp)) {
1344                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1345                                 nt_errstr(status_tmp)));
1346                         goto done;
1347                 }
1348
1349                 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1350                                                        &user_pol,
1351                                                        16,
1352                                                        &info);
1353
1354                 if (!NT_STATUS_IS_OK(status_tmp)) {
1355                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1356                                 nt_errstr(status_tmp)));
1357                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1358                         goto done;
1359                 }
1360
1361                 acct_flags = info->info16.acct_flags;
1362
1363                 if (acct_flags == 0) {
1364                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1365                         goto done;
1366                 }
1367
1368                 my_info3->base.acct_flags = acct_flags;
1369
1370                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1371
1372                 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1373         }
1374
1375         *info3 = my_info3;
1376 done:
1377         return result;
1378 }
1379
1380 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1381                                             struct winbindd_cli_state *state)
1382 {
1383         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1384         NTSTATUS krb5_result = NT_STATUS_OK;
1385         fstring name_domain, name_user;
1386         char *mapped_user;
1387         fstring domain_user;
1388         struct netr_SamInfo3 *info3 = NULL;
1389         NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1390
1391         /* Ensure null termination */
1392         state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1393
1394         /* Ensure null termination */
1395         state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1396
1397         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1398                   state->request->data.auth.user));
1399
1400         /* Parse domain and username */
1401
1402         name_map_status = normalize_name_unmap(state->mem_ctx,
1403                                                state->request->data.auth.user,
1404                                                &mapped_user);
1405
1406         /* If the name normalization didnt' actually do anything,
1407            just use the original name */
1408
1409         if (!NT_STATUS_IS_OK(name_map_status) &&
1410             !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1411         {
1412                 mapped_user = state->request->data.auth.user;
1413         }
1414
1415         parse_domain_user(mapped_user, name_domain, name_user);
1416
1417         if ( mapped_user != state->request->data.auth.user ) {
1418                 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1419                         *lp_winbind_separator(),
1420                         name_user );
1421                 safe_strcpy( state->request->data.auth.user, domain_user,
1422                              sizeof(state->request->data.auth.user)-1 );
1423         }
1424
1425         if (!domain->online) {
1426                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1427                 if (domain->startup) {
1428                         /* Logons are very important to users. If we're offline and
1429                            we get a request within the first 30 seconds of startup,
1430                            try very hard to find a DC and go online. */
1431
1432                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1433                                 "request in startup mode.\n", domain->name ));
1434
1435                         winbindd_flush_negative_conn_cache(domain);
1436                         result = init_dc_connection(domain);
1437                 }
1438         }
1439
1440         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1441
1442         /* Check for Kerberos authentication */
1443         if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1444
1445                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1446                 /* save for later */
1447                 krb5_result = result;
1448
1449
1450                 if (NT_STATUS_IS_OK(result)) {
1451                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1452                         goto process_result;
1453                 } else {
1454                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1455                 }
1456
1457                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1458                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1459                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1460                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1461                         set_domain_offline( domain );
1462                         goto cached_logon;
1463                 }
1464
1465                 /* there are quite some NT_STATUS errors where there is no
1466                  * point in retrying with a samlogon, we explictly have to take
1467                  * care not to increase the bad logon counter on the DC */
1468
1469                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1470                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1471                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1472                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1473                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1474                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1475                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1476                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1477                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1478                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1479                         goto done;
1480                 }
1481
1482                 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1483                         DEBUG(3,("falling back to samlogon\n"));
1484                         goto sam_logon;
1485                 } else {
1486                         goto cached_logon;
1487                 }
1488         }
1489
1490 sam_logon:
1491         /* Check for Samlogon authentication */
1492         if (domain->online) {
1493                 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1494
1495                 if (NT_STATUS_IS_OK(result)) {
1496                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1497                         /* add the Krb5 err if we have one */
1498                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1499                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1500                         }
1501                         goto process_result;
1502                 }
1503
1504                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1505                           nt_errstr(result)));
1506
1507                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1508                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1509                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1510                 {
1511                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1512                         set_domain_offline( domain );
1513                         goto cached_logon;
1514                 }
1515
1516                         if (domain->online) {
1517                                 /* We're still online - fail. */
1518                                 goto done;
1519                         }
1520         }
1521
1522 cached_logon:
1523         /* Check for Cached logons */
1524         if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1525             lp_winbind_offline_logon()) {
1526
1527                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1528
1529                 if (NT_STATUS_IS_OK(result)) {
1530                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1531                         goto process_result;
1532                 } else {
1533                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1534                         goto done;
1535                 }
1536         }
1537
1538 process_result:
1539
1540         if (NT_STATUS_IS_OK(result)) {
1541
1542                 struct dom_sid user_sid;
1543
1544                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1545                    been initialized. */
1546                 if (!info3) {
1547                         result = NT_STATUS_INTERNAL_ERROR;
1548                         goto done;
1549                 }
1550
1551                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1552                 netsamlogon_cache_store(name_user, info3);
1553
1554                 /* save name_to_sid info as early as possible (only if
1555                    this is our primary domain so we don't invalidate
1556                    the cache entry by storing the seq_num for the wrong
1557                    domain). */
1558                 if ( domain->primary ) {
1559                         sid_compose(&user_sid, info3->base.domain_sid,
1560                                     info3->base.rid);
1561                         cache_name2sid(domain, name_domain, name_user,
1562                                        SID_NAME_USER, &user_sid);
1563                 }
1564
1565                 /* Check if the user is in the right group */
1566
1567                 result = check_info3_in_group(
1568                         info3,
1569                         state->request->data.auth.require_membership_of_sid);
1570                 if (!NT_STATUS_IS_OK(result)) {
1571                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1572                                   state->request->data.auth.user,
1573                                   state->request->data.auth.require_membership_of_sid));
1574                         goto done;
1575                 }
1576
1577                 result = append_auth_data(state, info3, name_domain,
1578                                           name_user);
1579                 if (!NT_STATUS_IS_OK(result)) {
1580                         goto done;
1581                 }
1582
1583                 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1584                     && lp_winbind_offline_logon()) {
1585
1586                         result = winbindd_store_creds(domain,
1587                                                       state->request->data.auth.user,
1588                                                       state->request->data.auth.pass,
1589                                                       info3);
1590                 }
1591
1592                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1593                         struct winbindd_domain *our_domain = find_our_domain();
1594
1595                         /* This is not entirely correct I believe, but it is
1596                            consistent.  Only apply the password policy settings
1597                            too warn users for our own domain.  Cannot obtain these
1598                            from trusted DCs all the  time so don't do it at all.
1599                            -- jerry */
1600
1601                         result = NT_STATUS_NOT_SUPPORTED;
1602                         if (our_domain == domain ) {
1603                                 result = fillup_password_policy(our_domain, state);
1604                         }
1605
1606                         if (!NT_STATUS_IS_OK(result)
1607                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1608                         {
1609                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1610                                           domain->name, nt_errstr(result)));
1611                                 goto done;
1612                         }
1613                 }
1614
1615                 result = NT_STATUS_OK;
1616         }
1617
1618 done:
1619         /* give us a more useful (more correct?) error code */
1620         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1621             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1622                 result = NT_STATUS_NO_LOGON_SERVERS;
1623         }
1624
1625         set_auth_errors(state->response, result);
1626
1627         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1628               state->request->data.auth.user,
1629               state->response->data.auth.nt_status_string,
1630               state->response->data.auth.pam_error));
1631
1632         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1633 }
1634
1635 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1636                                                  struct winbindd_cli_state *state)
1637 {
1638         NTSTATUS result;
1639         struct netr_SamInfo3 *info3 = NULL;
1640         struct rpc_pipe_client *netlogon_pipe;
1641         const char *name_user = NULL;
1642         const char *name_domain = NULL;
1643         const char *workstation;
1644         int attempts = 0;
1645         bool retry;
1646
1647         DATA_BLOB lm_resp, nt_resp;
1648
1649         /* This is child-only, so no check for privileged access is needed
1650            anymore */
1651
1652         /* Ensure null termination */
1653         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1654         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1655
1656         name_user = state->request->data.auth_crap.user;
1657         name_domain = state->request->data.auth_crap.domain;
1658         workstation = state->request->data.auth_crap.workstation;
1659
1660         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1661                   name_domain, name_user));
1662
1663         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1664                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1665                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1666                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1667                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1668                                   state->request->data.auth_crap.lm_resp_len,
1669                                   state->request->data.auth_crap.nt_resp_len));
1670                         result = NT_STATUS_INVALID_PARAMETER;
1671                         goto done;
1672                 }
1673         }
1674
1675         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1676                                         state->request->data.auth_crap.lm_resp_len);
1677
1678         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1679                 nt_resp = data_blob_talloc(state->mem_ctx,
1680                                            state->request->extra_data.data,
1681                                            state->request->data.auth_crap.nt_resp_len);
1682         } else {
1683                 nt_resp = data_blob_talloc(state->mem_ctx,
1684                                            state->request->data.auth_crap.nt_resp,
1685                                            state->request->data.auth_crap.nt_resp_len);
1686         }
1687
1688         if (strequal(name_domain, get_global_sam_name())) {
1689                 DATA_BLOB chal_blob = data_blob_const(
1690                         state->request->data.auth_crap.chal,
1691                         sizeof(state->request->data.auth_crap.chal));
1692
1693                 result = winbindd_dual_auth_passdb(
1694                         state->mem_ctx, name_domain, name_user,
1695                         &chal_blob, &lm_resp, &nt_resp, &info3);
1696                 goto process_result;
1697         }
1698
1699         do {
1700                 netlogon_fn_t logon_fn;
1701
1702                 retry = false;
1703
1704                 netlogon_pipe = NULL;
1705                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1706
1707                 if (!NT_STATUS_IS_OK(result)) {
1708                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1709                                   nt_errstr(result)));
1710                         goto done;
1711                 }
1712
1713                 logon_fn = domain->can_do_samlogon_ex
1714                         ? rpccli_netlogon_sam_network_logon_ex
1715                         : rpccli_netlogon_sam_network_logon;
1716
1717                 result = logon_fn(netlogon_pipe,
1718                                   state->mem_ctx,
1719                                   state->request->data.auth_crap.logon_parameters,
1720                                   domain->dcname,
1721                                   name_user,
1722                                   name_domain,
1723                                   /* Bug #3248 - found by Stefan Burkei. */
1724                                   workstation, /* We carefully set this above so use it... */
1725                                   state->request->data.auth_crap.chal,
1726                                   lm_resp,
1727                                   nt_resp,
1728                                   &info3);
1729
1730                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1731                     && domain->can_do_samlogon_ex) {
1732                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1733                                   "retrying with NetSamLogon\n"));
1734                         domain->can_do_samlogon_ex = false;
1735                         continue;
1736                 }
1737
1738                 attempts += 1;
1739
1740                 /* We have to try a second time as cm_connect_netlogon
1741                    might not yet have noticed that the DC has killed
1742                    our connection. */
1743
1744                 if (!rpccli_is_connected(netlogon_pipe)) {
1745                         continue;
1746                 }
1747
1748                 /* if we get access denied, a possible cause was that we had and open
1749                    connection to the DC, but someone changed our machine account password
1750                    out from underneath us using 'net rpc changetrustpw' */
1751
1752                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1753                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1754                                  "ACCESS_DENIED.  Maybe the trust account "
1755                                 "password was changed and we didn't know it. "
1756                                  "Killing connections to domain %s\n",
1757                                 name_domain));
1758                         invalidate_cm_connection(&domain->conn);
1759                         retry = true;
1760                 }
1761
1762         } while ( (attempts < 2) && retry );
1763
1764 process_result:
1765
1766         if (NT_STATUS_IS_OK(result)) {
1767
1768                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1769                 netsamlogon_cache_store(name_user, info3);
1770
1771                 /* Check if the user is in the right group */
1772
1773                 result = check_info3_in_group(
1774                         info3,
1775                         state->request->data.auth_crap.require_membership_of_sid);
1776                 if (!NT_STATUS_IS_OK(result)) {
1777                         DEBUG(3, ("User %s is not in the required group (%s), so "
1778                                   "crap authentication is rejected\n",
1779                                   state->request->data.auth_crap.user,
1780                                   state->request->data.auth_crap.require_membership_of_sid));
1781                         goto done;
1782                 }
1783
1784                 result = append_auth_data(state, info3, name_domain,
1785                                           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
1828         ZERO_STRUCT(dom_pol);
1829
1830         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1831                   state->request->data.auth.user));
1832
1833         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1834                 goto done;
1835         }
1836
1837         /* Change password */
1838
1839         oldpass = state->request->data.chauthtok.oldpass;
1840         newpass = state->request->data.chauthtok.newpass;
1841
1842         /* Initialize reject reason */
1843         state->response->data.auth.reject_reason = Undefined;
1844
1845         /* Get sam handle */
1846
1847         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1848                                 &dom_pol);
1849         if (!NT_STATUS_IS_OK(result)) {
1850                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1851                 goto done;
1852         }
1853
1854         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1855                                              user,
1856                                              newpass,
1857                                              oldpass,
1858                                              &info,
1859                                              &reject);
1860
1861         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1862
1863         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1864
1865                 fill_in_password_policy(state->response, info);
1866
1867                 state->response->data.auth.reject_reason =
1868                         reject->extendedFailureReason;
1869
1870                 got_info = true;
1871         }
1872
1873         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1874          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1875          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1876          * short to comply with the samr_ChangePasswordUser3 idl - gd */
1877
1878         /* only fallback when the chgpasswd_user3 call is not supported */
1879         if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1880                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1881                    (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1882                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1883
1884                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1885                         nt_errstr(result)));
1886
1887                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1888
1889                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1890                    Map to the same status code as Windows 2003. */
1891
1892                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1893                         result = NT_STATUS_PASSWORD_RESTRICTION;
1894                 }
1895         }
1896
1897 done:
1898
1899         if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1900                 if (lp_winbind_offline_logon()) {
1901                         result = winbindd_update_creds_by_name(contact_domain,
1902                                                                user, newpass);
1903                         /* Again, this happens when we login from gdm or xdm
1904                          * and the password expires, *BUT* cached crendentials
1905                          * doesn't exist. winbindd_update_creds_by_name()
1906                          * returns NT_STATUS_NO_SUCH_USER.
1907                          * This is not a failure.
1908                          * --- BoYang
1909                          * */
1910                         if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1911                                 result = NT_STATUS_OK;
1912                         }
1913
1914                         if (!NT_STATUS_IS_OK(result)) {
1915                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1916                                 goto process_result;
1917                         }
1918                 }
1919         }
1920
1921         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1922
1923                 NTSTATUS policy_ret;
1924
1925                 policy_ret = fillup_password_policy(contact_domain, state);
1926
1927                 /* failure of this is non critical, it will just provide no
1928                  * additional information to the client why the change has
1929                  * failed - Guenther */
1930
1931                 if (!NT_STATUS_IS_OK(policy_ret)) {
1932                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1933                         goto process_result;
1934                 }
1935         }
1936
1937 process_result:
1938
1939         if (strequal(contact_domain->name, get_global_sam_name())) {
1940                 /* FIXME: internal rpc pipe does not cache handles yet */
1941                 if (cli) {
1942                         if (is_valid_policy_hnd(&dom_pol)) {
1943                                 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1944                         }
1945                         TALLOC_FREE(cli);
1946                 }
1947         }
1948
1949         set_auth_errors(state->response, result);
1950
1951         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1952               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1953                domain,
1954                user,
1955                state->response->data.auth.nt_status_string,
1956                state->response->data.auth.pam_error));
1957
1958         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1959 }
1960
1961 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1962                                               struct winbindd_cli_state *state)
1963 {
1964         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1965
1966         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1967                 state->request->data.logoff.user));
1968
1969         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
1970                 result = NT_STATUS_OK;
1971                 goto process_result;
1972         }
1973
1974         if (state->request->data.logoff.krb5ccname[0] == '\0') {
1975                 result = NT_STATUS_OK;
1976                 goto process_result;
1977         }
1978
1979 #ifdef HAVE_KRB5
1980
1981         if (state->request->data.logoff.uid < 0) {
1982                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1983                 goto process_result;
1984         }
1985
1986         /* what we need here is to find the corresponding krb5 ccache name *we*
1987          * created for a given username and destroy it */
1988
1989         if (!ccache_entry_exists(state->request->data.logoff.user)) {
1990                 result = NT_STATUS_OK;
1991                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
1992                 goto process_result;
1993         }
1994
1995         if (!ccache_entry_identical(state->request->data.logoff.user,
1996                                         state->request->data.logoff.uid,
1997                                         state->request->data.logoff.krb5ccname)) {
1998                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
1999                 goto process_result;
2000         }
2001
2002         result = remove_ccache(state->request->data.logoff.user);
2003         if (!NT_STATUS_IS_OK(result)) {
2004                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2005                         nt_errstr(result)));
2006                 goto process_result;
2007         }
2008
2009 #else
2010         result = NT_STATUS_NOT_SUPPORTED;
2011 #endif
2012
2013 process_result:
2014
2015
2016         set_auth_errors(state->response, result);
2017
2018         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2019 }
2020
2021 /* Change user password with auth crap*/
2022
2023 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2024 {
2025         NTSTATUS result;
2026         DATA_BLOB new_nt_password;
2027         DATA_BLOB old_nt_hash_enc;
2028         DATA_BLOB new_lm_password;
2029         DATA_BLOB old_lm_hash_enc;
2030         fstring  domain,user;
2031         struct policy_handle dom_pol;
2032         struct winbindd_domain *contact_domain = domainSt;
2033         struct rpc_pipe_client *cli = NULL;
2034
2035         ZERO_STRUCT(dom_pol);
2036
2037         /* Ensure null termination */
2038         state->request->data.chng_pswd_auth_crap.user[
2039                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2040         state->request->data.chng_pswd_auth_crap.domain[
2041                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2042         *domain = 0;
2043         *user = 0;
2044
2045         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2046                   (unsigned long)state->pid,
2047                   state->request->data.chng_pswd_auth_crap.domain,
2048                   state->request->data.chng_pswd_auth_crap.user));
2049
2050         if (lp_winbind_offline_logon()) {
2051                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2052                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2053                 result = NT_STATUS_ACCESS_DENIED;
2054                 goto done;
2055         }
2056
2057         if (*state->request->data.chng_pswd_auth_crap.domain) {
2058                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2059         } else {
2060                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2061                                   domain, user);
2062
2063                 if(!*domain) {
2064                         DEBUG(3,("no domain specified with username (%s) - "
2065                                  "failing auth\n",
2066                                  state->request->data.chng_pswd_auth_crap.user));
2067                         result = NT_STATUS_NO_SUCH_USER;
2068                         goto done;
2069                 }
2070         }
2071
2072         if (!*domain && lp_winbind_use_default_domain()) {
2073                 fstrcpy(domain,(char *)lp_workgroup());
2074         }
2075
2076         if(!*user) {
2077                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2078         }
2079
2080         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2081                   (unsigned long)state->pid, domain, user));
2082
2083         /* Change password */
2084         new_nt_password = data_blob_const(
2085                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2086                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2087
2088         old_nt_hash_enc = data_blob_const(
2089                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2090                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2091
2092         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2093                 new_lm_password = data_blob_const(
2094                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2095                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2096
2097                 old_lm_hash_enc = data_blob_const(
2098                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2099                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2100         } else {
2101                 new_lm_password.length = 0;
2102                 old_lm_hash_enc.length = 0;
2103         }
2104
2105         /* Get sam handle */
2106
2107         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2108         if (!NT_STATUS_IS_OK(result)) {
2109                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2110                 goto done;
2111         }
2112
2113         result = rpccli_samr_chng_pswd_auth_crap(
2114                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2115                 new_lm_password, old_lm_hash_enc);
2116
2117  done:
2118
2119         if (strequal(contact_domain->name, get_global_sam_name())) {
2120                 /* FIXME: internal rpc pipe does not cache handles yet */
2121                 if (cli) {
2122                         if (is_valid_policy_hnd(&dom_pol)) {
2123                                 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2124                         }
2125                         TALLOC_FREE(cli);
2126                 }
2127         }
2128
2129         set_auth_errors(state->response, result);
2130
2131         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2132               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2133                domain, user,
2134                state->response->data.auth.nt_status_string,
2135                state->response->data.auth.pam_error));
2136
2137         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2138 }