s3: Remove a nested if-statement
[abartlet/samba.git/.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                     && lp_winbind_offline_logon()) {
1617
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                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1626                         struct winbindd_domain *our_domain = find_our_domain();
1627
1628                         /* This is not entirely correct I believe, but it is
1629                            consistent.  Only apply the password policy settings
1630                            too warn users for our own domain.  Cannot obtain these
1631                            from trusted DCs all the  time so don't do it at all.
1632                            -- jerry */
1633
1634                         result = NT_STATUS_NOT_SUPPORTED;
1635                         if (our_domain == domain ) {
1636                                 result = fillup_password_policy(our_domain, state);
1637                         }
1638
1639                         if (!NT_STATUS_IS_OK(result)
1640                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1641                         {
1642                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1643                                           domain->name, nt_errstr(result)));
1644                                 goto done;
1645                         }
1646                 }
1647
1648                 result = NT_STATUS_OK;
1649         }
1650
1651 done:
1652         /* give us a more useful (more correct?) error code */
1653         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1654             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1655                 result = NT_STATUS_NO_LOGON_SERVERS;
1656         }
1657
1658         set_auth_errors(state->response, result);
1659
1660         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1661               state->request->data.auth.user,
1662               state->response->data.auth.nt_status_string,
1663               state->response->data.auth.pam_error));
1664
1665         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1666 }
1667
1668 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1669                                                  struct winbindd_cli_state *state)
1670 {
1671         NTSTATUS result;
1672         struct netr_SamInfo3 *info3 = NULL;
1673         struct rpc_pipe_client *netlogon_pipe;
1674         const char *name_user = NULL;
1675         const char *name_domain = NULL;
1676         const char *workstation;
1677         int attempts = 0;
1678         bool retry;
1679
1680         DATA_BLOB lm_resp, nt_resp;
1681
1682         /* This is child-only, so no check for privileged access is needed
1683            anymore */
1684
1685         /* Ensure null termination */
1686         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1687         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1688
1689         name_user = state->request->data.auth_crap.user;
1690         name_domain = state->request->data.auth_crap.domain;
1691         workstation = state->request->data.auth_crap.workstation;
1692
1693         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1694                   name_domain, name_user));
1695
1696         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1697                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1698                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1699                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1700                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1701                                   state->request->data.auth_crap.lm_resp_len,
1702                                   state->request->data.auth_crap.nt_resp_len));
1703                         result = NT_STATUS_INVALID_PARAMETER;
1704                         goto done;
1705                 }
1706         }
1707
1708         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1709                                         state->request->data.auth_crap.lm_resp_len);
1710
1711         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1712                 nt_resp = data_blob_talloc(state->mem_ctx,
1713                                            state->request->extra_data.data,
1714                                            state->request->data.auth_crap.nt_resp_len);
1715         } else {
1716                 nt_resp = data_blob_talloc(state->mem_ctx,
1717                                            state->request->data.auth_crap.nt_resp,
1718                                            state->request->data.auth_crap.nt_resp_len);
1719         }
1720
1721         if (strequal(name_domain, get_global_sam_name())) {
1722                 DATA_BLOB chal_blob = data_blob_const(
1723                         state->request->data.auth_crap.chal,
1724                         sizeof(state->request->data.auth_crap.chal));
1725
1726                 result = winbindd_dual_auth_passdb(
1727                         state->mem_ctx, name_domain, name_user,
1728                         &chal_blob, &lm_resp, &nt_resp, &info3);
1729                 goto process_result;
1730         }
1731
1732         do {
1733                 netlogon_fn_t logon_fn;
1734
1735                 retry = false;
1736
1737                 netlogon_pipe = NULL;
1738                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1739
1740                 if (!NT_STATUS_IS_OK(result)) {
1741                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1742                                   nt_errstr(result)));
1743                         goto done;
1744                 }
1745
1746                 logon_fn = domain->can_do_samlogon_ex
1747                         ? rpccli_netlogon_sam_network_logon_ex
1748                         : rpccli_netlogon_sam_network_logon;
1749
1750                 result = logon_fn(netlogon_pipe,
1751                                   state->mem_ctx,
1752                                   state->request->data.auth_crap.logon_parameters,
1753                                   domain->dcname,
1754                                   name_user,
1755                                   name_domain,
1756                                   /* Bug #3248 - found by Stefan Burkei. */
1757                                   workstation, /* We carefully set this above so use it... */
1758                                   state->request->data.auth_crap.chal,
1759                                   lm_resp,
1760                                   nt_resp,
1761                                   &info3);
1762
1763                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1764                     && domain->can_do_samlogon_ex) {
1765                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1766                                   "retrying with NetSamLogon\n"));
1767                         domain->can_do_samlogon_ex = false;
1768                         retry = true;
1769                         continue;
1770                 }
1771
1772                 attempts += 1;
1773
1774                 /* We have to try a second time as cm_connect_netlogon
1775                    might not yet have noticed that the DC has killed
1776                    our connection. */
1777
1778                 if (!rpccli_is_connected(netlogon_pipe)) {
1779                         retry = true;
1780                         continue;
1781                 }
1782
1783                 /* if we get access denied, a possible cause was that we had and open
1784                    connection to the DC, but someone changed our machine account password
1785                    out from underneath us using 'net rpc changetrustpw' */
1786
1787                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1788                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1789                                  "ACCESS_DENIED.  Maybe the trust account "
1790                                 "password was changed and we didn't know it. "
1791                                  "Killing connections to domain %s\n",
1792                                 name_domain));
1793                         invalidate_cm_connection(&domain->conn);
1794                         retry = true;
1795                 }
1796
1797         } while ( (attempts < 2) && retry );
1798
1799 process_result:
1800
1801         if (NT_STATUS_IS_OK(result)) {
1802
1803                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1804                 netsamlogon_cache_store(name_user, info3);
1805
1806                 /* Check if the user is in the right group */
1807
1808                 result = check_info3_in_group(
1809                         info3,
1810                         state->request->data.auth_crap.require_membership_of_sid);
1811                 if (!NT_STATUS_IS_OK(result)) {
1812                         DEBUG(3, ("User %s is not in the required group (%s), so "
1813                                   "crap authentication is rejected\n",
1814                                   state->request->data.auth_crap.user,
1815                                   state->request->data.auth_crap.require_membership_of_sid));
1816                         goto done;
1817                 }
1818
1819                 result = append_auth_data(state, info3, name_domain,
1820                                           name_user);
1821                 if (!NT_STATUS_IS_OK(result)) {
1822                         goto done;
1823                 }
1824         }
1825
1826 done:
1827
1828         /* give us a more useful (more correct?) error code */
1829         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1830             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1831                 result = NT_STATUS_NO_LOGON_SERVERS;
1832         }
1833
1834         if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1835                 result = nt_status_squash(result);
1836         }
1837
1838         set_auth_errors(state->response, result);
1839
1840         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1841               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1842                name_domain,
1843                name_user,
1844                state->response->data.auth.nt_status_string,
1845                state->response->data.auth.pam_error));
1846
1847         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1848 }
1849
1850 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1851                                                  struct winbindd_cli_state *state)
1852 {
1853         char *oldpass;
1854         char *newpass = NULL;
1855         struct policy_handle dom_pol;
1856         struct rpc_pipe_client *cli = NULL;
1857         bool got_info = false;
1858         struct samr_DomInfo1 *info = NULL;
1859         struct userPwdChangeFailureInformation *reject = NULL;
1860         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1861         fstring domain, user;
1862
1863         ZERO_STRUCT(dom_pol);
1864
1865         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1866                   state->request->data.auth.user));
1867
1868         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1869                 goto done;
1870         }
1871
1872         /* Change password */
1873
1874         oldpass = state->request->data.chauthtok.oldpass;
1875         newpass = state->request->data.chauthtok.newpass;
1876
1877         /* Initialize reject reason */
1878         state->response->data.auth.reject_reason = Undefined;
1879
1880         /* Get sam handle */
1881
1882         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1883                                 &dom_pol);
1884         if (!NT_STATUS_IS_OK(result)) {
1885                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1886                 goto done;
1887         }
1888
1889         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1890                                              user,
1891                                              newpass,
1892                                              oldpass,
1893                                              &info,
1894                                              &reject);
1895
1896         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1897
1898         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1899
1900                 fill_in_password_policy(state->response, info);
1901
1902                 state->response->data.auth.reject_reason =
1903                         reject->extendedFailureReason;
1904
1905                 got_info = true;
1906         }
1907
1908         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1909          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1910          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1911          * short to comply with the samr_ChangePasswordUser3 idl - gd */
1912
1913         /* only fallback when the chgpasswd_user3 call is not supported */
1914         if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1915                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1916                    (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1917                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1918
1919                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1920                         nt_errstr(result)));
1921
1922                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1923
1924                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1925                    Map to the same status code as Windows 2003. */
1926
1927                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1928                         result = NT_STATUS_PASSWORD_RESTRICTION;
1929                 }
1930         }
1931
1932 done:
1933
1934         if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1935                 if (lp_winbind_offline_logon()) {
1936                         result = winbindd_update_creds_by_name(contact_domain,
1937                                                          state->mem_ctx, user,
1938                                                          newpass);
1939                         /* Again, this happens when we login from gdm or xdm
1940                          * and the password expires, *BUT* cached crendentials
1941                          * doesn't exist. winbindd_update_creds_by_name()
1942                          * returns NT_STATUS_NO_SUCH_USER.
1943                          * This is not a failure.
1944                          * --- BoYang
1945                          * */
1946                         if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1947                                 result = NT_STATUS_OK;
1948                         }
1949
1950                         if (!NT_STATUS_IS_OK(result)) {
1951                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1952                                 goto process_result;
1953                         }
1954                 }
1955         }
1956
1957         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1958
1959                 NTSTATUS policy_ret;
1960
1961                 policy_ret = fillup_password_policy(contact_domain, state);
1962
1963                 /* failure of this is non critical, it will just provide no
1964                  * additional information to the client why the change has
1965                  * failed - Guenther */
1966
1967                 if (!NT_STATUS_IS_OK(policy_ret)) {
1968                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1969                         goto process_result;
1970                 }
1971         }
1972
1973 process_result:
1974
1975         if (strequal(contact_domain->name, get_global_sam_name())) {
1976                 /* FIXME: internal rpc pipe does not cache handles yet */
1977                 if (cli) {
1978                         if (is_valid_policy_hnd(&dom_pol)) {
1979                                 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1980                         }
1981                         TALLOC_FREE(cli);
1982                 }
1983         }
1984
1985         set_auth_errors(state->response, result);
1986
1987         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1988               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1989                domain,
1990                user,
1991                state->response->data.auth.nt_status_string,
1992                state->response->data.auth.pam_error));
1993
1994         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1995 }
1996
1997 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1998                                               struct winbindd_cli_state *state)
1999 {
2000         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2001
2002         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2003                 state->request->data.logoff.user));
2004
2005         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2006                 result = NT_STATUS_OK;
2007                 goto process_result;
2008         }
2009
2010         if (state->request->data.logoff.krb5ccname[0] == '\0') {
2011                 result = NT_STATUS_OK;
2012                 goto process_result;
2013         }
2014
2015 #ifdef HAVE_KRB5
2016
2017         if (state->request->data.logoff.uid < 0) {
2018                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2019                 goto process_result;
2020         }
2021
2022         /* what we need here is to find the corresponding krb5 ccache name *we*
2023          * created for a given username and destroy it */
2024
2025         if (!ccache_entry_exists(state->request->data.logoff.user)) {
2026                 result = NT_STATUS_OK;
2027                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2028                 goto process_result;
2029         }
2030
2031         if (!ccache_entry_identical(state->request->data.logoff.user,
2032                                         state->request->data.logoff.uid,
2033                                         state->request->data.logoff.krb5ccname)) {
2034                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2035                 goto process_result;
2036         }
2037
2038         result = remove_ccache(state->request->data.logoff.user);
2039         if (!NT_STATUS_IS_OK(result)) {
2040                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2041                         nt_errstr(result)));
2042                 goto process_result;
2043         }
2044
2045 #else
2046         result = NT_STATUS_NOT_SUPPORTED;
2047 #endif
2048
2049 process_result:
2050
2051
2052         set_auth_errors(state->response, result);
2053
2054         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2055 }
2056
2057 /* Change user password with auth crap*/
2058
2059 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2060 {
2061         NTSTATUS result;
2062         DATA_BLOB new_nt_password;
2063         DATA_BLOB old_nt_hash_enc;
2064         DATA_BLOB new_lm_password;
2065         DATA_BLOB old_lm_hash_enc;
2066         fstring  domain,user;
2067         struct policy_handle dom_pol;
2068         struct winbindd_domain *contact_domain = domainSt;
2069         struct rpc_pipe_client *cli = NULL;
2070
2071         ZERO_STRUCT(dom_pol);
2072
2073         /* Ensure null termination */
2074         state->request->data.chng_pswd_auth_crap.user[
2075                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2076         state->request->data.chng_pswd_auth_crap.domain[
2077                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2078         *domain = 0;
2079         *user = 0;
2080
2081         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2082                   (unsigned long)state->pid,
2083                   state->request->data.chng_pswd_auth_crap.domain,
2084                   state->request->data.chng_pswd_auth_crap.user));
2085
2086         if (lp_winbind_offline_logon()) {
2087                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2088                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2089                 result = NT_STATUS_ACCESS_DENIED;
2090                 goto done;
2091         }
2092
2093         if (*state->request->data.chng_pswd_auth_crap.domain) {
2094                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2095         } else {
2096                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2097                                   domain, user);
2098
2099                 if(!*domain) {
2100                         DEBUG(3,("no domain specified with username (%s) - "
2101                                  "failing auth\n",
2102                                  state->request->data.chng_pswd_auth_crap.user));
2103                         result = NT_STATUS_NO_SUCH_USER;
2104                         goto done;
2105                 }
2106         }
2107
2108         if (!*domain && lp_winbind_use_default_domain()) {
2109                 fstrcpy(domain,(char *)lp_workgroup());
2110         }
2111
2112         if(!*user) {
2113                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2114         }
2115
2116         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2117                   (unsigned long)state->pid, domain, user));
2118
2119         /* Change password */
2120         new_nt_password = data_blob_const(
2121                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2122                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2123
2124         old_nt_hash_enc = data_blob_const(
2125                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2126                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2127
2128         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2129                 new_lm_password = data_blob_const(
2130                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2131                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2132
2133                 old_lm_hash_enc = data_blob_const(
2134                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2135                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2136         } else {
2137                 new_lm_password.length = 0;
2138                 old_lm_hash_enc.length = 0;
2139         }
2140
2141         /* Get sam handle */
2142
2143         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2144         if (!NT_STATUS_IS_OK(result)) {
2145                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2146                 goto done;
2147         }
2148
2149         result = rpccli_samr_chng_pswd_auth_crap(
2150                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2151                 new_lm_password, old_lm_hash_enc);
2152
2153  done:
2154
2155         if (strequal(contact_domain->name, get_global_sam_name())) {
2156                 /* FIXME: internal rpc pipe does not cache handles yet */
2157                 if (cli) {
2158                         if (is_valid_policy_hnd(&dom_pol)) {
2159                                 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2160                         }
2161                         TALLOC_FREE(cli);
2162                 }
2163         }
2164
2165         set_auth_errors(state->response, result);
2166
2167         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2168               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2169                domain, user,
2170                state->response->data.auth.nt_status_string,
2171                state->response->data.auth.pam_error));
2172
2173         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2174 }