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

Last change on this file was 587, checked in by Herwig Bauernfeind, 14 years ago

Samba 3.5: Update vendor to version 3.5.6

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