source: branches/samba-3.2.x/source/winbindd/winbindd_pam.c

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

Update 3.2 branch to 3.2.9

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