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