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