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