source: branches/samba-3.5.x/source3/winbindd/winbindd_pam.c

Last change on this file was 738, checked in by Silvan Scherrer, 13 years ago

Samba Server 3.5: update branche to 3.5.18

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