178b3ea74be68c11f95a08580ad0359926f272ae
[samba.git] / source3 / winbindd / winbindd_pam.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - pam auth funcions
5
6    Copyright (C) Andrew Tridgell 2000
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett 2001-2002
9    Copyright (C) Guenther Deschner 2005
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #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;
801         char *mapped_user = 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_user);
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         {
833                 mapped_user = state->request->data.auth.user;
834         }
835
836         if (!canonicalize_username(mapped_user, name_domain, name_user)) {
837                 result = NT_STATUS_NO_SUCH_USER;
838                 goto done;
839         }
840
841         domain = find_auth_domain(state->request->flags, name_domain);
842
843         if (domain == NULL) {
844                 result = NT_STATUS_NO_SUCH_USER;
845                 goto done;
846         }
847
848         sendto_domain(state, domain);
849         return;
850  done:
851         set_auth_errors(state->response, result);
852         DEBUG(5, ("Plain text authentication for %s returned %s "
853                   "(PAM: %d)\n",
854                   state->request->data.auth.user,
855                   state->response->data.auth.nt_status_string,
856                   state->response->data.auth.pam_error));
857         request_error(state);
858 }
859
860 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
861                                               struct winbindd_cli_state *state,
862                                               struct netr_SamInfo3 **info3)
863 {
864         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
865         uint16 max_allowed_bad_attempts;
866         fstring name_domain, name_user;
867         DOM_SID sid;
868         enum lsa_SidType type;
869         uchar new_nt_pass[NT_HASH_LEN];
870         const uint8 *cached_nt_pass;
871         const uint8 *cached_salt;
872         struct netr_SamInfo3 *my_info3;
873         time_t kickoff_time, must_change_time;
874         bool password_good = false;
875 #ifdef HAVE_KRB5
876         struct winbindd_tdc_domain *tdc_domain = NULL;
877 #endif
878
879         *info3 = NULL;
880
881         ZERO_STRUCTP(info3);
882
883         DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
884
885         /* Parse domain and username */
886
887         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
888
889
890         if (!lookup_cached_name(state->mem_ctx,
891                                 name_domain,
892                                 name_user,
893                                 &sid,
894                                 &type)) {
895                 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
896                 return NT_STATUS_NO_SUCH_USER;
897         }
898
899         if (type != SID_NAME_USER) {
900                 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
901                 return NT_STATUS_LOGON_FAILURE;
902         }
903
904         result = winbindd_get_creds(domain,
905                                     state->mem_ctx,
906                                     &sid,
907                                     &my_info3,
908                                     &cached_nt_pass,
909                                     &cached_salt);
910         if (!NT_STATUS_IS_OK(result)) {
911                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
912                 return result;
913         }
914
915         *info3 = my_info3;
916
917         E_md4hash(state->request->data.auth.pass, new_nt_pass);
918
919         dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
920         dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
921         if (cached_salt) {
922                 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
923         }
924
925         if (cached_salt) {
926                 /* In this case we didn't store the nt_hash itself,
927                    but the MD5 combination of salt + nt_hash. */
928                 uchar salted_hash[NT_HASH_LEN];
929                 E_md5hash(cached_salt, new_nt_pass, salted_hash);
930
931                 password_good = (memcmp(cached_nt_pass, salted_hash,
932                                         NT_HASH_LEN) == 0);
933         } else {
934                 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
935                 password_good = (memcmp(cached_nt_pass, new_nt_pass,
936                                         NT_HASH_LEN) == 0);
937         }
938
939         if (password_good) {
940
941                 /* User *DOES* know the password, update logon_time and reset
942                  * bad_pw_count */
943
944                 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
945
946                 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
947                         return NT_STATUS_ACCOUNT_LOCKED_OUT;
948                 }
949
950                 if (my_info3->base.acct_flags & ACB_DISABLED) {
951                         return NT_STATUS_ACCOUNT_DISABLED;
952                 }
953
954                 if (my_info3->base.acct_flags & ACB_WSTRUST) {
955                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
956                 }
957
958                 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
959                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
960                 }
961
962                 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
963                         return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
964                 }
965
966                 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
967                         DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
968                                 my_info3->base.acct_flags));
969                         return NT_STATUS_LOGON_FAILURE;
970                 }
971
972                 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
973                 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
974                         return NT_STATUS_ACCOUNT_EXPIRED;
975                 }
976
977                 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
978                 if (must_change_time != 0 && must_change_time < time(NULL)) {
979                         /* we allow grace logons when the password has expired */
980                         my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
981                         /* return NT_STATUS_PASSWORD_EXPIRED; */
982                         goto success;
983                 }
984
985 #ifdef HAVE_KRB5
986                 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
987                     ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
988                     (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
989
990                         uid_t uid = -1;
991                         const char *cc = NULL;
992                         char *realm = NULL;
993                         const char *principal_s = NULL;
994                         const char *service = NULL;
995                         bool internal_ccache = false;
996
997                         uid = get_uid_from_state(state);
998                         if (uid == -1) {
999                                 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1000                                 return NT_STATUS_INVALID_PARAMETER;
1001                         }
1002
1003                         cc = generate_krb5_ccache(state->mem_ctx,
1004                                                 state->request->data.auth.krb5_cc_type,
1005                                                 state->request->data.auth.uid,
1006                                                 &internal_ccache);
1007                         if (cc == NULL) {
1008                                 return NT_STATUS_NO_MEMORY;
1009                         }
1010
1011                         realm = domain->alt_name;
1012                         strupper_m(realm);
1013
1014                         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1015                         if (principal_s == NULL) {
1016                                 return NT_STATUS_NO_MEMORY;
1017                         }
1018
1019                         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1020                         if (service == NULL) {
1021                                 return NT_STATUS_NO_MEMORY;
1022                         }
1023
1024                         if (!internal_ccache) {
1025
1026                                 setup_return_cc_name(state, cc);
1027
1028                                 result = add_ccache_to_list(principal_s,
1029                                                             cc,
1030                                                             service,
1031                                                             state->request->data.auth.user,
1032                                                             domain->alt_name,
1033                                                             uid,
1034                                                             time(NULL),
1035                                                             time(NULL) + lp_winbind_cache_time(),
1036                                                             time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1037                                                             true);
1038
1039                                 if (!NT_STATUS_IS_OK(result)) {
1040                                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1041                                                 "to add ccache to list: %s\n",
1042                                                 nt_errstr(result)));
1043                                 }
1044                         }
1045                 }
1046 #endif /* HAVE_KRB5 */
1047  success:
1048                 /* FIXME: we possibly should handle logon hours as well (does xp when
1049                  * offline?) see auth/auth_sam.c:sam_account_ok for details */
1050
1051                 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1052                 my_info3->base.bad_password_count = 0;
1053
1054                 result = winbindd_update_creds_by_info3(domain,
1055                                                         state->mem_ctx,
1056                                                         state->request->data.auth.user,
1057                                                         state->request->data.auth.pass,
1058                                                         my_info3);
1059                 if (!NT_STATUS_IS_OK(result)) {
1060                         DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1061                                 nt_errstr(result)));
1062                         return result;
1063                 }
1064
1065                 return NT_STATUS_OK;
1066
1067         }
1068
1069         /* User does *NOT* know the correct password, modify info3 accordingly */
1070
1071         /* failure of this is not critical */
1072         result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1073         if (!NT_STATUS_IS_OK(result)) {
1074                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1075                           "Won't be able to honour account lockout policies\n"));
1076         }
1077
1078         /* increase counter */
1079         my_info3->base.bad_password_count++;
1080
1081         if (max_allowed_bad_attempts == 0) {
1082                 goto failed;
1083         }
1084
1085         /* lockout user */
1086         if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1087
1088                 uint32 password_properties;
1089
1090                 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1091                 if (!NT_STATUS_IS_OK(result)) {
1092                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1093                 }
1094
1095                 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1096                     (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1097                         my_info3->base.acct_flags |= ACB_AUTOLOCK;
1098                 }
1099         }
1100
1101 failed:
1102         result = winbindd_update_creds_by_info3(domain,
1103                                                 state->mem_ctx,
1104                                                 state->request->data.auth.user,
1105                                                 NULL,
1106                                                 my_info3);
1107
1108         if (!NT_STATUS_IS_OK(result)) {
1109                 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1110                         nt_errstr(result)));
1111         }
1112
1113         return NT_STATUS_LOGON_FAILURE;
1114 }
1115
1116 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1117                                                 struct winbindd_cli_state *state,
1118                                                 struct netr_SamInfo3 **info3)
1119 {
1120         struct winbindd_domain *contact_domain;
1121         fstring name_domain, name_user;
1122         NTSTATUS result;
1123
1124         DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1125
1126         /* Parse domain and username */
1127
1128         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1129
1130         /* what domain should we contact? */
1131
1132         if ( IS_DC ) {
1133                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1134                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1135                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1136                         result = NT_STATUS_NO_SUCH_USER;
1137                         goto done;
1138                 }
1139
1140         } else {
1141                 if (is_myname(name_domain)) {
1142                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1143                         result =  NT_STATUS_NO_SUCH_USER;
1144                         goto done;
1145                 }
1146
1147                 contact_domain = find_domain_from_name(name_domain);
1148                 if (contact_domain == NULL) {
1149                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1150                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1151
1152                         contact_domain = find_our_domain();
1153                 }
1154         }
1155
1156         if (contact_domain->initialized &&
1157             contact_domain->active_directory) {
1158                 goto try_login;
1159         }
1160
1161         if (!contact_domain->initialized) {
1162                 init_dc_connection(contact_domain);
1163         }
1164
1165         if (!contact_domain->active_directory) {
1166                 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1167                 return NT_STATUS_INVALID_LOGON_TYPE;
1168         }
1169 try_login:
1170         result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1171 done:
1172         return result;
1173 }
1174
1175 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1176                                   TALLOC_CTX *mem_ctx,
1177                                   uint32 logon_parameters,
1178                                   const char *server,
1179                                   const char *username,
1180                                   const char *domain,
1181                                   const char *workstation,
1182                                   const uint8 chal[8],
1183                                   DATA_BLOB lm_response,
1184                                   DATA_BLOB nt_response,
1185                                   struct netr_SamInfo3 **info3);
1186
1187 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1188                                                 struct winbindd_cli_state *state,
1189                                                 struct netr_SamInfo3 **info3)
1190 {
1191
1192         struct rpc_pipe_client *netlogon_pipe;
1193         uchar chal[8];
1194         DATA_BLOB lm_resp;
1195         DATA_BLOB nt_resp;
1196         int attempts = 0;
1197         unsigned char local_lm_response[24];
1198         unsigned char local_nt_response[24];
1199         struct winbindd_domain *contact_domain;
1200         fstring name_domain, name_user;
1201         bool retry;
1202         NTSTATUS result;
1203         struct netr_SamInfo3 *my_info3 = NULL;
1204
1205         *info3 = NULL;
1206
1207         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1208
1209         /* Parse domain and username */
1210
1211         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1212
1213         /* do password magic */
1214
1215
1216         generate_random_buffer(chal, 8);
1217         if (lp_client_ntlmv2_auth()) {
1218                 DATA_BLOB server_chal;
1219                 DATA_BLOB names_blob;
1220                 DATA_BLOB nt_response;
1221                 DATA_BLOB lm_response;
1222                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1223
1224                 /* note that the 'workgroup' here is a best guess - we don't know
1225                    the server's domain at this point.  The 'server name' is also
1226                    dodgy...
1227                 */
1228                 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1229
1230                 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1231                                       state->request->data.auth.pass,
1232                                       &server_chal,
1233                                       &names_blob,
1234                                       &lm_response, &nt_response, NULL, NULL)) {
1235                         data_blob_free(&names_blob);
1236                         data_blob_free(&server_chal);
1237                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1238                         result = NT_STATUS_NO_MEMORY;
1239                         goto done;
1240                 }
1241                 data_blob_free(&names_blob);
1242                 data_blob_free(&server_chal);
1243                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1244                                            lm_response.length);
1245                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1246                                            nt_response.length);
1247                 data_blob_free(&lm_response);
1248                 data_blob_free(&nt_response);
1249
1250         } else {
1251                 if (lp_client_lanman_auth()
1252                     && SMBencrypt(state->request->data.auth.pass,
1253                                   chal,
1254                                   local_lm_response)) {
1255                         lm_resp = data_blob_talloc(state->mem_ctx,
1256                                                    local_lm_response,
1257                                                    sizeof(local_lm_response));
1258                 } else {
1259                         lm_resp = data_blob_null;
1260                 }
1261                 SMBNTencrypt(state->request->data.auth.pass,
1262                              chal,
1263                              local_nt_response);
1264
1265                 nt_resp = data_blob_talloc(state->mem_ctx,
1266                                            local_nt_response,
1267                                            sizeof(local_nt_response));
1268         }
1269
1270         /* what domain should we contact? */
1271
1272         if ( IS_DC ) {
1273                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1274                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1275                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1276                         result = NT_STATUS_NO_SUCH_USER;
1277                         goto done;
1278                 }
1279
1280         } else {
1281                 if (is_myname(name_domain)) {
1282                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1283                         result =  NT_STATUS_NO_SUCH_USER;
1284                         goto done;
1285                 }
1286
1287                 contact_domain = find_our_domain();
1288         }
1289
1290         /* check authentication loop */
1291
1292         do {
1293                 netlogon_fn_t logon_fn;
1294
1295                 ZERO_STRUCTP(my_info3);
1296                 retry = false;
1297
1298                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1299
1300                 if (!NT_STATUS_IS_OK(result)) {
1301                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1302                         goto done;
1303                 }
1304
1305                 /* It is really important to try SamLogonEx here,
1306                  * because in a clustered environment, we want to use
1307                  * one machine account from multiple physical
1308                  * computers.
1309                  *
1310                  * With a normal SamLogon call, we must keep the
1311                  * credentials chain updated and intact between all
1312                  * users of the machine account (which would imply
1313                  * cross-node communication for every NTLM logon).
1314                  *
1315                  * (The credentials chain is not per NETLOGON pipe
1316                  * connection, but globally on the server/client pair
1317                  * by machine name).
1318                  *
1319                  * When using SamLogonEx, the credentials are not
1320                  * supplied, but the session key is implied by the
1321                  * wrapping SamLogon context.
1322                  *
1323                  *  -- abartlet 21 April 2008
1324                  */
1325
1326                 logon_fn = contact_domain->can_do_samlogon_ex
1327                         ? rpccli_netlogon_sam_network_logon_ex
1328                         : rpccli_netlogon_sam_network_logon;
1329
1330                 result = logon_fn(netlogon_pipe,
1331                                   state->mem_ctx,
1332                                   0,
1333                                   contact_domain->dcname, /* server name */
1334                                   name_user,              /* user name */
1335                                   name_domain,            /* target domain */
1336                                   global_myname(),        /* workstation */
1337                                   chal,
1338                                   lm_resp,
1339                                   nt_resp,
1340                                   &my_info3);
1341                 attempts += 1;
1342
1343                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1344                     && contact_domain->can_do_samlogon_ex) {
1345                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1346                                   "retrying with NetSamLogon\n"));
1347                         contact_domain->can_do_samlogon_ex = false;
1348                         retry = true;
1349                         continue;
1350                 }
1351
1352                 /* We have to try a second time as cm_connect_netlogon
1353                    might not yet have noticed that the DC has killed
1354                    our connection. */
1355
1356                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1357                         retry = true;
1358                         continue;
1359                 }
1360
1361                 /* if we get access denied, a possible cause was that we had
1362                    and open connection to the DC, but someone changed our
1363                    machine account password out from underneath us using 'net
1364                    rpc changetrustpw' */
1365
1366                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1367                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1368                                  "ACCESS_DENIED.  Maybe the trust account "
1369                                 "password was changed and we didn't know it. "
1370                                  "Killing connections to domain %s\n",
1371                                 name_domain));
1372                         invalidate_cm_connection(&contact_domain->conn);
1373                         retry = true;
1374                 }
1375
1376         } while ( (attempts < 2) && retry );
1377
1378         /* handle the case where a NT4 DC does not fill in the acct_flags in
1379          * the samlogon reply info3. When accurate info3 is required by the
1380          * caller, we look up the account flags ourselve - gd */
1381
1382         if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1383             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1384
1385                 struct rpc_pipe_client *samr_pipe;
1386                 struct policy_handle samr_domain_handle, user_pol;
1387                 union samr_UserInfo *info = NULL;
1388                 NTSTATUS status_tmp;
1389                 uint32 acct_flags;
1390
1391                 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1392                                             &samr_pipe, &samr_domain_handle);
1393
1394                 if (!NT_STATUS_IS_OK(status_tmp)) {
1395                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1396                                 nt_errstr(status_tmp)));
1397                         goto done;
1398                 }
1399
1400                 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1401                                                   &samr_domain_handle,
1402                                                   MAXIMUM_ALLOWED_ACCESS,
1403                                                   my_info3->base.rid,
1404                                                   &user_pol);
1405
1406                 if (!NT_STATUS_IS_OK(status_tmp)) {
1407                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1408                                 nt_errstr(status_tmp)));
1409                         goto done;
1410                 }
1411
1412                 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1413                                                        &user_pol,
1414                                                        16,
1415                                                        &info);
1416
1417                 if (!NT_STATUS_IS_OK(status_tmp)) {
1418                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1419                                 nt_errstr(status_tmp)));
1420                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1421                         goto done;
1422                 }
1423
1424                 acct_flags = info->info16.acct_flags;
1425
1426                 if (acct_flags == 0) {
1427                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1428                         goto done;
1429                 }
1430
1431                 my_info3->base.acct_flags = acct_flags;
1432
1433                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1434
1435                 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1436         }
1437
1438         *info3 = my_info3;
1439 done:
1440         return result;
1441 }
1442
1443 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1444                                             struct winbindd_cli_state *state)
1445 {
1446         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1447         NTSTATUS krb5_result = NT_STATUS_OK;
1448         fstring name_domain, name_user;
1449         char *mapped_user;
1450         fstring domain_user;
1451         struct netr_SamInfo3 *info3 = NULL;
1452         NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1453
1454         /* Ensure null termination */
1455         state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1456
1457         /* Ensure null termination */
1458         state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1459
1460         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1461                   state->request->data.auth.user));
1462
1463         if (!check_request_flags(state->request->flags)) {
1464                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1465                 goto done;
1466         }
1467
1468         /* Parse domain and username */
1469
1470         name_map_status = normalize_name_unmap(state->mem_ctx,
1471                                                state->request->data.auth.user,
1472                                                &mapped_user);
1473
1474         /* If the name normalization didnt' actually do anything,
1475            just use the original name */
1476
1477         if (!NT_STATUS_IS_OK(name_map_status) &&
1478             !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1479         {
1480                 mapped_user = state->request->data.auth.user;
1481         }
1482
1483         parse_domain_user(mapped_user, name_domain, name_user);
1484
1485         if ( mapped_user != state->request->data.auth.user ) {
1486                 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1487                 safe_strcpy( state->request->data.auth.user, domain_user,
1488                              sizeof(state->request->data.auth.user)-1 );
1489         }
1490
1491         if (domain->online == false) {
1492                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1493                 if (domain->startup) {
1494                         /* Logons are very important to users. If we're offline and
1495                            we get a request within the first 30 seconds of startup,
1496                            try very hard to find a DC and go online. */
1497
1498                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1499                                 "request in startup mode.\n", domain->name ));
1500
1501                         winbindd_flush_negative_conn_cache(domain);
1502                         result = init_dc_connection(domain);
1503                 }
1504         }
1505
1506         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1507
1508         /* Check for Kerberos authentication */
1509         if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1510
1511                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1512                 /* save for later */
1513                 krb5_result = result;
1514
1515
1516                 if (NT_STATUS_IS_OK(result)) {
1517                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1518                         goto process_result;
1519                 } else {
1520                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1521                 }
1522
1523                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1524                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1525                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1526                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1527                         set_domain_offline( domain );
1528                         goto cached_logon;
1529                 }
1530
1531                 /* there are quite some NT_STATUS errors where there is no
1532                  * point in retrying with a samlogon, we explictly have to take
1533                  * care not to increase the bad logon counter on the DC */
1534
1535                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1536                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1537                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1538                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1539                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1540                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1541                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1542                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1543                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1544                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1545                         goto process_result;
1546                 }
1547
1548                 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1549                         DEBUG(3,("falling back to samlogon\n"));
1550                         goto sam_logon;
1551                 } else {
1552                         goto cached_logon;
1553                 }
1554         }
1555
1556 sam_logon:
1557         /* Check for Samlogon authentication */
1558         if (domain->online) {
1559                 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1560
1561                 if (NT_STATUS_IS_OK(result)) {
1562                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1563                         /* add the Krb5 err if we have one */
1564                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1565                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1566                         }
1567                         goto process_result;
1568                 }
1569
1570                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1571                           nt_errstr(result)));
1572
1573                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1574                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1575                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1576                 {
1577                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1578                         set_domain_offline( domain );
1579                         goto cached_logon;
1580                 }
1581
1582                         if (domain->online) {
1583                                 /* We're still online - fail. */
1584                                 goto done;
1585                         }
1586         }
1587
1588 cached_logon:
1589         /* Check for Cached logons */
1590         if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1591             lp_winbind_offline_logon()) {
1592
1593                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1594
1595                 if (NT_STATUS_IS_OK(result)) {
1596                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1597                         goto process_result;
1598                 } else {
1599                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1600                         goto done;
1601                 }
1602         }
1603
1604 process_result:
1605
1606         if (NT_STATUS_IS_OK(result)) {
1607
1608                 DOM_SID user_sid;
1609
1610                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1611                    been initialized. */
1612                 if (!info3) {
1613                         result = NT_STATUS_INTERNAL_ERROR;
1614                         goto done;
1615                 }
1616
1617                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1618                 netsamlogon_cache_store(name_user, info3);
1619
1620                 /* save name_to_sid info as early as possible (only if
1621                    this is our primary domain so we don't invalidate
1622                    the cache entry by storing the seq_num for the wrong
1623                    domain). */
1624                 if ( domain->primary ) {
1625                         sid_compose(&user_sid, info3->base.domain_sid,
1626                                     info3->base.rid);
1627                         cache_name2sid(domain, name_domain, name_user,
1628                                        SID_NAME_USER, &user_sid);
1629                 }
1630
1631                 /* Check if the user is in the right group */
1632
1633                 result = check_info3_in_group(
1634                         info3,
1635                         state->request->data.auth.require_membership_of_sid);
1636                 if (!NT_STATUS_IS_OK(result)) {
1637                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1638                                   state->request->data.auth.user,
1639                                   state->request->data.auth.require_membership_of_sid));
1640                         goto done;
1641                 }
1642
1643                 result = append_auth_data(state, info3, name_domain,
1644                                           name_user);
1645                 if (!NT_STATUS_IS_OK(result)) {
1646                         goto done;
1647                 }
1648
1649                 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1650
1651                         /* Store in-memory creds for single-signon using ntlm_auth. */
1652                         result = winbindd_add_memory_creds(state->request->data.auth.user,
1653                                                         get_uid_from_state(state),
1654                                                         state->request->data.auth.pass);
1655
1656                         if (!NT_STATUS_IS_OK(result)) {
1657                                 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1658                                 goto done;
1659                         }
1660
1661                         if (lp_winbind_offline_logon()) {
1662                                 result = winbindd_store_creds(domain,
1663                                                       state->mem_ctx,
1664                                                       state->request->data.auth.user,
1665                                                       state->request->data.auth.pass,
1666                                                       info3, NULL);
1667                                 if (!NT_STATUS_IS_OK(result)) {
1668
1669                                         /* Release refcount. */
1670                                         winbindd_delete_memory_creds(state->request->data.auth.user);
1671
1672                                         DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1673                                         goto done;
1674                                 }
1675                         }
1676                 }
1677
1678
1679                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1680                         struct winbindd_domain *our_domain = find_our_domain();
1681
1682                         /* This is not entirely correct I believe, but it is
1683                            consistent.  Only apply the password policy settings
1684                            too warn users for our own domain.  Cannot obtain these
1685                            from trusted DCs all the  time so don't do it at all.
1686                            -- jerry */
1687
1688                         result = NT_STATUS_NOT_SUPPORTED;
1689                         if (our_domain == domain ) {
1690                                 result = fillup_password_policy(our_domain, state);
1691                         }
1692
1693                         if (!NT_STATUS_IS_OK(result)
1694                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1695                         {
1696                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1697                                           domain->name, nt_errstr(result)));
1698                                 goto done;
1699                         }
1700                 }
1701
1702                 result = NT_STATUS_OK;
1703         }
1704
1705 done:
1706         /* give us a more useful (more correct?) error code */
1707         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1708             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1709                 result = NT_STATUS_NO_LOGON_SERVERS;
1710         }
1711
1712         set_auth_errors(state->response, result);
1713
1714         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1715               state->request->data.auth.user,
1716               state->response->data.auth.nt_status_string,
1717               state->response->data.auth.pam_error));
1718
1719         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1720 }
1721
1722
1723 /**********************************************************************
1724  Challenge Response Authentication Protocol
1725 **********************************************************************/
1726
1727 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1728 {
1729         struct winbindd_domain *domain = NULL;
1730         const char *domain_name = NULL;
1731         NTSTATUS result;
1732
1733         if (!check_request_flags(state->request->flags)) {
1734                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1735                 goto done;
1736         }
1737
1738         if (!state->privileged) {
1739                 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1740                           "denied.  !\n"));
1741                 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1742                              "on %s are set correctly.\n",
1743                              get_winbind_priv_pipe_dir()));
1744                 /* send a better message than ACCESS_DENIED */
1745                 fstr_sprintf(state->response->data.auth.error_string,
1746                              "winbind client not authorized to use "
1747                              "winbindd_pam_auth_crap. Ensure permissions on "
1748                              "%s are set correctly.",
1749                              get_winbind_priv_pipe_dir());
1750                 result = NT_STATUS_ACCESS_DENIED;
1751                 goto done;
1752         }
1753
1754         /* Ensure null termination */
1755         state->request->data.auth_crap.user
1756                 [sizeof(state->request->data.auth_crap.user)-1]=0;
1757         state->request->data.auth_crap.domain
1758                 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1759
1760         DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1761                   (unsigned long)state->pid,
1762                   state->request->data.auth_crap.domain,
1763                   state->request->data.auth_crap.user));
1764
1765         if (*state->request->data.auth_crap.domain != '\0') {
1766                 domain_name = state->request->data.auth_crap.domain;
1767         } else if (lp_winbind_use_default_domain()) {
1768                 domain_name = lp_workgroup();
1769         }
1770
1771         if (domain_name != NULL)
1772                 domain = find_auth_domain(state->request->flags, domain_name);
1773
1774         if (domain != NULL) {
1775                 sendto_domain(state, domain);
1776                 return;
1777         }
1778
1779         result = NT_STATUS_NO_SUCH_USER;
1780
1781  done:
1782         set_auth_errors(state->response, result);
1783         DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1784                   state->request->data.auth_crap.domain,
1785                   state->request->data.auth_crap.user,
1786                   state->response->data.auth.nt_status_string,
1787                   state->response->data.auth.pam_error));
1788         request_error(state);
1789         return;
1790 }
1791
1792
1793 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1794                                                  struct winbindd_cli_state *state)
1795 {
1796         NTSTATUS result;
1797         struct netr_SamInfo3 *info3 = NULL;
1798         struct rpc_pipe_client *netlogon_pipe;
1799         const char *name_user = NULL;
1800         const char *name_domain = NULL;
1801         const char *workstation;
1802         struct winbindd_domain *contact_domain;
1803         int attempts = 0;
1804         bool retry;
1805
1806         DATA_BLOB lm_resp, nt_resp;
1807
1808         /* This is child-only, so no check for privileged access is needed
1809            anymore */
1810
1811         /* Ensure null termination */
1812         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1813         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1814
1815         if (!check_request_flags(state->request->flags)) {
1816                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1817                 goto done;
1818         }
1819
1820         name_user = state->request->data.auth_crap.user;
1821
1822         if (*state->request->data.auth_crap.domain) {
1823                 name_domain = state->request->data.auth_crap.domain;
1824         } else if (lp_winbind_use_default_domain()) {
1825                 name_domain = lp_workgroup();
1826         } else {
1827                 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1828                          name_user));
1829                 result = NT_STATUS_NO_SUCH_USER;
1830                 goto done;
1831         }
1832
1833         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1834                   name_domain, name_user));
1835
1836         if (*state->request->data.auth_crap.workstation) {
1837                 workstation = state->request->data.auth_crap.workstation;
1838         } else {
1839                 workstation = global_myname();
1840         }
1841
1842         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1843                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1844                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1845                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1846                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1847                                   state->request->data.auth_crap.lm_resp_len,
1848                                   state->request->data.auth_crap.nt_resp_len));
1849                         result = NT_STATUS_INVALID_PARAMETER;
1850                         goto done;
1851                 }
1852         }
1853
1854         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1855                                         state->request->data.auth_crap.lm_resp_len);
1856
1857         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1858                 nt_resp = data_blob_talloc(state->mem_ctx,
1859                                            state->request->extra_data.data,
1860                                            state->request->data.auth_crap.nt_resp_len);
1861         } else {
1862                 nt_resp = data_blob_talloc(state->mem_ctx,
1863                                            state->request->data.auth_crap.nt_resp,
1864                                            state->request->data.auth_crap.nt_resp_len);
1865         }
1866
1867         /* what domain should we contact? */
1868
1869         if ( IS_DC ) {
1870                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1871                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1872                                   state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1873                         result = NT_STATUS_NO_SUCH_USER;
1874                         goto done;
1875                 }
1876         } else {
1877                 if (is_myname(name_domain)) {
1878                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1879                         result =  NT_STATUS_NO_SUCH_USER;
1880                         goto done;
1881                 }
1882                 contact_domain = find_our_domain();
1883         }
1884
1885         do {
1886                 netlogon_fn_t logon_fn;
1887
1888                 retry = false;
1889
1890                 netlogon_pipe = NULL;
1891                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1892
1893                 if (!NT_STATUS_IS_OK(result)) {
1894                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1895                                   nt_errstr(result)));
1896                         goto done;
1897                 }
1898
1899                 logon_fn = contact_domain->can_do_samlogon_ex
1900                         ? rpccli_netlogon_sam_network_logon_ex
1901                         : rpccli_netlogon_sam_network_logon;
1902
1903                 result = logon_fn(netlogon_pipe,
1904                                   state->mem_ctx,
1905                                   state->request->data.auth_crap.logon_parameters,
1906                                   contact_domain->dcname,
1907                                   name_user,
1908                                   name_domain,
1909                                   /* Bug #3248 - found by Stefan Burkei. */
1910                                   workstation, /* We carefully set this above so use it... */
1911                                   state->request->data.auth_crap.chal,
1912                                   lm_resp,
1913                                   nt_resp,
1914                                   &info3);
1915
1916                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1917                     && contact_domain->can_do_samlogon_ex) {
1918                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1919                                   "retrying with NetSamLogon\n"));
1920                         contact_domain->can_do_samlogon_ex = false;
1921                         retry = true;
1922                         continue;
1923                 }
1924
1925                 attempts += 1;
1926
1927                 /* We have to try a second time as cm_connect_netlogon
1928                    might not yet have noticed that the DC has killed
1929                    our connection. */
1930
1931                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1932                         retry = true;
1933                         continue;
1934                 }
1935
1936                 /* if we get access denied, a possible cause was that we had and open
1937                    connection to the DC, but someone changed our machine account password
1938                    out from underneath us using 'net rpc changetrustpw' */
1939
1940                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1941                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1942                                  "ACCESS_DENIED.  Maybe the trust account "
1943                                 "password was changed and we didn't know it. "
1944                                  "Killing connections to domain %s\n",
1945                                 name_domain));
1946                         invalidate_cm_connection(&contact_domain->conn);
1947                         retry = true;
1948                 }
1949
1950         } while ( (attempts < 2) && retry );
1951
1952         if (NT_STATUS_IS_OK(result)) {
1953
1954                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1955                 netsamlogon_cache_store(name_user, info3);
1956
1957                 /* Check if the user is in the right group */
1958
1959                 result = check_info3_in_group(
1960                         info3,
1961                         state->request->data.auth_crap.require_membership_of_sid);
1962                 if (!NT_STATUS_IS_OK(result)) {
1963                         DEBUG(3, ("User %s is not in the required group (%s), so "
1964                                   "crap authentication is rejected\n",
1965                                   state->request->data.auth_crap.user,
1966                                   state->request->data.auth_crap.require_membership_of_sid));
1967                         goto done;
1968                 }
1969
1970                 result = append_auth_data(state, info3, name_domain,
1971                                           name_user);
1972                 if (!NT_STATUS_IS_OK(result)) {
1973                         goto done;
1974                 }
1975         }
1976
1977 done:
1978
1979         /* give us a more useful (more correct?) error code */
1980         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1981             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1982                 result = NT_STATUS_NO_LOGON_SERVERS;
1983         }
1984
1985         if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1986                 result = nt_status_squash(result);
1987         }
1988
1989         set_auth_errors(state->response, result);
1990
1991         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1992               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1993                name_domain,
1994                name_user,
1995                state->response->data.auth.nt_status_string,
1996                state->response->data.auth.pam_error));
1997
1998         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1999 }
2000
2001 /* Change a user password */
2002
2003 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2004 {
2005         fstring domain, user;
2006         char *mapped_user;
2007         struct winbindd_domain *contact_domain;
2008         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2009
2010         DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2011                 state->request->data.chauthtok.user));
2012
2013         /* Setup crap */
2014
2015         nt_status = normalize_name_unmap(state->mem_ctx,
2016                                          state->request->data.chauthtok.user,
2017                                          &mapped_user);
2018
2019         /* Update the chauthtok name if we did any mapping */
2020
2021         if (NT_STATUS_IS_OK(nt_status) ||
2022             NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2023         {
2024                 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2025         }
2026
2027         /* Must pass in state->...chauthtok.user because
2028            canonicalize_username() assumes an fstring().  Since
2029            we have already copied it (if necessary), this is ok. */
2030
2031         if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2032                 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2033                 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2034                           "(PAM: %d)\n",
2035                           state->request->data.auth.user,
2036                           state->response->data.auth.nt_status_string,
2037                           state->response->data.auth.pam_error));
2038                 request_error(state);
2039                 return;
2040         }
2041
2042         contact_domain = find_domain_from_name(domain);
2043         if (!contact_domain) {
2044                 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2045                 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2046                           state->request->data.chauthtok.user, domain, user, domain));
2047                 request_error(state);
2048                 return;
2049         }
2050
2051         sendto_domain(state, contact_domain);
2052 }
2053
2054 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2055                                                  struct winbindd_cli_state *state)
2056 {
2057         char *oldpass;
2058         char *newpass = NULL;
2059         struct policy_handle dom_pol;
2060         struct rpc_pipe_client *cli;
2061         bool got_info = false;
2062         struct samr_DomInfo1 *info = NULL;
2063         struct samr_ChangeReject *reject = NULL;
2064         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2065         fstring domain, user;
2066
2067         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2068                   state->request->data.auth.user));
2069
2070         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2071                 goto done;
2072         }
2073
2074         /* Change password */
2075
2076         oldpass = state->request->data.chauthtok.oldpass;
2077         newpass = state->request->data.chauthtok.newpass;
2078
2079         /* Initialize reject reason */
2080         state->response->data.auth.reject_reason = Undefined;
2081
2082         /* Get sam handle */
2083
2084         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2085                                 &dom_pol);
2086         if (!NT_STATUS_IS_OK(result)) {
2087                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2088                 goto done;
2089         }
2090
2091         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2092                                              user,
2093                                              newpass,
2094                                              oldpass,
2095                                              &info,
2096                                              &reject);
2097
2098         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2099
2100         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2101
2102                 fill_in_password_policy(state->response, info);
2103
2104                 state->response->data.auth.reject_reason =
2105                         reject->reason;
2106
2107                 got_info = true;
2108         }
2109
2110         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2111          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2112          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2113          * short to comply with the samr_ChangePasswordUser3 idl - gd */
2114
2115         /* only fallback when the chgpasswd_user3 call is not supported */
2116         if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2117                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2118                    (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2119                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2120
2121                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2122                         nt_errstr(result)));
2123
2124                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2125
2126                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2127                    Map to the same status code as Windows 2003. */
2128
2129                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2130                         result = NT_STATUS_PASSWORD_RESTRICTION;
2131                 }
2132         }
2133
2134 done:
2135
2136         if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2137
2138                 /* Update the single sign-on memory creds. */
2139                 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2140                                                         newpass);
2141
2142                 /* When we login from gdm or xdm and password expires,
2143                  * we change password, but there are no memory crendentials
2144                  * So, winbindd_replace_memory_creds() returns
2145                  * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2146                  * --- BoYang
2147                  * */
2148                 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2149                         result = NT_STATUS_OK;
2150                 }
2151
2152                 if (!NT_STATUS_IS_OK(result)) {
2153                         DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2154                         goto process_result;
2155                 }
2156
2157                 if (lp_winbind_offline_logon()) {
2158                         result = winbindd_update_creds_by_name(contact_domain,
2159                                                          state->mem_ctx, user,
2160                                                          newpass);
2161                         /* Again, this happens when we login from gdm or xdm
2162                          * and the password expires, *BUT* cached crendentials
2163                          * doesn't exist. winbindd_update_creds_by_name()
2164                          * returns NT_STATUS_NO_SUCH_USER.
2165                          * This is not a failure.
2166                          * --- BoYang
2167                          * */
2168                         if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2169                                 result = NT_STATUS_OK;
2170                         }
2171
2172                         if (!NT_STATUS_IS_OK(result)) {
2173                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2174                                 goto process_result;
2175                         }
2176                 }
2177         }
2178
2179         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2180
2181                 NTSTATUS policy_ret;
2182
2183                 policy_ret = fillup_password_policy(contact_domain, state);
2184
2185                 /* failure of this is non critical, it will just provide no
2186                  * additional information to the client why the change has
2187                  * failed - Guenther */
2188
2189                 if (!NT_STATUS_IS_OK(policy_ret)) {
2190                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2191                         goto process_result;
2192                 }
2193         }
2194
2195 process_result:
2196
2197         set_auth_errors(state->response, result);
2198
2199         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2200               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2201                domain,
2202                user,
2203                state->response->data.auth.nt_status_string,
2204                state->response->data.auth.pam_error));
2205
2206         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2207 }
2208
2209 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2210 {
2211         struct winbindd_domain *domain;
2212         fstring name_domain, user;
2213         uid_t caller_uid = (uid_t)-1;
2214         uid_t request_uid = state->request->data.logoff.uid;
2215
2216         DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2217                 state->request->data.logoff.user));
2218
2219         /* Ensure null termination */
2220         state->request->data.logoff.user
2221                 [sizeof(state->request->data.logoff.user)-1]='\0';
2222
2223         state->request->data.logoff.krb5ccname
2224                 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2225
2226         if (request_uid == (gid_t)-1) {
2227                 goto failed;
2228         }
2229
2230         if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2231                 goto failed;
2232         }
2233
2234         if ((domain = find_auth_domain(state->request->flags,
2235                                        name_domain)) == NULL) {
2236                 goto failed;
2237         }
2238
2239         if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2240                 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2241                         strerror(errno)));
2242                 goto failed;
2243         }
2244
2245         switch (caller_uid) {
2246                 case -1:
2247                         goto failed;
2248                 case 0:
2249                         /* root must be able to logoff any user - gd */
2250                         state->request->data.logoff.uid = request_uid;
2251                         break;
2252                 default:
2253                         if (caller_uid != request_uid) {
2254                                 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2255                                 goto failed;
2256                         }
2257                         state->request->data.logoff.uid = caller_uid;
2258                         break;
2259         }
2260
2261         sendto_domain(state, domain);
2262         return;
2263
2264  failed:
2265         set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2266         DEBUG(5, ("Pam Logoff for %s returned %s "
2267                   "(PAM: %d)\n",
2268                   state->request->data.logoff.user,
2269                   state->response->data.auth.nt_status_string,
2270                   state->response->data.auth.pam_error));
2271         request_error(state);
2272         return;
2273 }
2274
2275 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2276                                               struct winbindd_cli_state *state)
2277 {
2278         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2279
2280         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2281                 state->request->data.logoff.user));
2282
2283         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2284                 result = NT_STATUS_OK;
2285                 goto process_result;
2286         }
2287
2288         if (state->request->data.logoff.krb5ccname[0] == '\0') {
2289                 result = NT_STATUS_OK;
2290                 goto process_result;
2291         }
2292
2293 #ifdef HAVE_KRB5
2294
2295         if (state->request->data.logoff.uid < 0) {
2296                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2297                 goto process_result;
2298         }
2299
2300         /* what we need here is to find the corresponding krb5 ccache name *we*
2301          * created for a given username and destroy it */
2302
2303         if (!ccache_entry_exists(state->request->data.logoff.user)) {
2304                 result = NT_STATUS_OK;
2305                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2306                 goto process_result;
2307         }
2308
2309         if (!ccache_entry_identical(state->request->data.logoff.user,
2310                                         state->request->data.logoff.uid,
2311                                         state->request->data.logoff.krb5ccname)) {
2312                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2313                 goto process_result;
2314         }
2315
2316         result = remove_ccache(state->request->data.logoff.user);
2317         if (!NT_STATUS_IS_OK(result)) {
2318                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2319                         nt_errstr(result)));
2320                 goto process_result;
2321         }
2322
2323 #else
2324         result = NT_STATUS_NOT_SUPPORTED;
2325 #endif
2326
2327 process_result:
2328
2329         winbindd_delete_memory_creds(state->request->data.logoff.user);
2330
2331         set_auth_errors(state->response, result);
2332
2333         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2334 }
2335
2336 /* Change user password with auth crap*/
2337
2338 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2339 {
2340         struct winbindd_domain *domain = NULL;
2341         const char *domain_name = NULL;
2342
2343         /* Ensure null termination */
2344         state->request->data.chng_pswd_auth_crap.user[
2345                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2346         state->request->data.chng_pswd_auth_crap.domain[
2347                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2348
2349         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2350                   (unsigned long)state->pid,
2351                   state->request->data.chng_pswd_auth_crap.domain,
2352                   state->request->data.chng_pswd_auth_crap.user));
2353
2354         if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2355                 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2356         } else if (lp_winbind_use_default_domain()) {
2357                 domain_name = lp_workgroup();
2358         }
2359
2360         if (domain_name != NULL)
2361                 domain = find_domain_from_name(domain_name);
2362
2363         if (domain != NULL) {
2364                 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2365                           "%s\n", (unsigned long)state->pid,domain->name));
2366                 sendto_domain(state, domain);
2367                 return;
2368         }
2369
2370         set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2371         DEBUG(5, ("CRAP change password  for %s\\%s returned %s (PAM: %d)\n",
2372                   state->request->data.chng_pswd_auth_crap.domain,
2373                   state->request->data.chng_pswd_auth_crap.user,
2374                   state->response->data.auth.nt_status_string,
2375                   state->response->data.auth.pam_error));
2376         request_error(state);
2377         return;
2378 }
2379
2380 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2381 {
2382         NTSTATUS result;
2383         DATA_BLOB new_nt_password;
2384         DATA_BLOB old_nt_hash_enc;
2385         DATA_BLOB new_lm_password;
2386         DATA_BLOB old_lm_hash_enc;
2387         fstring  domain,user;
2388         struct policy_handle dom_pol;
2389         struct winbindd_domain *contact_domain = domainSt;
2390         struct rpc_pipe_client *cli;
2391
2392         /* Ensure null termination */
2393         state->request->data.chng_pswd_auth_crap.user[
2394                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2395         state->request->data.chng_pswd_auth_crap.domain[
2396                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2397         *domain = 0;
2398         *user = 0;
2399
2400         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2401                   (unsigned long)state->pid,
2402                   state->request->data.chng_pswd_auth_crap.domain,
2403                   state->request->data.chng_pswd_auth_crap.user));
2404
2405         if (lp_winbind_offline_logon()) {
2406                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2407                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2408                 result = NT_STATUS_ACCESS_DENIED;
2409                 goto done;
2410         }
2411
2412         if (*state->request->data.chng_pswd_auth_crap.domain) {
2413                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2414         } else {
2415                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2416                                   domain, user);
2417
2418                 if(!*domain) {
2419                         DEBUG(3,("no domain specified with username (%s) - "
2420                                  "failing auth\n",
2421                                  state->request->data.chng_pswd_auth_crap.user));
2422                         result = NT_STATUS_NO_SUCH_USER;
2423                         goto done;
2424                 }
2425         }
2426
2427         if (!*domain && lp_winbind_use_default_domain()) {
2428                 fstrcpy(domain,(char *)lp_workgroup());
2429         }
2430
2431         if(!*user) {
2432                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2433         }
2434
2435         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2436                   (unsigned long)state->pid, domain, user));
2437
2438         /* Change password */
2439         new_nt_password = data_blob_talloc(
2440                 state->mem_ctx,
2441                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2442                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2443
2444         old_nt_hash_enc = data_blob_talloc(
2445                 state->mem_ctx,
2446                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2447                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2448
2449         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2450                 new_lm_password = data_blob_talloc(
2451                         state->mem_ctx,
2452                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2453                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2454
2455                 old_lm_hash_enc = data_blob_talloc(
2456                         state->mem_ctx,
2457                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2458                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2459         } else {
2460                 new_lm_password.length = 0;
2461                 old_lm_hash_enc.length = 0;
2462         }
2463
2464         /* Get sam handle */
2465
2466         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2467         if (!NT_STATUS_IS_OK(result)) {
2468                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2469                 goto done;
2470         }
2471
2472         result = rpccli_samr_chng_pswd_auth_crap(
2473                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2474                 new_lm_password, old_lm_hash_enc);
2475
2476  done:
2477
2478         set_auth_errors(state->response, result);
2479
2480         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2481               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2482                domain, user,
2483                state->response->data.auth.nt_status_string,
2484                state->response->data.auth.pam_error));
2485
2486         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2487 }