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