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