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