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");
1347 ntlmssp_end(&client_ntlmssp_state);
1351 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1352 x_fprintf(x_stdout, "NA\n");
1353 ntlmssp_end(&client_ntlmssp_state);
1357 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1358 x_fprintf(x_stdout, "AF\n");
1359 ntlmssp_end(&client_ntlmssp_state);
1363 status = ntlmssp_update(client_ntlmssp_state,
1364 spnego.negTokenTarg.responseToken,
1367 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1368 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
1369 "ntlmssp_client_update, got: %s\n",
1370 nt_errstr(status)));
1371 x_fprintf(x_stdout, "BH\n");
1372 data_blob_free(&request);
1373 ntlmssp_end(&client_ntlmssp_state);
1377 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1378 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1379 spnego.negTokenTarg.supportedMech = (char *)OID_NTLMSSP;
1380 spnego.negTokenTarg.responseToken = request;
1381 spnego.negTokenTarg.mechListMIC = null_blob;
1383 write_spnego_data(&to_server, &spnego);
1384 data_blob_free(&request);
1386 to_server_base64 = base64_encode_data_blob(to_server);
1387 data_blob_free(&to_server);
1388 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1389 SAFE_FREE(to_server_base64);
1395 static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
1398 DATA_BLOB tkt, to_server;
1399 DATA_BLOB session_key_krb5 = data_blob(NULL, 0);
1404 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1407 if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
1408 (spnego.negTokenInit.mechListMIC.length == 0) ) {
1409 DEBUG(1, ("Did not get a principal for krb5\n"));
1413 principal = (char *)SMB_MALLOC(
1414 spnego.negTokenInit.mechListMIC.length+1);
1416 if (principal == NULL) {
1417 DEBUG(1, ("Could not malloc principal\n"));
1421 memcpy(principal, spnego.negTokenInit.mechListMIC.data,
1422 spnego.negTokenInit.mechListMIC.length);
1423 principal[spnego.negTokenInit.mechListMIC.length] = '\0';
1425 retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL);
1431 /* Let's try to first get the TGT, for that we need a
1434 if (opt_password == NULL) {
1435 DEBUG(10, ("Requesting password\n"));
1436 x_fprintf(x_stdout, "PW\n");
1440 pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
1442 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1443 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1447 retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL);
1450 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1455 data_blob_free(&session_key_krb5);
1459 reply.type = SPNEGO_NEG_TOKEN_INIT;
1460 reply.negTokenInit.mechTypes = my_mechs;
1461 reply.negTokenInit.reqFlags = 0;
1462 reply.negTokenInit.mechToken = tkt;
1463 reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
1465 len = write_spnego_data(&to_server, &reply);
1466 data_blob_free(&tkt);
1469 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1473 reply_base64 = base64_encode_data_blob(to_server);
1474 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1476 SAFE_FREE(reply_base64);
1477 data_blob_free(&to_server);
1478 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1482 static void manage_client_krb5_targ(SPNEGO_DATA spnego)
1484 switch (spnego.negTokenTarg.negResult) {
1485 case SPNEGO_ACCEPT_INCOMPLETE:
1486 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1487 x_fprintf(x_stdout, "BH\n");
1489 case SPNEGO_ACCEPT_COMPLETED:
1490 DEBUG(10, ("Accept completed\n"));
1491 x_fprintf(x_stdout, "AF\n");
1494 DEBUG(10, ("Rejected\n"));
1495 x_fprintf(x_stdout, "NA\n");
1498 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1499 x_fprintf(x_stdout, "AF\n");
1505 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1506 char *buf, int length)
1512 if (strlen(buf) <= 3) {
1513 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
1514 x_fprintf(x_stdout, "BH\n");
1518 request = base64_decode_data_blob(buf+3);
1520 if (strncmp(buf, "PW ", 3) == 0) {
1522 /* We asked for a password and obviously got it :-) */
1524 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
1526 if (opt_password == NULL) {
1527 DEBUG(1, ("Out of memory\n"));
1528 x_fprintf(x_stdout, "BH\n");
1529 data_blob_free(&request);
1533 x_fprintf(x_stdout, "OK\n");
1534 data_blob_free(&request);
1538 if ( (strncmp(buf, "TT ", 3) != 0) &&
1539 (strncmp(buf, "AF ", 3) != 0) &&
1540 (strncmp(buf, "NA ", 3) != 0) ) {
1541 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
1542 x_fprintf(x_stdout, "BH\n");
1543 data_blob_free(&request);
1547 /* So we got a server challenge to generate a SPNEGO
1548 client-to-server request... */
1550 len = read_spnego_data(request, &spnego);
1551 data_blob_free(&request);
1554 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
1555 x_fprintf(x_stdout, "BH\n");
1559 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
1561 /* The server offers a list of mechanisms */
1563 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
1565 while (*mechType != NULL) {
1568 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
1569 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
1570 if (manage_client_krb5_init(spnego))
1575 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
1576 if (manage_client_ntlmssp_init(spnego))
1583 DEBUG(1, ("Server offered no compatible mechanism\n"));
1584 x_fprintf(x_stdout, "BH\n");
1588 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
1590 if (spnego.negTokenTarg.supportedMech == NULL) {
1591 /* On accept/reject Windows does not send the
1592 mechanism anymore. Handle that here and
1593 shut down the mechanisms. */
1595 switch (spnego.negTokenTarg.negResult) {
1596 case SPNEGO_ACCEPT_COMPLETED:
1597 x_fprintf(x_stdout, "AF\n");
1600 x_fprintf(x_stdout, "NA\n");
1603 DEBUG(1, ("Got a negTokenTarg with no mech and an "
1604 "unknown negResult: %d\n",
1605 spnego.negTokenTarg.negResult));
1606 x_fprintf(x_stdout, "BH\n");
1609 ntlmssp_end(&client_ntlmssp_state);
1613 if (strcmp(spnego.negTokenTarg.supportedMech,
1614 OID_NTLMSSP) == 0) {
1615 manage_client_ntlmssp_targ(spnego);
1620 if (strcmp(spnego.negTokenTarg.supportedMech,
1621 OID_KERBEROS5_OLD) == 0) {
1622 manage_client_krb5_targ(spnego);
1629 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
1630 x_fprintf(x_stdout, "BH\n");
1634 free_spnego_data(&spnego);
1638 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1639 char *buf, int length)
1641 char *request, *parameter;
1642 static DATA_BLOB challenge;
1643 static DATA_BLOB lm_response;
1644 static DATA_BLOB nt_response;
1645 static char *full_username;
1646 static char *username;
1647 static char *domain;
1648 static char *plaintext_password;
1649 static BOOL ntlm_server_1_user_session_key;
1650 static BOOL ntlm_server_1_lm_session_key;
1652 if (strequal(buf, ".")) {
1653 if (!full_username && !username) {
1654 x_fprintf(x_stdout, "Error: No username supplied!\n");
1655 } else if (plaintext_password) {
1656 /* handle this request as plaintext */
1657 if (!full_username) {
1658 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1659 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1663 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1664 x_fprintf(x_stdout, "Authenticated: Yes\n");
1666 x_fprintf(x_stdout, "Authenticated: No\n");
1668 } else if (!lm_response.data && !nt_response.data) {
1669 x_fprintf(x_stdout, "Error: No password supplied!\n");
1670 } else if (!challenge.data) {
1671 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
1673 char *error_string = NULL;
1675 uchar user_session_key[16];
1678 if (full_username && !username) {
1680 fstring fstr_domain;
1682 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1683 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1684 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
1686 SAFE_FREE(username);
1688 username = smb_xstrdup(fstr_user);
1689 domain = smb_xstrdup(fstr_domain);
1693 domain = smb_xstrdup(get_winbind_domain());
1696 if (ntlm_server_1_lm_session_key)
1697 flags |= WBFLAG_PAM_LMKEY;
1699 if (ntlm_server_1_user_session_key)
1700 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1702 if (!NT_STATUS_IS_OK(
1703 contact_winbind_auth_crap(username,
1715 x_fprintf(x_stdout, "Authenticated: No\n");
1716 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
1717 SAFE_FREE(error_string);
1719 static char zeros[16];
1721 char *hex_user_session_key;
1723 x_fprintf(x_stdout, "Authenticated: Yes\n");
1725 if (ntlm_server_1_lm_session_key
1726 && (memcmp(zeros, lm_key,
1727 sizeof(lm_key)) != 0)) {
1728 hex_lm_key = hex_encode(NULL,
1729 (const unsigned char *)lm_key,
1731 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
1732 TALLOC_FREE(hex_lm_key);
1735 if (ntlm_server_1_user_session_key
1736 && (memcmp(zeros, user_session_key,
1737 sizeof(user_session_key)) != 0)) {
1738 hex_user_session_key = hex_encode(NULL,
1739 (const unsigned char *)user_session_key,
1740 sizeof(user_session_key));
1741 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
1742 TALLOC_FREE(hex_user_session_key);
1746 /* clear out the state */
1747 challenge = data_blob(NULL, 0);
1748 nt_response = data_blob(NULL, 0);
1749 lm_response = data_blob(NULL, 0);
1750 SAFE_FREE(full_username);
1751 SAFE_FREE(username);
1753 SAFE_FREE(plaintext_password);
1754 ntlm_server_1_user_session_key = False;
1755 ntlm_server_1_lm_session_key = False;
1756 x_fprintf(x_stdout, ".\n");
1763 /* Indicates a base64 encoded structure */
1764 parameter = strstr_m(request, ":: ");
1766 parameter = strstr_m(request, ": ");
1769 DEBUG(0, ("Parameter not found!\n"));
1770 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1787 base64_decode_inplace(parameter);
1790 if (strequal(request, "LANMAN-Challenge")) {
1791 challenge = strhex_to_data_blob(NULL, parameter);
1792 if (challenge.length != 8) {
1793 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
1795 (int)challenge.length);
1796 challenge = data_blob(NULL, 0);
1798 } else if (strequal(request, "NT-Response")) {
1799 nt_response = strhex_to_data_blob(NULL, parameter);
1800 if (nt_response.length < 24) {
1801 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
1803 (int)nt_response.length);
1804 nt_response = data_blob(NULL, 0);
1806 } else if (strequal(request, "LANMAN-Response")) {
1807 lm_response = strhex_to_data_blob(NULL, parameter);
1808 if (lm_response.length != 24) {
1809 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
1811 (int)lm_response.length);
1812 lm_response = data_blob(NULL, 0);
1814 } else if (strequal(request, "Password")) {
1815 plaintext_password = smb_xstrdup(parameter);
1816 } else if (strequal(request, "NT-Domain")) {
1817 domain = smb_xstrdup(parameter);
1818 } else if (strequal(request, "Username")) {
1819 username = smb_xstrdup(parameter);
1820 } else if (strequal(request, "Full-Username")) {
1821 full_username = smb_xstrdup(parameter);
1822 } else if (strequal(request, "Request-User-Session-Key")) {
1823 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1824 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1825 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1827 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
1831 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode helper_mode, char *buf, int length)
1833 char *request, *parameter;
1834 static DATA_BLOB new_nt_pswd;
1835 static DATA_BLOB old_nt_hash_enc;
1836 static DATA_BLOB new_lm_pswd;
1837 static DATA_BLOB old_lm_hash_enc;
1838 static char *full_username = NULL;
1839 static char *username = NULL;
1840 static char *domain = NULL;
1841 static char *newpswd = NULL;
1842 static char *oldpswd = NULL;
1844 if (strequal(buf, ".")) {
1845 if(newpswd && oldpswd) {
1846 uchar old_nt_hash[16];
1847 uchar old_lm_hash[16];
1848 uchar new_nt_hash[16];
1849 uchar new_lm_hash[16];
1851 new_nt_pswd = data_blob(NULL, 516);
1852 old_nt_hash_enc = data_blob(NULL, 16);
1854 /* Calculate the MD4 hash (NT compatible) of the
1856 E_md4hash(oldpswd, old_nt_hash);
1857 E_md4hash(newpswd, new_nt_hash);
1859 /* E_deshash returns false for 'long'
1860 passwords (> 14 DOS chars).
1862 Therefore, don't send a buffer
1863 encrypted with the truncated hash
1864 (it could allow an even easier
1865 attack on the password)
1867 Likewise, obey the admin's restriction
1870 if (lp_client_lanman_auth() &&
1871 E_deshash(newpswd, new_lm_hash) &&
1872 E_deshash(oldpswd, old_lm_hash)) {
1873 new_lm_pswd = data_blob(NULL, 516);
1874 old_lm_hash_enc = data_blob(NULL, 16);
1875 encode_pw_buffer(new_lm_pswd.data, newpswd,
1878 SamOEMhash(new_lm_pswd.data, old_nt_hash, 516);
1879 E_old_pw_hash(new_nt_hash, old_lm_hash,
1880 old_lm_hash_enc.data);
1882 new_lm_pswd.data = NULL;
1883 new_lm_pswd.length = 0;
1884 old_lm_hash_enc.data = NULL;
1885 old_lm_hash_enc.length = 0;
1888 encode_pw_buffer(new_nt_pswd.data, newpswd,
1891 SamOEMhash(new_nt_pswd.data, old_nt_hash, 516);
1892 E_old_pw_hash(new_nt_hash, old_nt_hash,
1893 old_nt_hash_enc.data);
1896 if (!full_username && !username) {
1897 x_fprintf(x_stdout, "Error: No username supplied!\n");
1898 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1899 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1900 x_fprintf(x_stdout, "Error: No NT or LM password "
1901 "blobs supplied!\n");
1903 char *error_string = NULL;
1905 if (full_username && !username) {
1907 fstring fstr_domain;
1909 if (!parse_ntlm_auth_domain_user(full_username,
1912 /* username might be 'tainted', don't
1913 * print into our new-line
1914 * deleimianted stream */
1915 x_fprintf(x_stdout, "Error: Could not "
1916 "parse into domain and "
1918 SAFE_FREE(username);
1919 username = smb_xstrdup(full_username);
1921 SAFE_FREE(username);
1923 username = smb_xstrdup(fstr_user);
1924 domain = smb_xstrdup(fstr_domain);
1929 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1936 x_fprintf(x_stdout, "Password-Change: No\n");
1937 x_fprintf(x_stdout, "Password-Change-Error: "
1938 "%s\n.\n", error_string);
1940 x_fprintf(x_stdout, "Password-Change: Yes\n");
1943 SAFE_FREE(error_string);
1945 /* clear out the state */
1946 new_nt_pswd = data_blob(NULL, 0);
1947 old_nt_hash_enc = data_blob(NULL, 0);
1948 new_lm_pswd = data_blob(NULL, 0);
1949 old_nt_hash_enc = data_blob(NULL, 0);
1950 SAFE_FREE(full_username);
1951 SAFE_FREE(username);
1955 x_fprintf(x_stdout, ".\n");
1962 /* Indicates a base64 encoded structure */
1963 parameter = strstr_m(request, ":: ");
1965 parameter = strstr_m(request, ": ");
1968 DEBUG(0, ("Parameter not found!\n"));
1969 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1985 base64_decode_inplace(parameter);
1988 if (strequal(request, "new-nt-password-blob")) {
1989 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
1990 if (new_nt_pswd.length != 516) {
1991 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
1992 "(got %d bytes, expected 516)\n.\n",
1994 (int)new_nt_pswd.length);
1995 new_nt_pswd = data_blob(NULL, 0);
1997 } else if (strequal(request, "old-nt-hash-blob")) {
1998 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
1999 if (old_nt_hash_enc.length != 16) {
2000 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2001 "(got %d bytes, expected 16)\n.\n",
2003 (int)old_nt_hash_enc.length);
2004 old_nt_hash_enc = data_blob(NULL, 0);
2006 } else if (strequal(request, "new-lm-password-blob")) {
2007 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2008 if (new_lm_pswd.length != 516) {
2009 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2010 "(got %d bytes, expected 516)\n.\n",
2012 (int)new_lm_pswd.length);
2013 new_lm_pswd = data_blob(NULL, 0);
2016 else if (strequal(request, "old-lm-hash-blob")) {
2017 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2018 if (old_lm_hash_enc.length != 16)
2020 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2021 "(got %d bytes, expected 16)\n.\n",
2023 (int)old_lm_hash_enc.length);
2024 old_lm_hash_enc = data_blob(NULL, 0);
2026 } else if (strequal(request, "nt-domain")) {
2027 domain = smb_xstrdup(parameter);
2028 } else if(strequal(request, "username")) {
2029 username = smb_xstrdup(parameter);
2030 } else if(strequal(request, "full-username")) {
2031 username = smb_xstrdup(parameter);
2032 } else if(strequal(request, "new-password")) {
2033 newpswd = smb_xstrdup(parameter);
2034 } else if (strequal(request, "old-password")) {
2035 oldpswd = smb_xstrdup(parameter);
2037 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2041 static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn)
2043 char buf[SQUID_BUFFER_SIZE+1];
2048 /* this is not a typo - x_fgets doesn't work too well under squid */
2049 if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
2050 if (ferror(stdin)) {
2051 DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
2052 strerror(ferror(stdin))));
2054 exit(1); /* BIIG buffer */
2059 c=(char *)memchr(buf,'\n',sizeof(buf)-1);
2068 DEBUG(2, ("Oversized message\n"));
2069 x_fprintf(x_stderr, "ERR\n");
2074 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2076 if (buf[0] == '\0') {
2077 DEBUG(2, ("Invalid Request\n"));
2078 x_fprintf(x_stderr, "ERR\n");
2082 fn(helper_mode, buf, length);
2086 static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
2087 /* initialize FDescs */
2088 x_setbuf(x_stdout, NULL);
2089 x_setbuf(x_stderr, NULL);
2091 manage_squid_request(stdio_mode, fn);
2096 /* Authenticate a user with a challenge/response */
2098 static BOOL check_auth_crap(void)
2103 char user_session_key[16];
2105 char *hex_user_session_key;
2107 static uint8 zeros[16];
2109 x_setbuf(x_stdout, NULL);
2112 flags |= WBFLAG_PAM_LMKEY;
2114 if (request_user_session_key)
2115 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2117 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2119 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2125 (unsigned char *)lm_key,
2126 (unsigned char *)user_session_key,
2127 &error_string, NULL);
2129 if (!NT_STATUS_IS_OK(nt_status)) {
2130 x_fprintf(x_stdout, "%s (0x%x)\n",
2132 NT_STATUS_V(nt_status));
2133 SAFE_FREE(error_string);
2138 && (memcmp(zeros, lm_key,
2139 sizeof(lm_key)) != 0)) {
2140 hex_lm_key = hex_encode(NULL, (const unsigned char *)lm_key,
2142 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2143 TALLOC_FREE(hex_lm_key);
2145 if (request_user_session_key
2146 && (memcmp(zeros, user_session_key,
2147 sizeof(user_session_key)) != 0)) {
2148 hex_user_session_key = hex_encode(NULL, (const unsigned char *)user_session_key,
2149 sizeof(user_session_key));
2150 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2151 TALLOC_FREE(hex_user_session_key);
2160 OPT_USERNAME = 1000,
2169 OPT_USER_SESSION_KEY,
2171 OPT_REQUIRE_MEMBERSHIP,
2172 OPT_USE_CACHED_CREDS
2175 int main(int argc, const char **argv)
2178 static const char *helper_protocol;
2179 static int diagnostics;
2181 static const char *hex_challenge;
2182 static const char *hex_lm_response;
2183 static const char *hex_nt_response;
2187 /* NOTE: DO NOT change this interface without considering the implications!
2188 This is an external interface, which other programs will use to interact
2192 /* We do not use single-letter command abbreviations, because they harm future
2193 interface stability. */
2195 struct poptOption long_options[] = {
2197 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2198 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2199 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2200 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2201 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2202 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2203 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2204 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2205 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2206 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2207 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2208 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
2209 { "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" },
2214 /* Samba client initialisation */
2219 /* Samba client initialisation */
2221 if (!lp_load(dyn_CONFIGFILE, True, False, False, True)) {
2222 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2223 dyn_CONFIGFILE, strerror(errno));
2229 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2231 /* Parse command line options */
2234 poptPrintHelp(pc, stderr, 0);
2238 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2239 POPT_CONTEXT_KEEP_FIRST);
2241 while((opt = poptGetNextOpt(pc)) != -1) {
2244 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2245 if (opt_challenge.length != 8) {
2246 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2248 (int)opt_challenge.length);
2253 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2254 if (opt_lm_response.length != 24) {
2255 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2257 (int)opt_lm_response.length);
2263 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2264 if (opt_nt_response.length < 24) {
2265 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2267 (int)opt_nt_response.length);
2272 case OPT_REQUIRE_MEMBERSHIP:
2273 if (StrnCaseCmp("S-", require_membership_of, 2) == 0) {
2274 require_membership_of_sid = require_membership_of;
2280 if (helper_protocol) {
2282 for (i=0; i<NUM_HELPER_MODES; i++) {
2283 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2284 squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
2288 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2290 for (i=0; i<NUM_HELPER_MODES; i++) {
2291 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2297 if (!opt_username) {
2298 x_fprintf(x_stderr, "username must be specified!\n\n");
2299 poptPrintHelp(pc, stderr, 0);
2303 if (opt_domain == NULL) {
2304 opt_domain = get_winbind_domain();
2307 if (opt_workstation == NULL) {
2308 opt_workstation = "";
2311 if (opt_challenge.length) {
2312 if (!check_auth_crap()) {
2318 if (!opt_password) {
2319 opt_password = getpass("password: ");
2323 if (!diagnose_ntlm_auth()) {
2329 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2330 if (!check_plaintext_auth(user, opt_password, True)) {
2337 poptFreeContext(pc);