s3: Remove redundant flag checks
[metze/samba/wip.git] / source3 / winbindd / winbindd_pam.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - pam auth funcions
5
6    Copyright (C) Andrew Tridgell 2000
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett 2001-2002
9    Copyright (C) Guenther Deschner 2005
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/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                                         bool *internal_ccache)
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         *internal_ccache = true;
475
476         if (uid == -1) {
477                 goto memory_ccache;
478         }
479
480         if (!type || type[0] == '\0') {
481                 goto memory_ccache;
482         }
483
484         if (strequal(type, "FILE")) {
485                 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
486         } else if (strequal(type, "WRFILE")) {
487                 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
488         } else {
489                 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
490                 goto memory_ccache;
491         }
492
493         *internal_ccache = false;
494         goto done;
495
496   memory_ccache:
497         gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
498
499   done:
500         if (gen_cc == NULL) {
501                 DEBUG(0,("out of memory\n"));
502                 return NULL;
503         }
504
505         DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
506
507         return gen_cc;
508 }
509
510 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
511 {
512         const char *type = state->request->data.auth.krb5_cc_type;
513
514         state->response->data.auth.krb5ccname[0] = '\0';
515
516         if (type[0] == '\0') {
517                 return;
518         }
519
520         if (!strequal(type, "FILE") &&
521             !strequal(type, "WRFILE")) {
522                 DEBUG(10,("won't return krbccname for a %s type ccache\n",
523                         type));
524                 return;
525         }
526
527         fstrcpy(state->response->data.auth.krb5ccname, cc);
528 }
529
530 #endif
531
532 uid_t get_uid_from_request(struct winbindd_request *request)
533 {
534         uid_t uid;
535
536         uid = request->data.auth.uid;
537
538         if (uid < 0) {
539                 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
540                 return -1;
541         }
542         return uid;
543 }
544
545 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
546 {
547         return get_uid_from_request(state->request);
548 }
549
550 /**********************************************************************
551  Authenticate a user with a clear text password using Kerberos and fill up
552  ccache if required
553  **********************************************************************/
554
555 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
556                                             struct winbindd_cli_state *state,
557                                             struct netr_SamInfo3 **info3)
558 {
559 #ifdef HAVE_KRB5
560         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
561         krb5_error_code krb5_ret;
562         const char *cc = NULL;
563         const char *principal_s = NULL;
564         const char *service = NULL;
565         char *realm = NULL;
566         fstring name_domain, name_user;
567         time_t ticket_lifetime = 0;
568         time_t renewal_until = 0;
569         uid_t uid = -1;
570         ADS_STRUCT *ads;
571         time_t time_offset = 0;
572         bool internal_ccache = true;
573         struct PAC_LOGON_INFO *logon_info = NULL;
574
575         *info3 = NULL;
576
577         /* 1st step:
578          * prepare a krb5_cc_cache string for the user */
579
580         uid = get_uid_from_state(state);
581         if (uid == -1) {
582                 DEBUG(0,("no valid uid\n"));
583         }
584
585         cc = generate_krb5_ccache(state->mem_ctx,
586                                   state->request->data.auth.krb5_cc_type,
587                                   state->request->data.auth.uid,
588                                   &internal_ccache);
589         if (cc == NULL) {
590                 return NT_STATUS_NO_MEMORY;
591         }
592
593
594         /* 2nd step:
595          * get kerberos properties */
596
597         if (domain->private_data) {
598                 ads = (ADS_STRUCT *)domain->private_data;
599                 time_offset = ads->auth.time_offset;
600         }
601
602
603         /* 3rd step:
604          * do kerberos auth and setup ccache as the user */
605
606         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
607
608         realm = domain->alt_name;
609         strupper_m(realm);
610
611         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
612         if (principal_s == NULL) {
613                 return NT_STATUS_NO_MEMORY;
614         }
615
616         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
617         if (service == NULL) {
618                 return NT_STATUS_NO_MEMORY;
619         }
620
621         /* if this is a user ccache, we need to act as the user to let the krb5
622          * library handle the chown, etc. */
623
624         /************************ ENTERING NON-ROOT **********************/
625
626         if (!internal_ccache) {
627                 set_effective_uid(uid);
628                 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
629         }
630
631         result = kerberos_return_pac(state->mem_ctx,
632                                      principal_s,
633                                      state->request->data.auth.pass,
634                                      time_offset,
635                                      &ticket_lifetime,
636                                      &renewal_until,
637                                      cc,
638                                      true,
639                                      true,
640                                      WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
641                                      NULL,
642                                      &logon_info);
643         if (!internal_ccache) {
644                 gain_root_privilege();
645         }
646
647         /************************ RETURNED TO ROOT **********************/
648
649         if (!NT_STATUS_IS_OK(result)) {
650                 goto failed;
651         }
652
653         *info3 = &logon_info->info3;
654
655         DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
656                 principal_s));
657
658         /* if we had a user's ccache then return that string for the pam
659          * environment */
660
661         if (!internal_ccache) {
662
663                 setup_return_cc_name(state, cc);
664
665                 result = add_ccache_to_list(principal_s,
666                                             cc,
667                                             service,
668                                             state->request->data.auth.user,
669                                             realm,
670                                             uid,
671                                             time(NULL),
672                                             ticket_lifetime,
673                                             renewal_until,
674                                             false);
675
676                 if (!NT_STATUS_IS_OK(result)) {
677                         DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
678                                 nt_errstr(result)));
679                 }
680         } else {
681
682                 /* need to delete the memory cred cache, it is not used anymore */
683
684                 krb5_ret = ads_kdestroy(cc);
685                 if (krb5_ret) {
686                         DEBUG(3,("winbindd_raw_kerberos_login: "
687                                  "could not destroy krb5 credential cache: "
688                                  "%s\n", error_message(krb5_ret)));
689                 }
690
691         }
692
693         return NT_STATUS_OK;
694
695 failed:
696
697         /* we could have created a new credential cache with a valid tgt in it
698          * but we werent able to get or verify the service ticket for this
699          * local host and therefor didn't get the PAC, we need to remove that
700          * cache entirely now */
701
702         krb5_ret = ads_kdestroy(cc);
703         if (krb5_ret) {
704                 DEBUG(3,("winbindd_raw_kerberos_login: "
705                          "could not destroy krb5 credential cache: "
706                          "%s\n", error_message(krb5_ret)));
707         }
708
709         if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
710                 DEBUG(3,("winbindd_raw_kerberos_login: "
711                           "could not remove ccache for user %s\n",
712                         state->request->data.auth.user));
713         }
714
715         return result;
716 #else
717         return NT_STATUS_NOT_SUPPORTED;
718 #endif /* HAVE_KRB5 */
719 }
720
721 /****************************************************************
722 ****************************************************************/
723
724 bool check_request_flags(uint32_t flags)
725 {
726         uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
727                                WBFLAG_PAM_INFO3_TEXT |
728                                WBFLAG_PAM_INFO3_NDR;
729
730         if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
731              ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
732              ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
733               !(flags & flags_edata) ) {
734                 return true;
735         }
736
737         DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
738                   flags));
739
740         return false;
741 }
742
743 /****************************************************************
744 ****************************************************************/
745
746 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
747                                  struct netr_SamInfo3 *info3,
748                                  const char *name_domain,
749                                  const char *name_user)
750 {
751         NTSTATUS result;
752         uint32_t flags = state->request->flags;
753
754         if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
755                 memcpy(state->response->data.auth.user_session_key,
756                        info3->base.key.key,
757                        sizeof(state->response->data.auth.user_session_key)
758                        /* 16 */);
759         }
760
761         if (flags & WBFLAG_PAM_LMKEY) {
762                 memcpy(state->response->data.auth.first_8_lm_hash,
763                        info3->base.LMSessKey.key,
764                        sizeof(state->response->data.auth.first_8_lm_hash)
765                        /* 8 */);
766         }
767
768         if (flags & WBFLAG_PAM_UNIX_NAME) {
769                 result = append_unix_username(state->mem_ctx, state, info3,
770                                               name_domain, name_user);
771                 if (!NT_STATUS_IS_OK(result)) {
772                         DEBUG(10,("Failed to append Unix Username: %s\n",
773                                 nt_errstr(result)));
774                         return result;
775                 }
776         }
777
778         /* currently, anything from here on potentially overwrites extra_data. */
779
780         if (flags & WBFLAG_PAM_INFO3_NDR) {
781                 result = append_info3_as_ndr(state->mem_ctx, state, info3);
782                 if (!NT_STATUS_IS_OK(result)) {
783                         DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
784                                 nt_errstr(result)));
785                         return result;
786                 }
787         }
788
789         if (flags & WBFLAG_PAM_INFO3_TEXT) {
790                 result = append_info3_as_txt(state->mem_ctx, state, info3);
791                 if (!NT_STATUS_IS_OK(result)) {
792                         DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
793                                 nt_errstr(result)));
794                         return result;
795                 }
796         }
797
798         if (flags & WBFLAG_PAM_AFS_TOKEN) {
799                 result = append_afs_token(state->mem_ctx, state, info3,
800                                           name_domain, name_user);
801                 if (!NT_STATUS_IS_OK(result)) {
802                         DEBUG(10,("Failed to append AFS token: %s\n",
803                                 nt_errstr(result)));
804                         return result;
805                 }
806         }
807
808         return NT_STATUS_OK;
809 }
810
811 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
812                                               struct winbindd_cli_state *state,
813                                               struct netr_SamInfo3 **info3)
814 {
815         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
816         uint16 max_allowed_bad_attempts;
817         fstring name_domain, name_user;
818         struct dom_sid sid;
819         enum lsa_SidType type;
820         uchar new_nt_pass[NT_HASH_LEN];
821         const uint8 *cached_nt_pass;
822         const uint8 *cached_salt;
823         struct netr_SamInfo3 *my_info3;
824         time_t kickoff_time, must_change_time;
825         bool password_good = false;
826 #ifdef HAVE_KRB5
827         struct winbindd_tdc_domain *tdc_domain = NULL;
828 #endif
829
830         *info3 = NULL;
831
832         ZERO_STRUCTP(info3);
833
834         DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
835
836         /* Parse domain and username */
837
838         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
839
840
841         if (!lookup_cached_name(state->mem_ctx,
842                                 name_domain,
843                                 name_user,
844                                 &sid,
845                                 &type)) {
846                 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
847                 return NT_STATUS_NO_SUCH_USER;
848         }
849
850         if (type != SID_NAME_USER) {
851                 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
852                 return NT_STATUS_LOGON_FAILURE;
853         }
854
855         result = winbindd_get_creds(domain,
856                                     state->mem_ctx,
857                                     &sid,
858                                     &my_info3,
859                                     &cached_nt_pass,
860                                     &cached_salt);
861         if (!NT_STATUS_IS_OK(result)) {
862                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
863                 return result;
864         }
865
866         *info3 = my_info3;
867
868         E_md4hash(state->request->data.auth.pass, new_nt_pass);
869
870         dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
871         dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
872         if (cached_salt) {
873                 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
874         }
875
876         if (cached_salt) {
877                 /* In this case we didn't store the nt_hash itself,
878                    but the MD5 combination of salt + nt_hash. */
879                 uchar salted_hash[NT_HASH_LEN];
880                 E_md5hash(cached_salt, new_nt_pass, salted_hash);
881
882                 password_good = (memcmp(cached_nt_pass, salted_hash,
883                                         NT_HASH_LEN) == 0);
884         } else {
885                 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
886                 password_good = (memcmp(cached_nt_pass, new_nt_pass,
887                                         NT_HASH_LEN) == 0);
888         }
889
890         if (password_good) {
891
892                 /* User *DOES* know the password, update logon_time and reset
893                  * bad_pw_count */
894
895                 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
896
897                 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
898                         return NT_STATUS_ACCOUNT_LOCKED_OUT;
899                 }
900
901                 if (my_info3->base.acct_flags & ACB_DISABLED) {
902                         return NT_STATUS_ACCOUNT_DISABLED;
903                 }
904
905                 if (my_info3->base.acct_flags & ACB_WSTRUST) {
906                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
907                 }
908
909                 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
910                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
911                 }
912
913                 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
914                         return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
915                 }
916
917                 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
918                         DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
919                                 my_info3->base.acct_flags));
920                         return NT_STATUS_LOGON_FAILURE;
921                 }
922
923                 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
924                 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
925                         return NT_STATUS_ACCOUNT_EXPIRED;
926                 }
927
928                 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
929                 if (must_change_time != 0 && must_change_time < time(NULL)) {
930                         /* we allow grace logons when the password has expired */
931                         my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
932                         /* return NT_STATUS_PASSWORD_EXPIRED; */
933                         goto success;
934                 }
935
936 #ifdef HAVE_KRB5
937                 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
938                     ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
939                     ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
940                     /* used to cope with the case winbindd starting without network. */
941                     !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
942
943                         uid_t uid = -1;
944                         const char *cc = NULL;
945                         char *realm = NULL;
946                         const char *principal_s = NULL;
947                         const char *service = NULL;
948                         bool internal_ccache = false;
949
950                         uid = get_uid_from_state(state);
951                         if (uid == -1) {
952                                 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
953                                 return NT_STATUS_INVALID_PARAMETER;
954                         }
955
956                         cc = generate_krb5_ccache(state->mem_ctx,
957                                                 state->request->data.auth.krb5_cc_type,
958                                                 state->request->data.auth.uid,
959                                                 &internal_ccache);
960                         if (cc == NULL) {
961                                 return NT_STATUS_NO_MEMORY;
962                         }
963
964                         realm = domain->alt_name;
965                         strupper_m(realm);
966
967                         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
968                         if (principal_s == NULL) {
969                                 return NT_STATUS_NO_MEMORY;
970                         }
971
972                         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
973                         if (service == NULL) {
974                                 return NT_STATUS_NO_MEMORY;
975                         }
976
977                         if (!internal_ccache) {
978
979                                 setup_return_cc_name(state, cc);
980
981                                 result = add_ccache_to_list(principal_s,
982                                                             cc,
983                                                             service,
984                                                             state->request->data.auth.user,
985                                                             domain->alt_name,
986                                                             uid,
987                                                             time(NULL),
988                                                             time(NULL) + lp_winbind_cache_time(),
989                                                             time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
990                                                             true);
991
992                                 if (!NT_STATUS_IS_OK(result)) {
993                                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
994                                                 "to add ccache to list: %s\n",
995                                                 nt_errstr(result)));
996                                 }
997                         }
998                 }
999 #endif /* HAVE_KRB5 */
1000  success:
1001                 /* FIXME: we possibly should handle logon hours as well (does xp when
1002                  * offline?) see auth/auth_sam.c:sam_account_ok for details */
1003
1004                 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1005                 my_info3->base.bad_password_count = 0;
1006
1007                 result = winbindd_update_creds_by_info3(domain,
1008                                                         state->mem_ctx,
1009                                                         state->request->data.auth.user,
1010                                                         state->request->data.auth.pass,
1011                                                         my_info3);
1012                 if (!NT_STATUS_IS_OK(result)) {
1013                         DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1014                                 nt_errstr(result)));
1015                         return result;
1016                 }
1017
1018                 return NT_STATUS_OK;
1019
1020         }
1021
1022         /* User does *NOT* know the correct password, modify info3 accordingly */
1023
1024         /* failure of this is not critical */
1025         result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1026         if (!NT_STATUS_IS_OK(result)) {
1027                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1028                           "Won't be able to honour account lockout policies\n"));
1029         }
1030
1031         /* increase counter */
1032         my_info3->base.bad_password_count++;
1033
1034         if (max_allowed_bad_attempts == 0) {
1035                 goto failed;
1036         }
1037
1038         /* lockout user */
1039         if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1040
1041                 uint32 password_properties;
1042
1043                 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1044                 if (!NT_STATUS_IS_OK(result)) {
1045                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1046                 }
1047
1048                 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1049                     (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1050                         my_info3->base.acct_flags |= ACB_AUTOLOCK;
1051                 }
1052         }
1053
1054 failed:
1055         result = winbindd_update_creds_by_info3(domain,
1056                                                 state->mem_ctx,
1057                                                 state->request->data.auth.user,
1058                                                 NULL,
1059                                                 my_info3);
1060
1061         if (!NT_STATUS_IS_OK(result)) {
1062                 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1063                         nt_errstr(result)));
1064         }
1065
1066         return NT_STATUS_LOGON_FAILURE;
1067 }
1068
1069 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1070                                                 struct winbindd_cli_state *state,
1071                                                 struct netr_SamInfo3 **info3)
1072 {
1073         struct winbindd_domain *contact_domain;
1074         fstring name_domain, name_user;
1075         NTSTATUS result;
1076
1077         DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1078
1079         /* Parse domain and username */
1080
1081         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1082
1083         /* what domain should we contact? */
1084
1085         if ( IS_DC ) {
1086                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1087                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1088                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1089                         result = NT_STATUS_NO_SUCH_USER;
1090                         goto done;
1091                 }
1092
1093         } else {
1094                 if (is_myname(name_domain)) {
1095                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1096                         result =  NT_STATUS_NO_SUCH_USER;
1097                         goto done;
1098                 }
1099
1100                 contact_domain = find_domain_from_name(name_domain);
1101                 if (contact_domain == NULL) {
1102                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1103                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1104
1105                         contact_domain = find_our_domain();
1106                 }
1107         }
1108
1109         if (contact_domain->initialized &&
1110             contact_domain->active_directory) {
1111                 goto try_login;
1112         }
1113
1114         if (!contact_domain->initialized) {
1115                 init_dc_connection(contact_domain);
1116         }
1117
1118         if (!contact_domain->active_directory) {
1119                 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1120                 return NT_STATUS_INVALID_LOGON_TYPE;
1121         }
1122 try_login:
1123         result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1124 done:
1125         return result;
1126 }
1127
1128 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1129                                           const char *domain, const char *user,
1130                                           const DATA_BLOB *challenge,
1131                                           const DATA_BLOB *lm_resp,
1132                                           const DATA_BLOB *nt_resp,
1133                                           struct netr_SamInfo3 **pinfo3)
1134 {
1135         struct auth_usersupplied_info *user_info = NULL;
1136         NTSTATUS status;
1137
1138         status = make_user_info(&user_info, user, user, domain, domain,
1139                                 global_myname(), lm_resp, nt_resp, NULL, NULL,
1140                                 NULL, AUTH_PASSWORD_RESPONSE);
1141         if (!NT_STATUS_IS_OK(status)) {
1142                 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1143                 return status;
1144         }
1145
1146         /* We don't want any more mapping of the username */
1147         user_info->mapped_state = True;
1148
1149         status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1150                                           pinfo3);
1151         free_user_info(&user_info);
1152         DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1153         return NT_STATUS_OK;
1154 }
1155
1156 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1157                                   TALLOC_CTX *mem_ctx,
1158                                   uint32 logon_parameters,
1159                                   const char *server,
1160                                   const char *username,
1161                                   const char *domain,
1162                                   const char *workstation,
1163                                   const uint8 chal[8],
1164                                   DATA_BLOB lm_response,
1165                                   DATA_BLOB nt_response,
1166                                   struct netr_SamInfo3 **info3);
1167
1168 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1169                                                 struct winbindd_cli_state *state,
1170                                                 struct netr_SamInfo3 **info3)
1171 {
1172
1173         struct rpc_pipe_client *netlogon_pipe;
1174         uchar chal[8];
1175         DATA_BLOB lm_resp;
1176         DATA_BLOB nt_resp;
1177         int attempts = 0;
1178         unsigned char local_lm_response[24];
1179         unsigned char local_nt_response[24];
1180         fstring name_domain, name_user;
1181         bool retry;
1182         NTSTATUS result;
1183         struct netr_SamInfo3 *my_info3 = NULL;
1184
1185         *info3 = NULL;
1186
1187         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1188
1189         /* Parse domain and username */
1190
1191         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1192
1193         /* do password magic */
1194
1195         generate_random_buffer(chal, sizeof(chal));
1196
1197         if (lp_client_ntlmv2_auth()) {
1198                 DATA_BLOB server_chal;
1199                 DATA_BLOB names_blob;
1200                 DATA_BLOB nt_response;
1201                 DATA_BLOB lm_response;
1202                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1203
1204                 /* note that the 'workgroup' here is a best guess - we don't know
1205                    the server's domain at this point.  The 'server name' is also
1206                    dodgy...
1207                 */
1208                 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1209
1210                 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1211                                       state->request->data.auth.pass,
1212                                       &server_chal,
1213                                       &names_blob,
1214                                       &lm_response, &nt_response, NULL, NULL)) {
1215                         data_blob_free(&names_blob);
1216                         data_blob_free(&server_chal);
1217                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1218                         result = NT_STATUS_NO_MEMORY;
1219                         goto done;
1220                 }
1221                 data_blob_free(&names_blob);
1222                 data_blob_free(&server_chal);
1223                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1224                                            lm_response.length);
1225                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1226                                            nt_response.length);
1227                 data_blob_free(&lm_response);
1228                 data_blob_free(&nt_response);
1229
1230         } else {
1231                 if (lp_client_lanman_auth()
1232                     && SMBencrypt(state->request->data.auth.pass,
1233                                   chal,
1234                                   local_lm_response)) {
1235                         lm_resp = data_blob_talloc(state->mem_ctx,
1236                                                    local_lm_response,
1237                                                    sizeof(local_lm_response));
1238                 } else {
1239                         lm_resp = data_blob_null;
1240                 }
1241                 SMBNTencrypt(state->request->data.auth.pass,
1242                              chal,
1243                              local_nt_response);
1244
1245                 nt_resp = data_blob_talloc(state->mem_ctx,
1246                                            local_nt_response,
1247                                            sizeof(local_nt_response));
1248         }
1249
1250         if (strequal(name_domain, get_global_sam_name())) {
1251                 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1252
1253                 result = winbindd_dual_auth_passdb(
1254                         state->mem_ctx, name_domain, name_user,
1255                         &chal_blob, &lm_resp, &nt_resp, info3);
1256                 goto done;
1257         }
1258
1259         /* check authentication loop */
1260
1261         do {
1262                 netlogon_fn_t logon_fn;
1263
1264                 ZERO_STRUCTP(my_info3);
1265                 retry = false;
1266
1267                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1268
1269                 if (!NT_STATUS_IS_OK(result)) {
1270                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1271                         goto done;
1272                 }
1273
1274                 /* It is really important to try SamLogonEx here,
1275                  * because in a clustered environment, we want to use
1276                  * one machine account from multiple physical
1277                  * computers.
1278                  *
1279                  * With a normal SamLogon call, we must keep the
1280                  * credentials chain updated and intact between all
1281                  * users of the machine account (which would imply
1282                  * cross-node communication for every NTLM logon).
1283                  *
1284                  * (The credentials chain is not per NETLOGON pipe
1285                  * connection, but globally on the server/client pair
1286                  * by machine name).
1287                  *
1288                  * When using SamLogonEx, the credentials are not
1289                  * supplied, but the session key is implied by the
1290                  * wrapping SamLogon context.
1291                  *
1292                  *  -- abartlet 21 April 2008
1293                  */
1294
1295                 logon_fn = domain->can_do_samlogon_ex
1296                         ? rpccli_netlogon_sam_network_logon_ex
1297                         : rpccli_netlogon_sam_network_logon;
1298
1299                 result = logon_fn(netlogon_pipe,
1300                                   state->mem_ctx,
1301                                   0,
1302                                   domain->dcname,         /* server name */
1303                                   name_user,              /* user name */
1304                                   name_domain,            /* target domain */
1305                                   global_myname(),        /* workstation */
1306                                   chal,
1307                                   lm_resp,
1308                                   nt_resp,
1309                                   &my_info3);
1310                 attempts += 1;
1311
1312                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1313                     && domain->can_do_samlogon_ex) {
1314                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1315                                   "retrying with NetSamLogon\n"));
1316                         domain->can_do_samlogon_ex = false;
1317                         retry = true;
1318                         continue;
1319                 }
1320
1321                 /* We have to try a second time as cm_connect_netlogon
1322                    might not yet have noticed that the DC has killed
1323                    our connection. */
1324
1325                 if (!rpccli_is_connected(netlogon_pipe)) {
1326                         retry = true;
1327                         continue;
1328                 }
1329
1330                 /* if we get access denied, a possible cause was that we had
1331                    and open connection to the DC, but someone changed our
1332                    machine account password out from underneath us using 'net
1333                    rpc changetrustpw' */
1334
1335                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1336                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1337                                  "ACCESS_DENIED.  Maybe the trust account "
1338                                 "password was changed and we didn't know it. "
1339                                  "Killing connections to domain %s\n",
1340                                 name_domain));
1341                         invalidate_cm_connection(&domain->conn);
1342                         retry = true;
1343                 }
1344
1345         } while ( (attempts < 2) && retry );
1346
1347         /* handle the case where a NT4 DC does not fill in the acct_flags in
1348          * the samlogon reply info3. When accurate info3 is required by the
1349          * caller, we look up the account flags ourselve - gd */
1350
1351         if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1352             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1353
1354                 struct rpc_pipe_client *samr_pipe;
1355                 struct policy_handle samr_domain_handle, user_pol;
1356                 union samr_UserInfo *info = NULL;
1357                 NTSTATUS status_tmp;
1358                 uint32 acct_flags;
1359
1360                 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1361                                             &samr_pipe, &samr_domain_handle);
1362
1363                 if (!NT_STATUS_IS_OK(status_tmp)) {
1364                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1365                                 nt_errstr(status_tmp)));
1366                         goto done;
1367                 }
1368
1369                 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1370                                                   &samr_domain_handle,
1371                                                   MAXIMUM_ALLOWED_ACCESS,
1372                                                   my_info3->base.rid,
1373                                                   &user_pol);
1374
1375                 if (!NT_STATUS_IS_OK(status_tmp)) {
1376                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1377                                 nt_errstr(status_tmp)));
1378                         goto done;
1379                 }
1380
1381                 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1382                                                        &user_pol,
1383                                                        16,
1384                                                        &info);
1385
1386                 if (!NT_STATUS_IS_OK(status_tmp)) {
1387                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1388                                 nt_errstr(status_tmp)));
1389                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1390                         goto done;
1391                 }
1392
1393                 acct_flags = info->info16.acct_flags;
1394
1395                 if (acct_flags == 0) {
1396                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1397                         goto done;
1398                 }
1399
1400                 my_info3->base.acct_flags = acct_flags;
1401
1402                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1403
1404                 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1405         }
1406
1407         *info3 = my_info3;
1408 done:
1409         return result;
1410 }
1411
1412 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1413                                             struct winbindd_cli_state *state)
1414 {
1415         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1416         NTSTATUS krb5_result = NT_STATUS_OK;
1417         fstring name_domain, name_user;
1418         char *mapped_user;
1419         fstring domain_user;
1420         struct netr_SamInfo3 *info3 = NULL;
1421         NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1422
1423         /* Ensure null termination */
1424         state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1425
1426         /* Ensure null termination */
1427         state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1428
1429         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1430                   state->request->data.auth.user));
1431
1432         /* Parse domain and username */
1433
1434         name_map_status = normalize_name_unmap(state->mem_ctx,
1435                                                state->request->data.auth.user,
1436                                                &mapped_user);
1437
1438         /* If the name normalization didnt' actually do anything,
1439            just use the original name */
1440
1441         if (!NT_STATUS_IS_OK(name_map_status) &&
1442             !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1443         {
1444                 mapped_user = state->request->data.auth.user;
1445         }
1446
1447         parse_domain_user(mapped_user, name_domain, name_user);
1448
1449         if ( mapped_user != state->request->data.auth.user ) {
1450                 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1451                         *lp_winbind_separator(),
1452                         name_user );
1453                 safe_strcpy( state->request->data.auth.user, domain_user,
1454                              sizeof(state->request->data.auth.user)-1 );
1455         }
1456
1457         if (!domain->online) {
1458                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1459                 if (domain->startup) {
1460                         /* Logons are very important to users. If we're offline and
1461                            we get a request within the first 30 seconds of startup,
1462                            try very hard to find a DC and go online. */
1463
1464                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1465                                 "request in startup mode.\n", domain->name ));
1466
1467                         winbindd_flush_negative_conn_cache(domain);
1468                         result = init_dc_connection(domain);
1469                 }
1470         }
1471
1472         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1473
1474         /* Check for Kerberos authentication */
1475         if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1476
1477                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1478                 /* save for later */
1479                 krb5_result = result;
1480
1481
1482                 if (NT_STATUS_IS_OK(result)) {
1483                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1484                         goto process_result;
1485                 } else {
1486                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1487                 }
1488
1489                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1490                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1491                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1492                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1493                         set_domain_offline( domain );
1494                         goto cached_logon;
1495                 }
1496
1497                 /* there are quite some NT_STATUS errors where there is no
1498                  * point in retrying with a samlogon, we explictly have to take
1499                  * care not to increase the bad logon counter on the DC */
1500
1501                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1502                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1503                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1504                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1505                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1506                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1507                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1508                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1509                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1510                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1511                         goto done;
1512                 }
1513
1514                 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1515                         DEBUG(3,("falling back to samlogon\n"));
1516                         goto sam_logon;
1517                 } else {
1518                         goto cached_logon;
1519                 }
1520         }
1521
1522 sam_logon:
1523         /* Check for Samlogon authentication */
1524         if (domain->online) {
1525                 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1526
1527                 if (NT_STATUS_IS_OK(result)) {
1528                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1529                         /* add the Krb5 err if we have one */
1530                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1531                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1532                         }
1533                         goto process_result;
1534                 }
1535
1536                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1537                           nt_errstr(result)));
1538
1539                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1540                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1541                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1542                 {
1543                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1544                         set_domain_offline( domain );
1545                         goto cached_logon;
1546                 }
1547
1548                         if (domain->online) {
1549                                 /* We're still online - fail. */
1550                                 goto done;
1551                         }
1552         }
1553
1554 cached_logon:
1555         /* Check for Cached logons */
1556         if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1557             lp_winbind_offline_logon()) {
1558
1559                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1560
1561                 if (NT_STATUS_IS_OK(result)) {
1562                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1563                         goto process_result;
1564                 } else {
1565                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1566                         goto done;
1567                 }
1568         }
1569
1570 process_result:
1571
1572         if (NT_STATUS_IS_OK(result)) {
1573
1574                 struct dom_sid user_sid;
1575
1576                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1577                    been initialized. */
1578                 if (!info3) {
1579                         result = NT_STATUS_INTERNAL_ERROR;
1580                         goto done;
1581                 }
1582
1583                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1584                 netsamlogon_cache_store(name_user, info3);
1585
1586                 /* save name_to_sid info as early as possible (only if
1587                    this is our primary domain so we don't invalidate
1588                    the cache entry by storing the seq_num for the wrong
1589                    domain). */
1590                 if ( domain->primary ) {
1591                         sid_compose(&user_sid, info3->base.domain_sid,
1592                                     info3->base.rid);
1593                         cache_name2sid(domain, name_domain, name_user,
1594                                        SID_NAME_USER, &user_sid);
1595                 }
1596
1597                 /* Check if the user is in the right group */
1598
1599                 result = check_info3_in_group(
1600                         info3,
1601                         state->request->data.auth.require_membership_of_sid);
1602                 if (!NT_STATUS_IS_OK(result)) {
1603                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1604                                   state->request->data.auth.user,
1605                                   state->request->data.auth.require_membership_of_sid));
1606                         goto done;
1607                 }
1608
1609                 result = append_auth_data(state, info3, name_domain,
1610                                           name_user);
1611                 if (!NT_STATUS_IS_OK(result)) {
1612                         goto done;
1613                 }
1614
1615                 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1616
1617                         if (lp_winbind_offline_logon()) {
1618                                 result = winbindd_store_creds(domain,
1619                                                       state->mem_ctx,
1620                                                       state->request->data.auth.user,
1621                                                       state->request->data.auth.pass,
1622                                                       info3);
1623                         }
1624                 }
1625
1626
1627                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1628                         struct winbindd_domain *our_domain = find_our_domain();
1629
1630                         /* This is not entirely correct I believe, but it is
1631                            consistent.  Only apply the password policy settings
1632                            too warn users for our own domain.  Cannot obtain these
1633                            from trusted DCs all the  time so don't do it at all.
1634                            -- jerry */
1635
1636                         result = NT_STATUS_NOT_SUPPORTED;
1637                         if (our_domain == domain ) {
1638                                 result = fillup_password_policy(our_domain, state);
1639                         }
1640
1641                         if (!NT_STATUS_IS_OK(result)
1642                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1643                         {
1644                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1645                                           domain->name, nt_errstr(result)));
1646                                 goto done;
1647                         }
1648                 }
1649
1650                 result = NT_STATUS_OK;
1651         }
1652
1653 done:
1654         /* give us a more useful (more correct?) error code */
1655         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1656             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1657                 result = NT_STATUS_NO_LOGON_SERVERS;
1658         }
1659
1660         set_auth_errors(state->response, result);
1661
1662         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1663               state->request->data.auth.user,
1664               state->response->data.auth.nt_status_string,
1665               state->response->data.auth.pam_error));
1666
1667         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1668 }
1669
1670 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1671                                                  struct winbindd_cli_state *state)
1672 {
1673         NTSTATUS result;
1674         struct netr_SamInfo3 *info3 = NULL;
1675         struct rpc_pipe_client *netlogon_pipe;
1676         const char *name_user = NULL;
1677         const char *name_domain = NULL;
1678         const char *workstation;
1679         int attempts = 0;
1680         bool retry;
1681
1682         DATA_BLOB lm_resp, nt_resp;
1683
1684         /* This is child-only, so no check for privileged access is needed
1685            anymore */
1686
1687         /* Ensure null termination */
1688         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1689         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1690
1691         name_user = state->request->data.auth_crap.user;
1692
1693         if (*state->request->data.auth_crap.domain) {
1694                 name_domain = state->request->data.auth_crap.domain;
1695         } else if (lp_winbind_use_default_domain()) {
1696                 name_domain = lp_workgroup();
1697         } else {
1698                 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1699                          name_user));
1700                 result = NT_STATUS_NO_SUCH_USER;
1701                 goto done;
1702         }
1703
1704         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1705                   name_domain, name_user));
1706
1707         if (*state->request->data.auth_crap.workstation) {
1708                 workstation = state->request->data.auth_crap.workstation;
1709         } else {
1710                 workstation = global_myname();
1711         }
1712
1713         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1714                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1715                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1716                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1717                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1718                                   state->request->data.auth_crap.lm_resp_len,
1719                                   state->request->data.auth_crap.nt_resp_len));
1720                         result = NT_STATUS_INVALID_PARAMETER;
1721                         goto done;
1722                 }
1723         }
1724
1725         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1726                                         state->request->data.auth_crap.lm_resp_len);
1727
1728         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1729                 nt_resp = data_blob_talloc(state->mem_ctx,
1730                                            state->request->extra_data.data,
1731                                            state->request->data.auth_crap.nt_resp_len);
1732         } else {
1733                 nt_resp = data_blob_talloc(state->mem_ctx,
1734                                            state->request->data.auth_crap.nt_resp,
1735                                            state->request->data.auth_crap.nt_resp_len);
1736         }
1737
1738         if (strequal(name_domain, get_global_sam_name())) {
1739                 DATA_BLOB chal_blob = data_blob_const(
1740                         state->request->data.auth_crap.chal,
1741                         sizeof(state->request->data.auth_crap.chal));
1742
1743                 result = winbindd_dual_auth_passdb(
1744                         state->mem_ctx, name_domain, name_user,
1745                         &chal_blob, &lm_resp, &nt_resp, &info3);
1746                 goto process_result;
1747         }
1748
1749         do {
1750                 netlogon_fn_t logon_fn;
1751
1752                 retry = false;
1753
1754                 netlogon_pipe = NULL;
1755                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1756
1757                 if (!NT_STATUS_IS_OK(result)) {
1758                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1759                                   nt_errstr(result)));
1760                         goto done;
1761                 }
1762
1763                 logon_fn = domain->can_do_samlogon_ex
1764                         ? rpccli_netlogon_sam_network_logon_ex
1765                         : rpccli_netlogon_sam_network_logon;
1766
1767                 result = logon_fn(netlogon_pipe,
1768                                   state->mem_ctx,
1769                                   state->request->data.auth_crap.logon_parameters,
1770                                   domain->dcname,
1771                                   name_user,
1772                                   name_domain,
1773                                   /* Bug #3248 - found by Stefan Burkei. */
1774                                   workstation, /* We carefully set this above so use it... */
1775                                   state->request->data.auth_crap.chal,
1776                                   lm_resp,
1777                                   nt_resp,
1778                                   &info3);
1779
1780                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1781                     && domain->can_do_samlogon_ex) {
1782                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1783                                   "retrying with NetSamLogon\n"));
1784                         domain->can_do_samlogon_ex = false;
1785                         retry = true;
1786                         continue;
1787                 }
1788
1789                 attempts += 1;
1790
1791                 /* We have to try a second time as cm_connect_netlogon
1792                    might not yet have noticed that the DC has killed
1793                    our connection. */
1794
1795                 if (!rpccli_is_connected(netlogon_pipe)) {
1796                         retry = true;
1797                         continue;
1798                 }
1799
1800                 /* if we get access denied, a possible cause was that we had and open
1801                    connection to the DC, but someone changed our machine account password
1802                    out from underneath us using 'net rpc changetrustpw' */
1803
1804                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1805                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1806                                  "ACCESS_DENIED.  Maybe the trust account "
1807                                 "password was changed and we didn't know it. "
1808                                  "Killing connections to domain %s\n",
1809                                 name_domain));
1810                         invalidate_cm_connection(&domain->conn);
1811                         retry = true;
1812                 }
1813
1814         } while ( (attempts < 2) && retry );
1815
1816 process_result:
1817
1818         if (NT_STATUS_IS_OK(result)) {
1819
1820                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1821                 netsamlogon_cache_store(name_user, info3);
1822
1823                 /* Check if the user is in the right group */
1824
1825                 result = check_info3_in_group(
1826                         info3,
1827                         state->request->data.auth_crap.require_membership_of_sid);
1828                 if (!NT_STATUS_IS_OK(result)) {
1829                         DEBUG(3, ("User %s is not in the required group (%s), so "
1830                                   "crap authentication is rejected\n",
1831                                   state->request->data.auth_crap.user,
1832                                   state->request->data.auth_crap.require_membership_of_sid));
1833                         goto done;
1834                 }
1835
1836                 result = append_auth_data(state, info3, name_domain,
1837                                           name_user);
1838                 if (!NT_STATUS_IS_OK(result)) {
1839                         goto done;
1840                 }
1841         }
1842
1843 done:
1844
1845         /* give us a more useful (more correct?) error code */
1846         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1847             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1848                 result = NT_STATUS_NO_LOGON_SERVERS;
1849         }
1850
1851         if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1852                 result = nt_status_squash(result);
1853         }
1854
1855         set_auth_errors(state->response, result);
1856
1857         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1858               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1859                name_domain,
1860                name_user,
1861                state->response->data.auth.nt_status_string,
1862                state->response->data.auth.pam_error));
1863
1864         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1865 }
1866
1867 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1868                                                  struct winbindd_cli_state *state)
1869 {
1870         char *oldpass;
1871         char *newpass = NULL;
1872         struct policy_handle dom_pol;
1873         struct rpc_pipe_client *cli = NULL;
1874         bool got_info = false;
1875         struct samr_DomInfo1 *info = NULL;
1876         struct userPwdChangeFailureInformation *reject = NULL;
1877         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1878         fstring domain, user;
1879
1880         ZERO_STRUCT(dom_pol);
1881
1882         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1883                   state->request->data.auth.user));
1884
1885         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1886                 goto done;
1887         }
1888
1889         /* Change password */
1890
1891         oldpass = state->request->data.chauthtok.oldpass;
1892         newpass = state->request->data.chauthtok.newpass;
1893
1894         /* Initialize reject reason */
1895         state->response->data.auth.reject_reason = Undefined;
1896
1897         /* Get sam handle */
1898
1899         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1900                                 &dom_pol);
1901         if (!NT_STATUS_IS_OK(result)) {
1902                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1903                 goto done;
1904         }
1905
1906         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1907                                              user,
1908                                              newpass,
1909                                              oldpass,
1910                                              &info,
1911                                              &reject);
1912
1913         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1914
1915         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1916
1917                 fill_in_password_policy(state->response, info);
1918
1919                 state->response->data.auth.reject_reason =
1920                         reject->extendedFailureReason;
1921
1922                 got_info = true;
1923         }
1924
1925         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1926          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1927          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1928          * short to comply with the samr_ChangePasswordUser3 idl - gd */
1929
1930         /* only fallback when the chgpasswd_user3 call is not supported */
1931         if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1932                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1933                    (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1934                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1935
1936                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1937                         nt_errstr(result)));
1938
1939                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1940
1941                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1942                    Map to the same status code as Windows 2003. */
1943
1944                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1945                         result = NT_STATUS_PASSWORD_RESTRICTION;
1946                 }
1947         }
1948
1949 done:
1950
1951         if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1952                 if (lp_winbind_offline_logon()) {
1953                         result = winbindd_update_creds_by_name(contact_domain,
1954                                                          state->mem_ctx, user,
1955                                                          newpass);
1956                         /* Again, this happens when we login from gdm or xdm
1957                          * and the password expires, *BUT* cached crendentials
1958                          * doesn't exist. winbindd_update_creds_by_name()
1959                          * returns NT_STATUS_NO_SUCH_USER.
1960                          * This is not a failure.
1961                          * --- BoYang
1962                          * */
1963                         if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1964                                 result = NT_STATUS_OK;
1965                         }
1966
1967                         if (!NT_STATUS_IS_OK(result)) {
1968                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1969                                 goto process_result;
1970                         }
1971                 }
1972         }
1973
1974         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1975
1976                 NTSTATUS policy_ret;
1977
1978                 policy_ret = fillup_password_policy(contact_domain, state);
1979
1980                 /* failure of this is non critical, it will just provide no
1981                  * additional information to the client why the change has
1982                  * failed - Guenther */
1983
1984                 if (!NT_STATUS_IS_OK(policy_ret)) {
1985                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1986                         goto process_result;
1987                 }
1988         }
1989
1990 process_result:
1991
1992         if (strequal(contact_domain->name, get_global_sam_name())) {
1993                 /* FIXME: internal rpc pipe does not cache handles yet */
1994                 if (cli) {
1995                         if (is_valid_policy_hnd(&dom_pol)) {
1996                                 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1997                         }
1998                         TALLOC_FREE(cli);
1999                 }
2000         }
2001
2002         set_auth_errors(state->response, result);
2003
2004         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2005               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2006                domain,
2007                user,
2008                state->response->data.auth.nt_status_string,
2009                state->response->data.auth.pam_error));
2010
2011         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2012 }
2013
2014 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2015                                               struct winbindd_cli_state *state)
2016 {
2017         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2018
2019         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2020                 state->request->data.logoff.user));
2021
2022         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2023                 result = NT_STATUS_OK;
2024                 goto process_result;
2025         }
2026
2027         if (state->request->data.logoff.krb5ccname[0] == '\0') {
2028                 result = NT_STATUS_OK;
2029                 goto process_result;
2030         }
2031
2032 #ifdef HAVE_KRB5
2033
2034         if (state->request->data.logoff.uid < 0) {
2035                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2036                 goto process_result;
2037         }
2038
2039         /* what we need here is to find the corresponding krb5 ccache name *we*
2040          * created for a given username and destroy it */
2041
2042         if (!ccache_entry_exists(state->request->data.logoff.user)) {
2043                 result = NT_STATUS_OK;
2044                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2045                 goto process_result;
2046         }
2047
2048         if (!ccache_entry_identical(state->request->data.logoff.user,
2049                                         state->request->data.logoff.uid,
2050                                         state->request->data.logoff.krb5ccname)) {
2051                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2052                 goto process_result;
2053         }
2054
2055         result = remove_ccache(state->request->data.logoff.user);
2056         if (!NT_STATUS_IS_OK(result)) {
2057                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2058                         nt_errstr(result)));
2059                 goto process_result;
2060         }
2061
2062 #else
2063         result = NT_STATUS_NOT_SUPPORTED;
2064 #endif
2065
2066 process_result:
2067
2068
2069         set_auth_errors(state->response, result);
2070
2071         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2072 }
2073
2074 /* Change user password with auth crap*/
2075
2076 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2077 {
2078         NTSTATUS result;
2079         DATA_BLOB new_nt_password;
2080         DATA_BLOB old_nt_hash_enc;
2081         DATA_BLOB new_lm_password;
2082         DATA_BLOB old_lm_hash_enc;
2083         fstring  domain,user;
2084         struct policy_handle dom_pol;
2085         struct winbindd_domain *contact_domain = domainSt;
2086         struct rpc_pipe_client *cli = NULL;
2087
2088         ZERO_STRUCT(dom_pol);
2089
2090         /* Ensure null termination */
2091         state->request->data.chng_pswd_auth_crap.user[
2092                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2093         state->request->data.chng_pswd_auth_crap.domain[
2094                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2095         *domain = 0;
2096         *user = 0;
2097
2098         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2099                   (unsigned long)state->pid,
2100                   state->request->data.chng_pswd_auth_crap.domain,
2101                   state->request->data.chng_pswd_auth_crap.user));
2102
2103         if (lp_winbind_offline_logon()) {
2104                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2105                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2106                 result = NT_STATUS_ACCESS_DENIED;
2107                 goto done;
2108         }
2109
2110         if (*state->request->data.chng_pswd_auth_crap.domain) {
2111                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2112         } else {
2113                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2114                                   domain, user);
2115
2116                 if(!*domain) {
2117                         DEBUG(3,("no domain specified with username (%s) - "
2118                                  "failing auth\n",
2119                                  state->request->data.chng_pswd_auth_crap.user));
2120                         result = NT_STATUS_NO_SUCH_USER;
2121                         goto done;
2122                 }
2123         }
2124
2125         if (!*domain && lp_winbind_use_default_domain()) {
2126                 fstrcpy(domain,(char *)lp_workgroup());
2127         }
2128
2129         if(!*user) {
2130                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2131         }
2132
2133         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2134                   (unsigned long)state->pid, domain, user));
2135
2136         /* Change password */
2137         new_nt_password = data_blob_const(
2138                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2139                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2140
2141         old_nt_hash_enc = data_blob_const(
2142                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2143                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2144
2145         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2146                 new_lm_password = data_blob_const(
2147                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2148                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2149
2150                 old_lm_hash_enc = data_blob_const(
2151                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2152                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2153         } else {
2154                 new_lm_password.length = 0;
2155                 old_lm_hash_enc.length = 0;
2156         }
2157
2158         /* Get sam handle */
2159
2160         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2161         if (!NT_STATUS_IS_OK(result)) {
2162                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2163                 goto done;
2164         }
2165
2166         result = rpccli_samr_chng_pswd_auth_crap(
2167                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2168                 new_lm_password, old_lm_hash_enc);
2169
2170  done:
2171
2172         if (strequal(contact_domain->name, get_global_sam_name())) {
2173                 /* FIXME: internal rpc pipe does not cache handles yet */
2174                 if (cli) {
2175                         if (is_valid_policy_hnd(&dom_pol)) {
2176                                 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2177                         }
2178                         TALLOC_FREE(cli);
2179                 }
2180         }
2181
2182         set_auth_errors(state->response, result);
2183
2184         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2185               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2186                domain, user,
2187                state->response->data.auth.nt_status_string,
2188                state->response->data.auth.pam_error));
2189
2190         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2191 }