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

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

Samba 3.5: Update vendor to version 3.5.8

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