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