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