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

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

Samba 3.5: Update vendor to version 3.5.9

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