source: vendor/3.5.0/source3/winbindd/winbindd_pam.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 16 years ago

Samba 3.5.0: Initial import

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