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