2 Unix SMB/CIFS implementation.
4 Winbind status program.
6 Copyright (C) Tim Potter 2000-2003
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
8 Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000
9 Copyright (C) Robert O'Callahan 2006 (added cached credential code).
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 2 of the License, or
14 (at your option) any later version.
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.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "utils/ntlm_auth.h"
30 #define DBGC_CLASS DBGC_WINBIND
32 #define SQUID_BUFFER_SIZE 2010
34 enum stdio_helper_mode {
42 NTLM_CHANGE_PASSWORD_1,
46 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
47 char *buf, int length);
49 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
50 char *buf, int length);
52 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
53 char *buf, int length);
55 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
56 char *buf, int length);
58 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
59 char *buf, int length);
61 static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
62 char *buf, int length);
64 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
65 char *buf, int length);
67 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode helper_mode, char *buf, int length);
70 enum stdio_helper_mode mode;
72 stdio_helper_function fn;
73 } stdio_helper_protocols[] = {
74 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
75 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
76 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
77 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
78 { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request},
79 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
80 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
81 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
82 { NUM_HELPER_MODES, NULL, NULL}
85 extern int winbindd_fd;
87 const char *opt_username;
88 const char *opt_domain;
89 const char *opt_workstation;
90 const char *opt_password;
91 static DATA_BLOB opt_challenge;
92 static DATA_BLOB opt_lm_response;
93 static DATA_BLOB opt_nt_response;
94 static int request_lm_key;
95 static int request_user_session_key;
96 static int use_cached_creds;
98 static const char *require_membership_of;
99 static const char *require_membership_of_sid;
101 static char winbind_separator(void)
103 struct winbindd_response response;
110 ZERO_STRUCT(response);
112 /* Send off request */
114 if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
115 NSS_STATUS_SUCCESS) {
116 d_printf("could not obtain winbind separator!\n");
117 return *lp_winbind_separator();
120 sep = response.data.info.winbind_separator;
124 d_printf("winbind separator was NULL!\n");
125 return *lp_winbind_separator();
131 const char *get_winbind_domain(void)
133 struct winbindd_response response;
135 static fstring winbind_domain;
136 if (*winbind_domain) {
137 return winbind_domain;
140 ZERO_STRUCT(response);
142 /* Send off request */
144 if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
145 NSS_STATUS_SUCCESS) {
146 DEBUG(0, ("could not obtain winbind domain name!\n"));
147 return lp_workgroup();
150 fstrcpy(winbind_domain, response.data.domain_name);
152 return winbind_domain;
156 const char *get_winbind_netbios_name(void)
158 struct winbindd_response response;
160 static fstring winbind_netbios_name;
162 if (*winbind_netbios_name) {
163 return winbind_netbios_name;
166 ZERO_STRUCT(response);
168 /* Send off request */
170 if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) !=
171 NSS_STATUS_SUCCESS) {
172 DEBUG(0, ("could not obtain winbind netbios name!\n"));
173 return global_myname();
176 fstrcpy(winbind_netbios_name, response.data.netbios_name);
178 return winbind_netbios_name;
182 DATA_BLOB get_challenge(void)
184 static DATA_BLOB chal;
185 if (opt_challenge.length)
186 return opt_challenge;
188 chal = data_blob(NULL, 8);
190 generate_random_buffer(chal.data, chal.length);
194 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
195 form DOMAIN/user into a domain and a user */
197 static BOOL parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
201 char *p = strchr(domuser,winbind_separator());
208 fstrcpy(domain, domuser);
209 domain[PTR_DIFF(p, domuser)] = 0;
215 static BOOL get_require_membership_sid(void) {
216 struct winbindd_request request;
217 struct winbindd_response response;
219 if (!require_membership_of) {
223 if (require_membership_of_sid) {
227 /* Otherwise, ask winbindd for the name->sid request */
229 ZERO_STRUCT(request);
230 ZERO_STRUCT(response);
232 if (!parse_ntlm_auth_domain_user(require_membership_of,
233 request.data.name.dom_name,
234 request.data.name.name)) {
235 DEBUG(0, ("Could not parse %s into seperate domain/name parts!\n",
236 require_membership_of));
240 if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
241 NSS_STATUS_SUCCESS) {
242 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
243 require_membership_of));
247 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
249 if (require_membership_of_sid)
254 /* Authenticate a user with a plaintext password */
256 static BOOL check_plaintext_auth(const char *user, const char *pass,
257 BOOL stdout_diagnostics)
259 struct winbindd_request request;
260 struct winbindd_response response;
263 if (!get_require_membership_sid()) {
267 /* Send off request */
269 ZERO_STRUCT(request);
270 ZERO_STRUCT(response);
272 fstrcpy(request.data.auth.user, user);
273 fstrcpy(request.data.auth.pass, pass);
274 if (require_membership_of_sid)
275 fstrcpy(request.data.auth.require_membership_of_sid, require_membership_of_sid);
277 result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
279 /* Display response */
281 if (stdout_diagnostics) {
282 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
283 d_printf("Reading winbind reply failed! (0x01)\n");
286 d_printf("%s: %s (0x%x)\n",
287 response.data.auth.nt_status_string,
288 response.data.auth.error_string,
289 response.data.auth.nt_status);
291 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
292 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
295 DEBUG(3, ("%s: %s (0x%x)\n",
296 response.data.auth.nt_status_string,
297 response.data.auth.error_string,
298 response.data.auth.nt_status));
301 return (result == NSS_STATUS_SUCCESS);
304 /* authenticate a user with an encrypted username/password */
306 NTSTATUS contact_winbind_auth_crap(const char *username,
308 const char *workstation,
309 const DATA_BLOB *challenge,
310 const DATA_BLOB *lm_response,
311 const DATA_BLOB *nt_response,
314 uint8 user_session_key[16],
320 struct winbindd_request request;
321 struct winbindd_response response;
323 if (!get_require_membership_sid()) {
324 return NT_STATUS_INVALID_PARAMETER;
327 ZERO_STRUCT(request);
328 ZERO_STRUCT(response);
330 request.flags = flags;
332 request.data.auth_crap.logon_parameters = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
334 if (require_membership_of_sid)
335 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
337 fstrcpy(request.data.auth_crap.user, username);
338 fstrcpy(request.data.auth_crap.domain, domain);
340 fstrcpy(request.data.auth_crap.workstation,
343 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
345 if (lm_response && lm_response->length) {
346 memcpy(request.data.auth_crap.lm_resp,
348 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
349 request.data.auth_crap.lm_resp_len = lm_response->length;
352 if (nt_response && nt_response->length) {
353 memcpy(request.data.auth_crap.nt_resp,
355 MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp)));
356 request.data.auth_crap.nt_resp_len = nt_response->length;
359 result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
361 /* Display response */
363 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
364 nt_status = NT_STATUS_UNSUCCESSFUL;
366 *error_string = smb_xstrdup("Reading winbind reply failed!");
367 free_response(&response);
371 nt_status = (NT_STATUS(response.data.auth.nt_status));
372 if (!NT_STATUS_IS_OK(nt_status)) {
374 *error_string = smb_xstrdup(response.data.auth.error_string);
375 free_response(&response);
379 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
380 memcpy(lm_key, response.data.auth.first_8_lm_hash,
381 sizeof(response.data.auth.first_8_lm_hash));
383 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
384 memcpy(user_session_key, response.data.auth.user_session_key,
385 sizeof(response.data.auth.user_session_key));
388 if (flags & WBFLAG_PAM_UNIX_NAME) {
389 *unix_name = SMB_STRDUP((char *)response.extra_data.data);
391 free_response(&response);
392 return NT_STATUS_NO_MEMORY;
396 free_response(&response);
400 /* contact server to change user password using auth crap */
401 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
403 const DATA_BLOB new_nt_pswd,
404 const DATA_BLOB old_nt_hash_enc,
405 const DATA_BLOB new_lm_pswd,
406 const DATA_BLOB old_lm_hash_enc,
411 struct winbindd_request request;
412 struct winbindd_response response;
414 if (!get_require_membership_sid())
417 *error_string = smb_xstrdup("Can't get membership sid.");
418 return NT_STATUS_INVALID_PARAMETER;
421 ZERO_STRUCT(request);
422 ZERO_STRUCT(response);
425 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
427 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
429 if(new_nt_pswd.length)
431 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
432 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
435 if(old_nt_hash_enc.length)
437 memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc, old_nt_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_nt_hash_enc));
438 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
441 if(new_lm_pswd.length)
443 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
444 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
447 if(old_lm_hash_enc.length)
449 memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc, old_lm_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_lm_hash_enc));
450 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
453 result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
455 /* Display response */
457 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
459 nt_status = NT_STATUS_UNSUCCESSFUL;
461 *error_string = smb_xstrdup("Reading winbind reply failed!");
462 free_response(&response);
466 nt_status = (NT_STATUS(response.data.auth.nt_status));
467 if (!NT_STATUS_IS_OK(nt_status))
470 *error_string = smb_xstrdup(response.data.auth.error_string);
471 free_response(&response);
475 free_response(&response);
480 static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
482 static const char zeros[16];
486 uint8 user_sess_key[16];
489 nt_status = contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain,
490 ntlmssp_state->workstation,
491 &ntlmssp_state->chal,
492 &ntlmssp_state->lm_resp,
493 &ntlmssp_state->nt_resp,
494 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
495 lm_key, user_sess_key,
496 &error_string, &unix_name);
498 if (NT_STATUS_IS_OK(nt_status)) {
499 if (memcmp(lm_key, zeros, 8) != 0) {
500 *lm_session_key = data_blob(NULL, 16);
501 memcpy(lm_session_key->data, lm_key, 8);
502 memset(lm_session_key->data+8, '\0', 8);
505 if (memcmp(user_sess_key, zeros, 16) != 0) {
506 *user_session_key = data_blob(user_sess_key, 16);
508 ntlmssp_state->auth_context = talloc_strdup(ntlmssp_state->mem_ctx, unix_name);
509 SAFE_FREE(unix_name);
511 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
512 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
513 ntlmssp_state->domain, ntlmssp_state->user,
514 ntlmssp_state->workstation,
515 error_string ? error_string : "unknown error (NULL)"));
516 ntlmssp_state->auth_context = NULL;
521 static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
524 uint8 lm_pw[16], nt_pw[16];
526 nt_lm_owf_gen (opt_password, nt_pw, lm_pw);
528 nt_status = ntlm_password_check(ntlmssp_state->mem_ctx,
529 &ntlmssp_state->chal,
530 &ntlmssp_state->lm_resp,
531 &ntlmssp_state->nt_resp,
535 ntlmssp_state->domain,
536 lm_pw, nt_pw, user_session_key, lm_session_key);
538 if (NT_STATUS_IS_OK(nt_status)) {
539 ntlmssp_state->auth_context = talloc_asprintf(ntlmssp_state->mem_ctx,
540 "%s%c%s", ntlmssp_state->domain,
541 *lp_winbind_separator(),
542 ntlmssp_state->user);
544 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
545 ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation,
546 nt_errstr(nt_status)));
547 ntlmssp_state->auth_context = NULL;
552 static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_state)
555 if ( (opt_username == NULL) || (opt_domain == NULL) ) {
556 status = NT_STATUS_UNSUCCESSFUL;
557 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
558 return NT_STATUS_INVALID_PARAMETER;
561 status = ntlmssp_client_start(client_ntlmssp_state);
563 if (!NT_STATUS_IS_OK(status)) {
564 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
566 ntlmssp_end(client_ntlmssp_state);
570 status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
572 if (!NT_STATUS_IS_OK(status)) {
573 DEBUG(1, ("Could not set username: %s\n",
575 ntlmssp_end(client_ntlmssp_state);
579 status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
581 if (!NT_STATUS_IS_OK(status)) {
582 DEBUG(1, ("Could not set domain: %s\n",
584 ntlmssp_end(client_ntlmssp_state);
589 status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
591 if (!NT_STATUS_IS_OK(status)) {
592 DEBUG(1, ("Could not set password: %s\n",
594 ntlmssp_end(client_ntlmssp_state);
602 static NTSTATUS ntlm_auth_start_ntlmssp_server(NTLMSSP_STATE **ntlmssp_state)
604 NTSTATUS status = ntlmssp_server_start(ntlmssp_state);
606 if (!NT_STATUS_IS_OK(status)) {
607 DEBUG(1, ("Could not start NTLMSSP server: %s\n",
612 /* Have we been given a local password, or should we ask winbind? */
614 (*ntlmssp_state)->check_password = local_pw_check;
615 (*ntlmssp_state)->get_domain = lp_workgroup;
616 (*ntlmssp_state)->get_global_myname = global_myname;
618 (*ntlmssp_state)->check_password = winbind_pw_check;
619 (*ntlmssp_state)->get_domain = get_winbind_domain;
620 (*ntlmssp_state)->get_global_myname = get_winbind_netbios_name;
625 /*******************************************************************
626 Used by firefox to drive NTLM auth to IIS servers. Currently
627 requires krb5 enabled in winbindd as only then are the credentials
628 cached in memory. This needs fixing in winbindd. JRA.
629 *******************************************************************/
631 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
634 struct winbindd_request wb_request;
635 struct winbindd_response wb_response;
638 /* get winbindd to do the ntlmssp step on our behalf */
639 ZERO_STRUCT(wb_request);
640 ZERO_STRUCT(wb_response);
642 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
643 "%s%c%s", opt_domain, winbind_separator(), opt_username);
644 wb_request.data.ccache_ntlm_auth.uid = geteuid();
645 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
646 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
647 wb_request.extra_len = initial_msg.length + challenge_msg.length;
649 if (wb_request.extra_len > 0) {
650 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
651 if (wb_request.extra_data.data == NULL) {
652 return NT_STATUS_NO_MEMORY;
655 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
656 memcpy(wb_request.extra_data.data + initial_msg.length,
657 challenge_msg.data, challenge_msg.length);
660 result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
661 SAFE_FREE(wb_request.extra_data.data);
663 if (result != NSS_STATUS_SUCCESS) {
664 free_response(&wb_response);
665 return NT_STATUS_UNSUCCESSFUL;
669 *reply = data_blob(wb_response.extra_data.data,
670 wb_response.data.ccache_ntlm_auth.auth_blob_len);
671 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
672 reply->data == NULL) {
673 free_response(&wb_response);
674 return NT_STATUS_NO_MEMORY;
678 free_response(&wb_response);
679 return NT_STATUS_MORE_PROCESSING_REQUIRED;
682 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
683 char *buf, int length)
685 static NTLMSSP_STATE *ntlmssp_state = NULL;
686 static char* want_feature_list = NULL;
687 static uint32 neg_flags = 0;
688 static BOOL have_session_key = False;
689 static DATA_BLOB session_key;
690 DATA_BLOB request, reply;
693 if (strlen(buf) < 2) {
694 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
695 x_fprintf(x_stdout, "BH\n");
699 if (strlen(buf) > 3) {
700 if(strncmp(buf, "SF ", 3) == 0){
701 DEBUG(10, ("Setting flags to negotioate\n"));
702 SAFE_FREE(want_feature_list);
703 want_feature_list = SMB_STRNDUP(buf+3, strlen(buf)-3);
704 x_fprintf(x_stdout, "OK\n");
707 request = base64_decode_data_blob(buf + 3);
709 request = data_blob(NULL, 0);
712 if ((strncmp(buf, "PW ", 3) == 0)) {
713 /* The calling application wants us to use a local password (rather than winbindd) */
715 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
717 if (opt_password == NULL) {
718 DEBUG(1, ("Out of memory\n"));
719 x_fprintf(x_stdout, "BH\n");
720 data_blob_free(&request);
724 x_fprintf(x_stdout, "OK\n");
725 data_blob_free(&request);
729 if (strncmp(buf, "YR", 2) == 0) {
731 ntlmssp_end(&ntlmssp_state);
732 } else if (strncmp(buf, "KK", 2) == 0) {
734 } else if (strncmp(buf, "GF", 2) == 0) {
735 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
736 x_fprintf(x_stdout, "GF 0x%08lx\n", have_session_key?neg_flags:0l);
737 data_blob_free(&request);
739 } else if (strncmp(buf, "GK", 2) == 0) {
740 DEBUG(10, ("Requested NTLMSSP session key\n"));
741 if(have_session_key) {
742 char *key64 = base64_encode_data_blob(session_key);
743 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
746 x_fprintf(x_stdout, "BH\n");
749 data_blob_free(&request);
752 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
753 x_fprintf(x_stdout, "BH\n");
757 if (!ntlmssp_state) {
758 if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
759 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
762 ntlmssp_want_feature_list(ntlmssp_state, want_feature_list);
765 DEBUG(10, ("got NTLMSSP packet:\n"));
766 dump_data(10, (const char *)request.data, request.length);
768 nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
770 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
771 char *reply_base64 = base64_encode_data_blob(reply);
772 x_fprintf(x_stdout, "TT %s\n", reply_base64);
773 SAFE_FREE(reply_base64);
774 data_blob_free(&reply);
775 DEBUG(10, ("NTLMSSP challenge\n"));
776 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
777 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
778 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
780 ntlmssp_end(&ntlmssp_state);
781 } else if (!NT_STATUS_IS_OK(nt_status)) {
782 x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
783 DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
785 x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context);
786 DEBUG(10, ("NTLMSSP OK!\n"));
789 data_blob_free(&session_key);
790 session_key = data_blob(ntlmssp_state->session_key.data,
791 ntlmssp_state->session_key.length);
792 neg_flags = ntlmssp_state->neg_flags;
793 have_session_key = True;
796 data_blob_free(&request);
799 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
800 char *buf, int length)
802 /* The statics here are *HORRIBLE* and this entire concept
803 needs to be rewritten. Essentially it's using these statics
804 as the state in a state machine. BLEEEGH ! JRA. */
806 static NTLMSSP_STATE *ntlmssp_state = NULL;
807 static DATA_BLOB initial_message;
808 static char* want_feature_list = NULL;
809 static uint32 neg_flags = 0;
810 static BOOL have_session_key = False;
811 static DATA_BLOB session_key;
812 DATA_BLOB request, reply;
816 if (strlen(buf) < 2) {
817 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
818 x_fprintf(x_stdout, "BH\n");
822 if (strlen(buf) > 3) {
823 if(strncmp(buf, "SF ", 3) == 0) {
824 DEBUG(10, ("Looking for flags to negotiate\n"));
825 SAFE_FREE(want_feature_list);
826 want_feature_list = SMB_STRNDUP(buf+3, strlen(buf)-3);
827 x_fprintf(x_stdout, "OK\n");
830 request = base64_decode_data_blob(buf + 3);
832 request = data_blob(NULL, 0);
835 if (strncmp(buf, "PW ", 3) == 0) {
836 /* We asked for a password and obviously got it :-) */
838 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
840 if (opt_password == NULL) {
841 DEBUG(1, ("Out of memory\n"));
842 x_fprintf(x_stdout, "BH\n");
843 data_blob_free(&request);
847 x_fprintf(x_stdout, "OK\n");
848 data_blob_free(&request);
852 if (!ntlmssp_state && use_cached_creds) {
853 /* check whether credentials are usable. */
854 DATA_BLOB empty_blob = data_blob(NULL, 0);
856 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
857 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
858 /* failed to use cached creds */
859 use_cached_creds = False;
863 if (opt_password == NULL && !use_cached_creds) {
865 /* Request a password from the calling process. After
866 sending it, the calling process should retry asking for the negotiate. */
868 DEBUG(10, ("Requesting password\n"));
869 x_fprintf(x_stdout, "PW\n");
873 if (strncmp(buf, "YR", 2) == 0) {
875 ntlmssp_end(&ntlmssp_state);
876 } else if (strncmp(buf, "TT", 2) == 0) {
878 } else if (strncmp(buf, "GF", 2) == 0) {
879 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
880 x_fprintf(x_stdout, "GF 0x%08lx\n", have_session_key?neg_flags:0l);
881 data_blob_free(&request);
883 } else if (strncmp(buf, "GK", 2) == 0 ) {
884 DEBUG(10, ("Requested session key\n"));
886 if(have_session_key) {
887 char *key64 = base64_encode_data_blob(session_key);
888 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
892 x_fprintf(x_stdout, "BH\n");
895 data_blob_free(&request);
898 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
899 x_fprintf(x_stdout, "BH\n");
903 if (!ntlmssp_state) {
904 if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_client(&ntlmssp_state))) {
905 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
908 ntlmssp_want_feature_list(ntlmssp_state, want_feature_list);
910 initial_message = data_blob(NULL, 0);
913 DEBUG(10, ("got NTLMSSP packet:\n"));
914 dump_data(10, (const char *)request.data, request.length);
916 if (use_cached_creds && !opt_password && !first) {
917 nt_status = do_ccache_ntlm_auth(initial_message, request, &reply);
919 nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
922 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
923 char *reply_base64 = base64_encode_data_blob(reply);
925 x_fprintf(x_stdout, "YR %s\n", reply_base64);
927 x_fprintf(x_stdout, "KK %s\n", reply_base64);
929 SAFE_FREE(reply_base64);
931 initial_message = reply;
933 data_blob_free(&reply);
935 DEBUG(10, ("NTLMSSP challenge\n"));
936 } else if (NT_STATUS_IS_OK(nt_status)) {
937 char *reply_base64 = base64_encode_data_blob(reply);
938 x_fprintf(x_stdout, "AF %s\n", reply_base64);
939 SAFE_FREE(reply_base64);
942 data_blob_free(&session_key);
944 session_key = data_blob(ntlmssp_state->session_key.data,
945 ntlmssp_state->session_key.length);
946 neg_flags = ntlmssp_state->neg_flags;
947 have_session_key = True;
949 DEBUG(10, ("NTLMSSP OK!\n"));
951 ntlmssp_end(&ntlmssp_state);
953 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
954 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
956 ntlmssp_end(&ntlmssp_state);
959 data_blob_free(&request);
962 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
963 char *buf, int length)
968 pass=(char *)memchr(buf,' ',length);
970 DEBUG(2, ("Password not found. Denying access\n"));
971 x_fprintf(x_stdout, "ERR\n");
977 if (stdio_helper_mode == SQUID_2_5_BASIC) {
978 rfc1738_unescape(user);
979 rfc1738_unescape(pass);
982 if (check_plaintext_auth(user, pass, False)) {
983 x_fprintf(x_stdout, "OK\n");
985 x_fprintf(x_stdout, "ERR\n");
989 static void offer_gss_spnego_mechs(void) {
997 pstring myname_lower;
1001 pstrcpy(myname_lower, global_myname());
1002 strlower_m(myname_lower);
1004 pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm());
1006 /* Server negTokenInit (mech offerings) */
1007 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1008 spnego.negTokenInit.mechTypes = SMB_XMALLOC_ARRAY(const char *, 2);
1010 spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
1011 spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
1012 spnego.negTokenInit.mechTypes[2] = NULL;
1014 spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
1015 spnego.negTokenInit.mechTypes[1] = NULL;
1019 spnego.negTokenInit.mechListMIC = data_blob(principal,
1022 len = write_spnego_data(&token, &spnego);
1023 free_spnego_data(&spnego);
1026 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1027 x_fprintf(x_stdout, "BH\n");
1031 reply_base64 = base64_encode_data_blob(token);
1032 x_fprintf(x_stdout, "TT %s *\n", reply_base64);
1034 SAFE_FREE(reply_base64);
1035 data_blob_free(&token);
1036 DEBUG(10, ("sent SPNEGO negTokenInit\n"));
1040 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1041 char *buf, int length)
1043 static NTLMSSP_STATE *ntlmssp_state = NULL;
1044 SPNEGO_DATA request, response;
1050 char *domain = NULL;
1052 const char *reply_code;
1054 pstring reply_argument;
1056 if (strlen(buf) < 2) {
1057 DEBUG(1, ("SPENGO query [%s] invalid", buf));
1058 x_fprintf(x_stdout, "BH\n");
1062 if (strncmp(buf, "YR", 2) == 0) {
1064 ntlmssp_end(&ntlmssp_state);
1065 } else if (strncmp(buf, "KK", 2) == 0) {
1068 DEBUG(1, ("SPENGO query [%s] invalid", buf));
1069 x_fprintf(x_stdout, "BH\n");
1073 if ( (strlen(buf) == 2)) {
1075 /* no client data, get the negTokenInit offering
1078 offer_gss_spnego_mechs();
1082 /* All subsequent requests have a blob. This might be negTokenInit or negTokenTarg */
1084 if (strlen(buf) <= 3) {
1085 DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
1086 x_fprintf(x_stdout, "BH\n");
1090 token = base64_decode_data_blob(buf + 3);
1091 len = read_spnego_data(token, &request);
1092 data_blob_free(&token);
1095 DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
1096 x_fprintf(x_stdout, "BH\n");
1100 if (request.type == SPNEGO_NEG_TOKEN_INIT) {
1102 /* Second request from Client. This is where the
1103 client offers its mechanism to use. */
1105 if ( (request.negTokenInit.mechTypes == NULL) ||
1106 (request.negTokenInit.mechTypes[0] == NULL) ) {
1107 DEBUG(1, ("Client did not offer any mechanism"));
1108 x_fprintf(x_stdout, "BH\n");
1112 status = NT_STATUS_UNSUCCESSFUL;
1113 if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
1115 if ( request.negTokenInit.mechToken.data == NULL ) {
1116 DEBUG(1, ("Client did not provide NTLMSSP data\n"));
1117 x_fprintf(x_stdout, "BH\n");
1121 if ( ntlmssp_state != NULL ) {
1122 DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
1123 "already got one\n"));
1124 x_fprintf(x_stdout, "BH\n");
1125 ntlmssp_end(&ntlmssp_state);
1129 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
1130 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1134 DEBUG(10, ("got NTLMSSP packet:\n"));
1135 dump_data(10, (const char *)request.negTokenInit.mechToken.data,
1136 request.negTokenInit.mechToken.length);
1138 response.type = SPNEGO_NEG_TOKEN_TARG;
1139 response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP);
1140 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
1142 status = ntlmssp_update(ntlmssp_state,
1143 request.negTokenInit.mechToken,
1144 &response.negTokenTarg.responseToken);
1148 if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
1150 TALLOC_CTX *mem_ctx = talloc_init("manage_gss_spnego_request");
1153 DATA_BLOB session_key;
1155 if ( request.negTokenInit.mechToken.data == NULL ) {
1156 DEBUG(1, ("Client did not provide Kerberos data\n"));
1157 x_fprintf(x_stdout, "BH\n");
1161 response.type = SPNEGO_NEG_TOKEN_TARG;
1162 response.negTokenTarg.supportedMech = SMB_STRDUP(OID_KERBEROS5_OLD);
1163 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
1164 response.negTokenTarg.responseToken = data_blob(NULL, 0);
1166 status = ads_verify_ticket(mem_ctx, lp_realm(), 0,
1167 &request.negTokenInit.mechToken,
1168 &principal, NULL, &ap_rep,
1171 talloc_destroy(mem_ctx);
1173 /* Now in "principal" we have the name we are
1174 authenticated as. */
1176 if (NT_STATUS_IS_OK(status)) {
1178 domain = strchr_m(principal, '@');
1180 if (domain == NULL) {
1181 DEBUG(1, ("Did not get a valid principal "
1182 "from ads_verify_ticket\n"));
1183 x_fprintf(x_stdout, "BH\n");
1188 domain = SMB_STRDUP(domain);
1189 user = SMB_STRDUP(principal);
1191 data_blob_free(&ap_rep);
1193 SAFE_FREE(principal);
1200 if ( (request.negTokenTarg.supportedMech == NULL) ||
1201 ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
1202 /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
1203 is the only one we support that sends this stuff */
1204 DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
1205 request.negTokenTarg.supportedMech));
1206 x_fprintf(x_stdout, "BH\n");
1210 if (request.negTokenTarg.responseToken.data == NULL) {
1211 DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
1212 x_fprintf(x_stdout, "BH\n");
1216 status = ntlmssp_update(ntlmssp_state,
1217 request.negTokenTarg.responseToken,
1218 &response.negTokenTarg.responseToken);
1220 response.type = SPNEGO_NEG_TOKEN_TARG;
1221 response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP);
1222 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
1224 if (NT_STATUS_IS_OK(status)) {
1225 user = SMB_STRDUP(ntlmssp_state->user);
1226 domain = SMB_STRDUP(ntlmssp_state->domain);
1227 ntlmssp_end(&ntlmssp_state);
1231 free_spnego_data(&request);
1233 if (NT_STATUS_IS_OK(status)) {
1234 response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
1236 pstr_sprintf(reply_argument, "%s\\%s", domain, user);
1237 } else if (NT_STATUS_EQUAL(status,
1238 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1239 response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1241 pstr_sprintf(reply_argument, "*");
1243 response.negTokenTarg.negResult = SPNEGO_REJECT;
1245 pstrcpy(reply_argument, nt_errstr(status));
1251 len = write_spnego_data(&token, &response);
1252 free_spnego_data(&response);
1255 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1256 x_fprintf(x_stdout, "BH\n");
1260 reply_base64 = base64_encode_data_blob(token);
1262 x_fprintf(x_stdout, "%s %s %s\n",
1263 reply_code, reply_base64, reply_argument);
1265 SAFE_FREE(reply_base64);
1266 data_blob_free(&token);
1271 static NTLMSSP_STATE *client_ntlmssp_state = NULL;
1273 static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego)
1276 DATA_BLOB null_blob = data_blob(NULL, 0);
1277 DATA_BLOB to_server;
1278 char *to_server_base64;
1279 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1281 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1283 if (client_ntlmssp_state != NULL) {
1284 DEBUG(1, ("Request for initial SPNEGO request where "
1285 "we already have a state\n"));
1289 if (!client_ntlmssp_state) {
1290 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1291 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1297 if (opt_password == NULL) {
1299 /* Request a password from the calling process. After
1300 sending it, the calling process should retry with
1301 the negTokenInit. */
1303 DEBUG(10, ("Requesting password\n"));
1304 x_fprintf(x_stdout, "PW\n");
1308 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1309 spnego.negTokenInit.mechTypes = my_mechs;
1310 spnego.negTokenInit.reqFlags = 0;
1311 spnego.negTokenInit.mechListMIC = null_blob;
1313 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1314 &spnego.negTokenInit.mechToken);
1316 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1317 NT_STATUS_IS_OK(status)) ) {
1318 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1319 nt_errstr(status)));
1320 ntlmssp_end(&client_ntlmssp_state);
1324 write_spnego_data(&to_server, &spnego);
1325 data_blob_free(&spnego.negTokenInit.mechToken);
1327 to_server_base64 = base64_encode_data_blob(to_server);
1328 data_blob_free(&to_server);
1329 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1330 SAFE_FREE(to_server_base64);
1334 static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
1337 DATA_BLOB null_blob = data_blob(NULL, 0);
1339 DATA_BLOB to_server;
1340 char *to_server_base64;
1342 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1344 if (client_ntlmssp_state == NULL) {
1345 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1346 x_fprintf(x_stdout, "BH\n");
1350 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1351 x_fprintf(x_stdout, "NA\n");
1352 ntlmssp_end(&client_ntlmssp_state);
1356 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1357 x_fprintf(x_stdout, "AF\n");
1358 ntlmssp_end(&client_ntlmssp_state);
1362 status = ntlmssp_update(client_ntlmssp_state,
1363 spnego.negTokenTarg.responseToken,
1366 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1367 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
1368 "ntlmssp_client_update, got: %s\n",
1369 nt_errstr(status)));
1370 x_fprintf(x_stdout, "BH\n");
1371 data_blob_free(&request);
1372 ntlmssp_end(&client_ntlmssp_state);
1376 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1377 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1378 spnego.negTokenTarg.supportedMech = (char *)OID_NTLMSSP;
1379 spnego.negTokenTarg.responseToken = request;
1380 spnego.negTokenTarg.mechListMIC = null_blob;
1382 write_spnego_data(&to_server, &spnego);
1383 data_blob_free(&request);
1385 to_server_base64 = base64_encode_data_blob(to_server);
1386 data_blob_free(&to_server);
1387 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1388 SAFE_FREE(to_server_base64);
1394 static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
1397 DATA_BLOB tkt, to_server;
1398 DATA_BLOB session_key_krb5 = data_blob(NULL, 0);
1403 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1406 if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
1407 (spnego.negTokenInit.mechListMIC.length == 0) ) {
1408 DEBUG(1, ("Did not get a principal for krb5\n"));
1412 principal = (char *)SMB_MALLOC(
1413 spnego.negTokenInit.mechListMIC.length+1);
1415 if (principal == NULL) {
1416 DEBUG(1, ("Could not malloc principal\n"));
1420 memcpy(principal, spnego.negTokenInit.mechListMIC.data,
1421 spnego.negTokenInit.mechListMIC.length);
1422 principal[spnego.negTokenInit.mechListMIC.length] = '\0';
1424 retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL);
1430 /* Let's try to first get the TGT, for that we need a
1433 if (opt_password == NULL) {
1434 DEBUG(10, ("Requesting password\n"));
1435 x_fprintf(x_stdout, "PW\n");
1439 pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
1441 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1442 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1446 retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL);
1449 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1454 data_blob_free(&session_key_krb5);
1458 reply.type = SPNEGO_NEG_TOKEN_INIT;
1459 reply.negTokenInit.mechTypes = my_mechs;
1460 reply.negTokenInit.reqFlags = 0;
1461 reply.negTokenInit.mechToken = tkt;
1462 reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
1464 len = write_spnego_data(&to_server, &reply);
1465 data_blob_free(&tkt);
1468 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1472 reply_base64 = base64_encode_data_blob(to_server);
1473 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1475 SAFE_FREE(reply_base64);
1476 data_blob_free(&to_server);
1477 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1481 static void manage_client_krb5_targ(SPNEGO_DATA spnego)
1483 switch (spnego.negTokenTarg.negResult) {
1484 case SPNEGO_ACCEPT_INCOMPLETE:
1485 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1486 x_fprintf(x_stdout, "BH\n");
1488 case SPNEGO_ACCEPT_COMPLETED:
1489 DEBUG(10, ("Accept completed\n"));
1490 x_fprintf(x_stdout, "AF\n");
1493 DEBUG(10, ("Rejected\n"));
1494 x_fprintf(x_stdout, "NA\n");
1497 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1498 x_fprintf(x_stdout, "AF\n");
1504 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1505 char *buf, int length)
1511 if (strlen(buf) <= 3) {
1512 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
1513 x_fprintf(x_stdout, "BH\n");
1517 request = base64_decode_data_blob(buf+3);
1519 if (strncmp(buf, "PW ", 3) == 0) {
1521 /* We asked for a password and obviously got it :-) */
1523 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
1525 if (opt_password == NULL) {
1526 DEBUG(1, ("Out of memory\n"));
1527 x_fprintf(x_stdout, "BH\n");
1528 data_blob_free(&request);
1532 x_fprintf(x_stdout, "OK\n");
1533 data_blob_free(&request);
1537 if ( (strncmp(buf, "TT ", 3) != 0) &&
1538 (strncmp(buf, "AF ", 3) != 0) &&
1539 (strncmp(buf, "NA ", 3) != 0) ) {
1540 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
1541 x_fprintf(x_stdout, "BH\n");
1542 data_blob_free(&request);
1546 /* So we got a server challenge to generate a SPNEGO
1547 client-to-server request... */
1549 len = read_spnego_data(request, &spnego);
1550 data_blob_free(&request);
1553 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
1554 x_fprintf(x_stdout, "BH\n");
1558 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
1560 /* The server offers a list of mechanisms */
1562 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
1564 while (*mechType != NULL) {
1567 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
1568 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
1569 if (manage_client_krb5_init(spnego))
1574 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
1575 if (manage_client_ntlmssp_init(spnego))
1582 DEBUG(1, ("Server offered no compatible mechanism\n"));
1583 x_fprintf(x_stdout, "BH\n");
1587 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
1589 if (spnego.negTokenTarg.supportedMech == NULL) {
1590 /* On accept/reject Windows does not send the
1591 mechanism anymore. Handle that here and
1592 shut down the mechanisms. */
1594 switch (spnego.negTokenTarg.negResult) {
1595 case SPNEGO_ACCEPT_COMPLETED:
1596 x_fprintf(x_stdout, "AF\n");
1599 x_fprintf(x_stdout, "NA\n");
1602 DEBUG(1, ("Got a negTokenTarg with no mech and an "
1603 "unknown negResult: %d\n",
1604 spnego.negTokenTarg.negResult));
1605 x_fprintf(x_stdout, "BH\n");
1608 ntlmssp_end(&client_ntlmssp_state);
1612 if (strcmp(spnego.negTokenTarg.supportedMech,
1613 OID_NTLMSSP) == 0) {
1614 manage_client_ntlmssp_targ(spnego);
1619 if (strcmp(spnego.negTokenTarg.supportedMech,
1620 OID_KERBEROS5_OLD) == 0) {
1621 manage_client_krb5_targ(spnego);
1628 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
1629 x_fprintf(x_stdout, "BH\n");
1633 free_spnego_data(&spnego);
1637 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1638 char *buf, int length)
1640 char *request, *parameter;
1641 static DATA_BLOB challenge;
1642 static DATA_BLOB lm_response;
1643 static DATA_BLOB nt_response;
1644 static char *full_username;
1645 static char *username;
1646 static char *domain;
1647 static char *plaintext_password;
1648 static BOOL ntlm_server_1_user_session_key;
1649 static BOOL ntlm_server_1_lm_session_key;
1651 if (strequal(buf, ".")) {
1652 if (!full_username && !username) {
1653 x_fprintf(x_stdout, "Error: No username supplied!\n");
1654 } else if (plaintext_password) {
1655 /* handle this request as plaintext */
1656 if (!full_username) {
1657 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1658 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1662 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1663 x_fprintf(x_stdout, "Authenticated: Yes\n");
1665 x_fprintf(x_stdout, "Authenticated: No\n");
1667 } else if (!lm_response.data && !nt_response.data) {
1668 x_fprintf(x_stdout, "Error: No password supplied!\n");
1669 } else if (!challenge.data) {
1670 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
1672 char *error_string = NULL;
1674 uchar user_session_key[16];
1677 if (full_username && !username) {
1679 fstring fstr_domain;
1681 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1682 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1683 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
1685 SAFE_FREE(username);
1687 username = smb_xstrdup(fstr_user);
1688 domain = smb_xstrdup(fstr_domain);
1692 domain = smb_xstrdup(get_winbind_domain());
1695 if (ntlm_server_1_lm_session_key)
1696 flags |= WBFLAG_PAM_LMKEY;
1698 if (ntlm_server_1_user_session_key)
1699 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1701 if (!NT_STATUS_IS_OK(
1702 contact_winbind_auth_crap(username,
1714 x_fprintf(x_stdout, "Authenticated: No\n");
1715 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
1716 SAFE_FREE(error_string);
1718 static char zeros[16];
1720 char *hex_user_session_key;
1722 x_fprintf(x_stdout, "Authenticated: Yes\n");
1724 if (ntlm_server_1_lm_session_key
1725 && (memcmp(zeros, lm_key,
1726 sizeof(lm_key)) != 0)) {
1727 hex_lm_key = hex_encode(NULL,
1728 (const unsigned char *)lm_key,
1730 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
1731 TALLOC_FREE(hex_lm_key);
1734 if (ntlm_server_1_user_session_key
1735 && (memcmp(zeros, user_session_key,
1736 sizeof(user_session_key)) != 0)) {
1737 hex_user_session_key = hex_encode(NULL,
1738 (const unsigned char *)user_session_key,
1739 sizeof(user_session_key));
1740 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
1741 TALLOC_FREE(hex_user_session_key);
1745 /* clear out the state */
1746 challenge = data_blob(NULL, 0);
1747 nt_response = data_blob(NULL, 0);
1748 lm_response = data_blob(NULL, 0);
1749 SAFE_FREE(full_username);
1750 SAFE_FREE(username);
1752 SAFE_FREE(plaintext_password);
1753 ntlm_server_1_user_session_key = False;
1754 ntlm_server_1_lm_session_key = False;
1755 x_fprintf(x_stdout, ".\n");
1762 /* Indicates a base64 encoded structure */
1763 parameter = strstr_m(request, ":: ");
1765 parameter = strstr_m(request, ": ");
1768 DEBUG(0, ("Parameter not found!\n"));
1769 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1786 base64_decode_inplace(parameter);
1789 if (strequal(request, "LANMAN-Challenge")) {
1790 challenge = strhex_to_data_blob(NULL, parameter);
1791 if (challenge.length != 8) {
1792 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
1794 (int)challenge.length);
1795 challenge = data_blob(NULL, 0);
1797 } else if (strequal(request, "NT-Response")) {
1798 nt_response = strhex_to_data_blob(NULL, parameter);
1799 if (nt_response.length < 24) {
1800 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
1802 (int)nt_response.length);
1803 nt_response = data_blob(NULL, 0);
1805 } else if (strequal(request, "LANMAN-Response")) {
1806 lm_response = strhex_to_data_blob(NULL, parameter);
1807 if (lm_response.length != 24) {
1808 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
1810 (int)lm_response.length);
1811 lm_response = data_blob(NULL, 0);
1813 } else if (strequal(request, "Password")) {
1814 plaintext_password = smb_xstrdup(parameter);
1815 } else if (strequal(request, "NT-Domain")) {
1816 domain = smb_xstrdup(parameter);
1817 } else if (strequal(request, "Username")) {
1818 username = smb_xstrdup(parameter);
1819 } else if (strequal(request, "Full-Username")) {
1820 full_username = smb_xstrdup(parameter);
1821 } else if (strequal(request, "Request-User-Session-Key")) {
1822 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1823 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1824 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1826 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
1830 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode helper_mode, char *buf, int length)
1832 char *request, *parameter;
1833 static DATA_BLOB new_nt_pswd;
1834 static DATA_BLOB old_nt_hash_enc;
1835 static DATA_BLOB new_lm_pswd;
1836 static DATA_BLOB old_lm_hash_enc;
1837 static char *full_username = NULL;
1838 static char *username = NULL;
1839 static char *domain = NULL;
1840 static char *newpswd = NULL;
1841 static char *oldpswd = NULL;
1843 if (strequal(buf, ".")) {
1844 if(newpswd && oldpswd) {
1845 uchar old_nt_hash[16];
1846 uchar old_lm_hash[16];
1847 uchar new_nt_hash[16];
1848 uchar new_lm_hash[16];
1850 new_nt_pswd = data_blob(NULL, 516);
1851 old_nt_hash_enc = data_blob(NULL, 16);
1853 /* Calculate the MD4 hash (NT compatible) of the
1855 E_md4hash(oldpswd, old_nt_hash);
1856 E_md4hash(newpswd, new_nt_hash);
1858 /* E_deshash returns false for 'long'
1859 passwords (> 14 DOS chars).
1861 Therefore, don't send a buffer
1862 encrypted with the truncated hash
1863 (it could allow an even easier
1864 attack on the password)
1866 Likewise, obey the admin's restriction
1869 if (lp_client_lanman_auth() &&
1870 E_deshash(newpswd, new_lm_hash) &&
1871 E_deshash(oldpswd, old_lm_hash)) {
1872 new_lm_pswd = data_blob(NULL, 516);
1873 old_lm_hash_enc = data_blob(NULL, 16);
1874 encode_pw_buffer(new_lm_pswd.data, newpswd,
1877 SamOEMhash(new_lm_pswd.data, old_nt_hash, 516);
1878 E_old_pw_hash(new_nt_hash, old_lm_hash,
1879 old_lm_hash_enc.data);
1881 new_lm_pswd.data = NULL;
1882 new_lm_pswd.length = 0;
1883 old_lm_hash_enc.data = NULL;
1884 old_lm_hash_enc.length = 0;
1887 encode_pw_buffer(new_nt_pswd.data, newpswd,
1890 SamOEMhash(new_nt_pswd.data, old_nt_hash, 516);
1891 E_old_pw_hash(new_nt_hash, old_nt_hash,
1892 old_nt_hash_enc.data);
1895 if (!full_username && !username) {
1896 x_fprintf(x_stdout, "Error: No username supplied!\n");
1897 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1898 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1899 x_fprintf(x_stdout, "Error: No NT or LM password "
1900 "blobs supplied!\n");
1902 char *error_string = NULL;
1904 if (full_username && !username) {
1906 fstring fstr_domain;
1908 if (!parse_ntlm_auth_domain_user(full_username,
1911 /* username might be 'tainted', don't
1912 * print into our new-line
1913 * deleimianted stream */
1914 x_fprintf(x_stdout, "Error: Could not "
1915 "parse into domain and "
1917 SAFE_FREE(username);
1918 username = smb_xstrdup(full_username);
1920 SAFE_FREE(username);
1922 username = smb_xstrdup(fstr_user);
1923 domain = smb_xstrdup(fstr_domain);
1928 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1935 x_fprintf(x_stdout, "Password-Change: No\n");
1936 x_fprintf(x_stdout, "Password-Change-Error: "
1937 "%s\n.\n", error_string);
1939 x_fprintf(x_stdout, "Password-Change: Yes\n");
1942 SAFE_FREE(error_string);
1944 /* clear out the state */
1945 new_nt_pswd = data_blob(NULL, 0);
1946 old_nt_hash_enc = data_blob(NULL, 0);
1947 new_lm_pswd = data_blob(NULL, 0);
1948 old_nt_hash_enc = data_blob(NULL, 0);
1949 SAFE_FREE(full_username);
1950 SAFE_FREE(username);
1954 x_fprintf(x_stdout, ".\n");
1961 /* Indicates a base64 encoded structure */
1962 parameter = strstr_m(request, ":: ");
1964 parameter = strstr_m(request, ": ");
1967 DEBUG(0, ("Parameter not found!\n"));
1968 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1984 base64_decode_inplace(parameter);
1987 if (strequal(request, "new-nt-password-blob")) {
1988 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
1989 if (new_nt_pswd.length != 516) {
1990 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
1991 "(got %d bytes, expected 516)\n.\n",
1993 (int)new_nt_pswd.length);
1994 new_nt_pswd = data_blob(NULL, 0);
1996 } else if (strequal(request, "old-nt-hash-blob")) {
1997 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
1998 if (old_nt_hash_enc.length != 16) {
1999 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2000 "(got %d bytes, expected 16)\n.\n",
2002 (int)old_nt_hash_enc.length);
2003 old_nt_hash_enc = data_blob(NULL, 0);
2005 } else if (strequal(request, "new-lm-password-blob")) {
2006 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2007 if (new_lm_pswd.length != 516) {
2008 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2009 "(got %d bytes, expected 516)\n.\n",
2011 (int)new_lm_pswd.length);
2012 new_lm_pswd = data_blob(NULL, 0);
2015 else if (strequal(request, "old-lm-hash-blob")) {
2016 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2017 if (old_lm_hash_enc.length != 16)
2019 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2020 "(got %d bytes, expected 16)\n.\n",
2022 (int)old_lm_hash_enc.length);
2023 old_lm_hash_enc = data_blob(NULL, 0);
2025 } else if (strequal(request, "nt-domain")) {
2026 domain = smb_xstrdup(parameter);
2027 } else if(strequal(request, "username")) {
2028 username = smb_xstrdup(parameter);
2029 } else if(strequal(request, "full-username")) {
2030 username = smb_xstrdup(parameter);
2031 } else if(strequal(request, "new-password")) {
2032 newpswd = smb_xstrdup(parameter);
2033 } else if (strequal(request, "old-password")) {
2034 oldpswd = smb_xstrdup(parameter);
2036 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2040 static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn)
2042 char buf[SQUID_BUFFER_SIZE+1];
2047 /* this is not a typo - x_fgets doesn't work too well under squid */
2048 if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
2049 if (ferror(stdin)) {
2050 DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
2051 strerror(ferror(stdin))));
2053 exit(1); /* BIIG buffer */
2058 c=(char *)memchr(buf,'\n',sizeof(buf)-1);
2067 DEBUG(2, ("Oversized message\n"));
2068 x_fprintf(x_stderr, "ERR\n");
2073 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2075 if (buf[0] == '\0') {
2076 DEBUG(2, ("Invalid Request\n"));
2077 x_fprintf(x_stderr, "ERR\n");
2081 fn(helper_mode, buf, length);
2085 static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
2086 /* initialize FDescs */
2087 x_setbuf(x_stdout, NULL);
2088 x_setbuf(x_stderr, NULL);
2090 manage_squid_request(stdio_mode, fn);
2095 /* Authenticate a user with a challenge/response */
2097 static BOOL check_auth_crap(void)
2102 char user_session_key[16];
2104 char *hex_user_session_key;
2106 static uint8 zeros[16];
2108 x_setbuf(x_stdout, NULL);
2111 flags |= WBFLAG_PAM_LMKEY;
2113 if (request_user_session_key)
2114 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2116 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2118 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2124 (unsigned char *)lm_key,
2125 (unsigned char *)user_session_key,
2126 &error_string, NULL);
2128 if (!NT_STATUS_IS_OK(nt_status)) {
2129 x_fprintf(x_stdout, "%s (0x%x)\n",
2131 NT_STATUS_V(nt_status));
2132 SAFE_FREE(error_string);
2137 && (memcmp(zeros, lm_key,
2138 sizeof(lm_key)) != 0)) {
2139 hex_lm_key = hex_encode(NULL, (const unsigned char *)lm_key,
2141 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2142 TALLOC_FREE(hex_lm_key);
2144 if (request_user_session_key
2145 && (memcmp(zeros, user_session_key,
2146 sizeof(user_session_key)) != 0)) {
2147 hex_user_session_key = hex_encode(NULL, (const unsigned char *)user_session_key,
2148 sizeof(user_session_key));
2149 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2150 TALLOC_FREE(hex_user_session_key);
2159 OPT_USERNAME = 1000,
2168 OPT_USER_SESSION_KEY,
2170 OPT_REQUIRE_MEMBERSHIP,
2171 OPT_USE_CACHED_CREDS
2174 int main(int argc, const char **argv)
2177 static const char *helper_protocol;
2178 static int diagnostics;
2180 static const char *hex_challenge;
2181 static const char *hex_lm_response;
2182 static const char *hex_nt_response;
2186 /* NOTE: DO NOT change this interface without considering the implications!
2187 This is an external interface, which other programs will use to interact
2191 /* We do not use single-letter command abbreviations, because they harm future
2192 interface stability. */
2194 struct poptOption long_options[] = {
2196 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2197 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2198 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2199 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2200 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2201 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2202 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2203 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2204 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2205 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2206 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2207 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
2208 { "require-membership-of", 0, POPT_ARG_STRING, &require_membership_of, OPT_REQUIRE_MEMBERSHIP, "Require that a user be a member of this group (either name or SID) for authentication to succeed" },
2213 /* Samba client initialisation */
2218 /* Samba client initialisation */
2220 if (!lp_load(dyn_CONFIGFILE, True, False, False, True)) {
2221 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2222 dyn_CONFIGFILE, strerror(errno));
2228 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2230 /* Parse command line options */
2233 poptPrintHelp(pc, stderr, 0);
2237 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2238 POPT_CONTEXT_KEEP_FIRST);
2240 while((opt = poptGetNextOpt(pc)) != -1) {
2243 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2244 if (opt_challenge.length != 8) {
2245 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2247 (int)opt_challenge.length);
2252 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2253 if (opt_lm_response.length != 24) {
2254 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2256 (int)opt_lm_response.length);
2262 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2263 if (opt_nt_response.length < 24) {
2264 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2266 (int)opt_nt_response.length);
2271 case OPT_REQUIRE_MEMBERSHIP:
2272 if (StrnCaseCmp("S-", require_membership_of, 2) == 0) {
2273 require_membership_of_sid = require_membership_of;
2279 if (helper_protocol) {
2281 for (i=0; i<NUM_HELPER_MODES; i++) {
2282 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2283 squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
2287 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2289 for (i=0; i<NUM_HELPER_MODES; i++) {
2290 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2296 if (!opt_username) {
2297 x_fprintf(x_stderr, "username must be specified!\n\n");
2298 poptPrintHelp(pc, stderr, 0);
2302 if (opt_domain == NULL) {
2303 opt_domain = get_winbind_domain();
2306 if (opt_workstation == NULL) {
2307 opt_workstation = "";
2310 if (opt_challenge.length) {
2311 if (!check_auth_crap()) {
2317 if (!opt_password) {
2318 opt_password = getpass("password: ");
2322 if (!diagnose_ntlm_auth()) {
2328 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2329 if (!check_plaintext_auth(user, opt_password, True)) {
2336 poptFreeContext(pc);