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

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

Update Samba 3.3 to 3.3.10 (source)

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