[GLUE] Rsync SAMBA_3_0 SVN r25598 in order to create the v3-0-test branch.
[Samba.git] / source / utils / ntlm_auth.c
blob1f0a915574df018e49314390601285016721f192
1 /*
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.
26 #include "includes.h"
27 #include "utils/ntlm_auth.h"
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
32 #define SQUID_BUFFER_SIZE 2010
34 enum stdio_helper_mode {
35 SQUID_2_4_BASIC,
36 SQUID_2_5_BASIC,
37 SQUID_2_5_NTLMSSP,
38 NTLMSSP_CLIENT_1,
39 GSS_SPNEGO,
40 GSS_SPNEGO_CLIENT,
41 NTLM_SERVER_1,
42 NTLM_CHANGE_PASSWORD_1,
43 NUM_HELPER_MODES
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);
69 static const struct {
70 enum stdio_helper_mode mode;
71 const char *name;
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;
104 static BOOL got_sep;
105 static char sep;
107 if (got_sep)
108 return sep;
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;
121 got_sep = True;
123 if (!sep) {
124 d_printf("winbind separator was NULL!\n");
125 return *lp_winbind_separator();
128 return sep;
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);
191 return chal;
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,
198 fstring user)
201 char *p = strchr(domuser,winbind_separator());
203 if (!p) {
204 return False;
207 fstrcpy(user, p+1);
208 fstrcpy(domain, domuser);
209 domain[PTR_DIFF(p, domuser)] = 0;
210 strupper_m(domain);
212 return True;
215 static BOOL get_require_membership_sid(void) {
216 struct winbindd_request request;
217 struct winbindd_response response;
219 if (!require_membership_of) {
220 return True;
223 if (require_membership_of_sid) {
224 return True;
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));
237 return False;
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));
244 return False;
247 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
249 if (require_membership_of_sid)
250 return True;
252 return False;
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;
261 NSS_STATUS result;
263 if (!get_require_membership_sid()) {
264 return False;
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 pstrcpy(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);
290 } else {
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,
307 const char *domain,
308 const char *workstation,
309 const DATA_BLOB *challenge,
310 const DATA_BLOB *lm_response,
311 const DATA_BLOB *nt_response,
312 uint32 flags,
313 uint8 lm_key[8],
314 uint8 user_session_key[16],
315 char **error_string,
316 char **unix_name)
318 NTSTATUS nt_status;
319 NSS_STATUS result;
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,
341 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,
347 lm_response->data,
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,
354 nt_response->data,
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;
365 if (error_string)
366 *error_string = smb_xstrdup("Reading winbind reply failed!");
367 free_response(&response);
368 return nt_status;
371 nt_status = (NT_STATUS(response.data.auth.nt_status));
372 if (!NT_STATUS_IS_OK(nt_status)) {
373 if (error_string)
374 *error_string = smb_xstrdup(response.data.auth.error_string);
375 free_response(&response);
376 return nt_status;
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);
390 if (!*unix_name) {
391 free_response(&response);
392 return NT_STATUS_NO_MEMORY;
396 free_response(&response);
397 return nt_status;
400 /* contact server to change user password using auth crap */
401 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
402 const char *domain,
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,
407 char **error_string)
409 NTSTATUS nt_status;
410 NSS_STATUS result;
411 struct winbindd_request request;
412 struct winbindd_response response;
414 if (!get_require_membership_sid())
416 if(error_string)
417 *error_string = smb_xstrdup("Can't get membership sid.");
418 return NT_STATUS_INVALID_PARAMETER;
421 ZERO_STRUCT(request);
422 ZERO_STRUCT(response);
424 if(username != NULL)
425 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
426 if(domain != NULL)
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;
460 if (error_string)
461 *error_string = smb_xstrdup("Reading winbind reply failed!");
462 free_response(&response);
463 return nt_status;
466 nt_status = (NT_STATUS(response.data.auth.nt_status));
467 if (!NT_STATUS_IS_OK(nt_status))
469 if (error_string)
470 *error_string = smb_xstrdup(response.data.auth.error_string);
471 free_response(&response);
472 return nt_status;
475 free_response(&response);
477 return nt_status;
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] = { 0, };
483 NTSTATUS nt_status;
484 char *error_string;
485 uint8 lm_key[8];
486 uint8 user_sess_key[16];
487 char *unix_name;
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);
510 } else {
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;
518 return nt_status;
521 static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
523 NTSTATUS nt_status;
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,
532 NULL, NULL,
533 ntlmssp_state->user,
534 ntlmssp_state->user,
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);
543 } else {
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;
549 return nt_status;
552 static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_state)
554 NTSTATUS status;
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",
565 nt_errstr(status)));
566 ntlmssp_end(client_ntlmssp_state);
567 return status;
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",
574 nt_errstr(status)));
575 ntlmssp_end(client_ntlmssp_state);
576 return status;
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",
583 nt_errstr(status)));
584 ntlmssp_end(client_ntlmssp_state);
585 return status;
588 if (opt_password) {
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",
593 nt_errstr(status)));
594 ntlmssp_end(client_ntlmssp_state);
595 return status;
599 return NT_STATUS_OK;
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",
608 nt_errstr(status)));
609 return status;
612 /* Have we been given a local password, or should we ask winbind? */
613 if (opt_password) {
614 (*ntlmssp_state)->check_password = local_pw_check;
615 (*ntlmssp_state)->get_domain = lp_workgroup;
616 (*ntlmssp_state)->get_global_myname = global_myname;
617 } else {
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;
622 return NT_STATUS_OK;
625 /*******************************************************************
626 Used by firefox to drive NTLM auth to IIS servers.
627 *******************************************************************/
629 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
630 DATA_BLOB *reply)
632 struct winbindd_request wb_request;
633 struct winbindd_response wb_response;
634 NSS_STATUS result;
636 /* get winbindd to do the ntlmssp step on our behalf */
637 ZERO_STRUCT(wb_request);
638 ZERO_STRUCT(wb_response);
640 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
641 "%s%c%s", opt_domain, winbind_separator(), opt_username);
642 wb_request.data.ccache_ntlm_auth.uid = geteuid();
643 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
644 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
645 wb_request.extra_len = initial_msg.length + challenge_msg.length;
647 if (wb_request.extra_len > 0) {
648 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
649 if (wb_request.extra_data.data == NULL) {
650 return NT_STATUS_NO_MEMORY;
653 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
654 memcpy(wb_request.extra_data.data + initial_msg.length,
655 challenge_msg.data, challenge_msg.length);
658 result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
659 SAFE_FREE(wb_request.extra_data.data);
661 if (result != NSS_STATUS_SUCCESS) {
662 free_response(&wb_response);
663 return NT_STATUS_UNSUCCESSFUL;
666 if (reply) {
667 *reply = data_blob(wb_response.extra_data.data,
668 wb_response.data.ccache_ntlm_auth.auth_blob_len);
669 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
670 reply->data == NULL) {
671 free_response(&wb_response);
672 return NT_STATUS_NO_MEMORY;
676 free_response(&wb_response);
677 return NT_STATUS_MORE_PROCESSING_REQUIRED;
680 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
681 char *buf, int length)
683 static NTLMSSP_STATE *ntlmssp_state = NULL;
684 static char* want_feature_list = NULL;
685 static uint32 neg_flags = 0;
686 static BOOL have_session_key = False;
687 static DATA_BLOB session_key;
688 DATA_BLOB request, reply;
689 NTSTATUS nt_status;
691 if (strlen(buf) < 2) {
692 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
693 x_fprintf(x_stdout, "BH\n");
694 return;
697 if (strlen(buf) > 3) {
698 if(strncmp(buf, "SF ", 3) == 0){
699 DEBUG(10, ("Setting flags to negotioate\n"));
700 SAFE_FREE(want_feature_list);
701 want_feature_list = SMB_STRNDUP(buf+3, strlen(buf)-3);
702 x_fprintf(x_stdout, "OK\n");
703 return;
705 request = base64_decode_data_blob(buf + 3);
706 } else {
707 request = data_blob(NULL, 0);
710 if ((strncmp(buf, "PW ", 3) == 0)) {
711 /* The calling application wants us to use a local password (rather than winbindd) */
713 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
715 if (opt_password == NULL) {
716 DEBUG(1, ("Out of memory\n"));
717 x_fprintf(x_stdout, "BH\n");
718 data_blob_free(&request);
719 return;
722 x_fprintf(x_stdout, "OK\n");
723 data_blob_free(&request);
724 return;
727 if (strncmp(buf, "YR", 2) == 0) {
728 if (ntlmssp_state)
729 ntlmssp_end(&ntlmssp_state);
730 } else if (strncmp(buf, "KK", 2) == 0) {
732 } else if (strncmp(buf, "GF", 2) == 0) {
733 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
734 x_fprintf(x_stdout, "GF 0x%08lx\n", have_session_key?neg_flags:0l);
735 data_blob_free(&request);
736 return;
737 } else if (strncmp(buf, "GK", 2) == 0) {
738 DEBUG(10, ("Requested NTLMSSP session key\n"));
739 if(have_session_key) {
740 char *key64 = base64_encode_data_blob(session_key);
741 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
742 SAFE_FREE(key64);
743 } else {
744 x_fprintf(x_stdout, "BH\n");
747 data_blob_free(&request);
748 return;
749 } else {
750 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
751 x_fprintf(x_stdout, "BH\n");
752 return;
755 if (!ntlmssp_state) {
756 if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
757 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
758 return;
760 ntlmssp_want_feature_list(ntlmssp_state, want_feature_list);
763 DEBUG(10, ("got NTLMSSP packet:\n"));
764 dump_data(10, (const char *)request.data, request.length);
766 nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
768 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
769 char *reply_base64 = base64_encode_data_blob(reply);
770 x_fprintf(x_stdout, "TT %s\n", reply_base64);
771 SAFE_FREE(reply_base64);
772 data_blob_free(&reply);
773 DEBUG(10, ("NTLMSSP challenge\n"));
774 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
775 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
776 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
778 ntlmssp_end(&ntlmssp_state);
779 } else if (!NT_STATUS_IS_OK(nt_status)) {
780 x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
781 DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
782 } else {
783 x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context);
784 DEBUG(10, ("NTLMSSP OK!\n"));
786 if(have_session_key)
787 data_blob_free(&session_key);
788 session_key = data_blob(ntlmssp_state->session_key.data,
789 ntlmssp_state->session_key.length);
790 neg_flags = ntlmssp_state->neg_flags;
791 have_session_key = True;
794 data_blob_free(&request);
797 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
798 char *buf, int length)
800 /* The statics here are *HORRIBLE* and this entire concept
801 needs to be rewritten. Essentially it's using these statics
802 as the state in a state machine. BLEEEGH ! JRA. */
804 static NTLMSSP_STATE *ntlmssp_state = NULL;
805 static DATA_BLOB initial_message;
806 static char* want_feature_list = NULL;
807 static uint32 neg_flags = 0;
808 static BOOL have_session_key = False;
809 static DATA_BLOB session_key;
810 DATA_BLOB request, reply;
811 NTSTATUS nt_status;
812 BOOL first = False;
814 if (!opt_username || !*opt_username) {
815 x_fprintf(x_stderr, "username must be specified!\n\n");
816 exit(1);
819 if (strlen(buf) < 2) {
820 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
821 x_fprintf(x_stdout, "BH\n");
822 return;
825 if (strlen(buf) > 3) {
826 if(strncmp(buf, "SF ", 3) == 0) {
827 DEBUG(10, ("Looking for flags to negotiate\n"));
828 SAFE_FREE(want_feature_list);
829 want_feature_list = SMB_STRNDUP(buf+3, strlen(buf)-3);
830 x_fprintf(x_stdout, "OK\n");
831 return;
833 request = base64_decode_data_blob(buf + 3);
834 } else {
835 request = data_blob(NULL, 0);
838 if (strncmp(buf, "PW ", 3) == 0) {
839 /* We asked for a password and obviously got it :-) */
841 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
843 if (opt_password == NULL) {
844 DEBUG(1, ("Out of memory\n"));
845 x_fprintf(x_stdout, "BH\n");
846 data_blob_free(&request);
847 return;
850 x_fprintf(x_stdout, "OK\n");
851 data_blob_free(&request);
852 return;
855 if (!ntlmssp_state && use_cached_creds) {
856 /* check whether credentials are usable. */
857 DATA_BLOB empty_blob = data_blob(NULL, 0);
859 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
860 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
861 /* failed to use cached creds */
862 use_cached_creds = False;
866 if (opt_password == NULL && !use_cached_creds) {
868 /* Request a password from the calling process. After
869 sending it, the calling process should retry asking for the negotiate. */
871 DEBUG(10, ("Requesting password\n"));
872 x_fprintf(x_stdout, "PW\n");
873 return;
876 if (strncmp(buf, "YR", 2) == 0) {
877 if (ntlmssp_state)
878 ntlmssp_end(&ntlmssp_state);
879 } else if (strncmp(buf, "TT", 2) == 0) {
881 } else if (strncmp(buf, "GF", 2) == 0) {
882 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
883 x_fprintf(x_stdout, "GF 0x%08lx\n", have_session_key?neg_flags:0l);
884 data_blob_free(&request);
885 return;
886 } else if (strncmp(buf, "GK", 2) == 0 ) {
887 DEBUG(10, ("Requested session key\n"));
889 if(have_session_key) {
890 char *key64 = base64_encode_data_blob(session_key);
891 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
892 SAFE_FREE(key64);
894 else {
895 x_fprintf(x_stdout, "BH\n");
898 data_blob_free(&request);
899 return;
900 } else {
901 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
902 x_fprintf(x_stdout, "BH\n");
903 return;
906 if (!ntlmssp_state) {
907 if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_client(&ntlmssp_state))) {
908 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
909 return;
911 ntlmssp_want_feature_list(ntlmssp_state, want_feature_list);
912 first = True;
913 initial_message = data_blob(NULL, 0);
916 DEBUG(10, ("got NTLMSSP packet:\n"));
917 dump_data(10, (const char *)request.data, request.length);
919 if (use_cached_creds && !opt_password && !first) {
920 nt_status = do_ccache_ntlm_auth(initial_message, request, &reply);
921 } else {
922 nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
925 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
926 char *reply_base64 = base64_encode_data_blob(reply);
927 if (first) {
928 x_fprintf(x_stdout, "YR %s\n", reply_base64);
929 } else {
930 x_fprintf(x_stdout, "KK %s\n", reply_base64);
932 SAFE_FREE(reply_base64);
933 if (first) {
934 initial_message = reply;
935 } else {
936 data_blob_free(&reply);
938 DEBUG(10, ("NTLMSSP challenge\n"));
939 } else if (NT_STATUS_IS_OK(nt_status)) {
940 char *reply_base64 = base64_encode_data_blob(reply);
941 x_fprintf(x_stdout, "AF %s\n", reply_base64);
942 SAFE_FREE(reply_base64);
944 if(have_session_key)
945 data_blob_free(&session_key);
947 session_key = data_blob(ntlmssp_state->session_key.data,
948 ntlmssp_state->session_key.length);
949 neg_flags = ntlmssp_state->neg_flags;
950 have_session_key = True;
952 DEBUG(10, ("NTLMSSP OK!\n"));
953 if (ntlmssp_state)
954 ntlmssp_end(&ntlmssp_state);
955 } else {
956 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
957 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
958 if (ntlmssp_state)
959 ntlmssp_end(&ntlmssp_state);
962 data_blob_free(&request);
965 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
966 char *buf, int length)
968 char *user, *pass;
969 user=buf;
971 pass=(char *)memchr(buf,' ',length);
972 if (!pass) {
973 DEBUG(2, ("Password not found. Denying access\n"));
974 x_fprintf(x_stdout, "ERR\n");
975 return;
977 *pass='\0';
978 pass++;
980 if (stdio_helper_mode == SQUID_2_5_BASIC) {
981 rfc1738_unescape(user);
982 rfc1738_unescape(pass);
985 if (check_plaintext_auth(user, pass, False)) {
986 x_fprintf(x_stdout, "OK\n");
987 } else {
988 x_fprintf(x_stdout, "ERR\n");
992 static void offer_gss_spnego_mechs(void) {
994 DATA_BLOB token;
995 SPNEGO_DATA spnego;
996 ssize_t len;
997 char *reply_base64;
999 pstring principal;
1000 pstring myname_lower;
1002 ZERO_STRUCT(spnego);
1004 pstrcpy(myname_lower, global_myname());
1005 strlower_m(myname_lower);
1007 pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm());
1009 /* Server negTokenInit (mech offerings) */
1010 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1011 spnego.negTokenInit.mechTypes = SMB_XMALLOC_ARRAY(const char *, 2);
1012 #ifdef HAVE_KRB5
1013 spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
1014 spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
1015 spnego.negTokenInit.mechTypes[2] = NULL;
1016 #else
1017 spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
1018 spnego.negTokenInit.mechTypes[1] = NULL;
1019 #endif
1022 spnego.negTokenInit.mechListMIC = data_blob(principal,
1023 strlen(principal));
1025 len = write_spnego_data(&token, &spnego);
1026 free_spnego_data(&spnego);
1028 if (len == -1) {
1029 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1030 x_fprintf(x_stdout, "BH\n");
1031 return;
1034 reply_base64 = base64_encode_data_blob(token);
1035 x_fprintf(x_stdout, "TT %s *\n", reply_base64);
1037 SAFE_FREE(reply_base64);
1038 data_blob_free(&token);
1039 DEBUG(10, ("sent SPNEGO negTokenInit\n"));
1040 return;
1043 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1044 char *buf, int length)
1046 static NTLMSSP_STATE *ntlmssp_state = NULL;
1047 SPNEGO_DATA request, response;
1048 DATA_BLOB token;
1049 NTSTATUS status;
1050 ssize_t len;
1052 char *user = NULL;
1053 char *domain = NULL;
1055 const char *reply_code;
1056 char *reply_base64;
1057 pstring reply_argument;
1059 if (strlen(buf) < 2) {
1060 DEBUG(1, ("SPENGO query [%s] invalid", buf));
1061 x_fprintf(x_stdout, "BH\n");
1062 return;
1065 if (strncmp(buf, "YR", 2) == 0) {
1066 if (ntlmssp_state)
1067 ntlmssp_end(&ntlmssp_state);
1068 } else if (strncmp(buf, "KK", 2) == 0) {
1070 } else {
1071 DEBUG(1, ("SPENGO query [%s] invalid", buf));
1072 x_fprintf(x_stdout, "BH\n");
1073 return;
1076 if ( (strlen(buf) == 2)) {
1078 /* no client data, get the negTokenInit offering
1079 mechanisms */
1081 offer_gss_spnego_mechs();
1082 return;
1085 /* All subsequent requests have a blob. This might be negTokenInit or negTokenTarg */
1087 if (strlen(buf) <= 3) {
1088 DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
1089 x_fprintf(x_stdout, "BH\n");
1090 return;
1093 token = base64_decode_data_blob(buf + 3);
1094 len = read_spnego_data(token, &request);
1095 data_blob_free(&token);
1097 if (len == -1) {
1098 DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
1099 x_fprintf(x_stdout, "BH\n");
1100 return;
1103 if (request.type == SPNEGO_NEG_TOKEN_INIT) {
1105 /* Second request from Client. This is where the
1106 client offers its mechanism to use. */
1108 if ( (request.negTokenInit.mechTypes == NULL) ||
1109 (request.negTokenInit.mechTypes[0] == NULL) ) {
1110 DEBUG(1, ("Client did not offer any mechanism"));
1111 x_fprintf(x_stdout, "BH\n");
1112 return;
1115 status = NT_STATUS_UNSUCCESSFUL;
1116 if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
1118 if ( request.negTokenInit.mechToken.data == NULL ) {
1119 DEBUG(1, ("Client did not provide NTLMSSP data\n"));
1120 x_fprintf(x_stdout, "BH\n");
1121 return;
1124 if ( ntlmssp_state != NULL ) {
1125 DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
1126 "already got one\n"));
1127 x_fprintf(x_stdout, "BH\n");
1128 ntlmssp_end(&ntlmssp_state);
1129 return;
1132 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
1133 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1134 return;
1137 DEBUG(10, ("got NTLMSSP packet:\n"));
1138 dump_data(10, (const char *)request.negTokenInit.mechToken.data,
1139 request.negTokenInit.mechToken.length);
1141 response.type = SPNEGO_NEG_TOKEN_TARG;
1142 response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP);
1143 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
1145 status = ntlmssp_update(ntlmssp_state,
1146 request.negTokenInit.mechToken,
1147 &response.negTokenTarg.responseToken);
1150 #ifdef HAVE_KRB5
1151 if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
1153 TALLOC_CTX *mem_ctx = talloc_init("manage_gss_spnego_request");
1154 char *principal;
1155 DATA_BLOB ap_rep;
1156 DATA_BLOB session_key;
1158 if ( request.negTokenInit.mechToken.data == NULL ) {
1159 DEBUG(1, ("Client did not provide Kerberos data\n"));
1160 x_fprintf(x_stdout, "BH\n");
1161 return;
1164 response.type = SPNEGO_NEG_TOKEN_TARG;
1165 response.negTokenTarg.supportedMech = SMB_STRDUP(OID_KERBEROS5_OLD);
1166 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
1167 response.negTokenTarg.responseToken = data_blob(NULL, 0);
1169 status = ads_verify_ticket(mem_ctx, lp_realm(), 0,
1170 &request.negTokenInit.mechToken,
1171 &principal, NULL, &ap_rep,
1172 &session_key);
1174 talloc_destroy(mem_ctx);
1176 /* Now in "principal" we have the name we are
1177 authenticated as. */
1179 if (NT_STATUS_IS_OK(status)) {
1181 domain = strchr_m(principal, '@');
1183 if (domain == NULL) {
1184 DEBUG(1, ("Did not get a valid principal "
1185 "from ads_verify_ticket\n"));
1186 x_fprintf(x_stdout, "BH\n");
1187 return;
1190 *domain++ = '\0';
1191 domain = SMB_STRDUP(domain);
1192 user = SMB_STRDUP(principal);
1194 data_blob_free(&ap_rep);
1196 SAFE_FREE(principal);
1199 #endif
1201 } else {
1203 if ( (request.negTokenTarg.supportedMech == NULL) ||
1204 ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
1205 /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
1206 is the only one we support that sends this stuff */
1207 DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
1208 request.negTokenTarg.supportedMech));
1209 x_fprintf(x_stdout, "BH\n");
1210 return;
1213 if (request.negTokenTarg.responseToken.data == NULL) {
1214 DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
1215 x_fprintf(x_stdout, "BH\n");
1216 return;
1219 status = ntlmssp_update(ntlmssp_state,
1220 request.negTokenTarg.responseToken,
1221 &response.negTokenTarg.responseToken);
1223 response.type = SPNEGO_NEG_TOKEN_TARG;
1224 response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP);
1225 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
1227 if (NT_STATUS_IS_OK(status)) {
1228 user = SMB_STRDUP(ntlmssp_state->user);
1229 domain = SMB_STRDUP(ntlmssp_state->domain);
1230 ntlmssp_end(&ntlmssp_state);
1234 free_spnego_data(&request);
1236 if (NT_STATUS_IS_OK(status)) {
1237 response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
1238 reply_code = "AF";
1239 pstr_sprintf(reply_argument, "%s\\%s", domain, user);
1240 } else if (NT_STATUS_EQUAL(status,
1241 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1242 response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1243 reply_code = "TT";
1244 pstr_sprintf(reply_argument, "*");
1245 } else {
1246 response.negTokenTarg.negResult = SPNEGO_REJECT;
1247 reply_code = "NA";
1248 pstrcpy(reply_argument, nt_errstr(status));
1251 SAFE_FREE(user);
1252 SAFE_FREE(domain);
1254 len = write_spnego_data(&token, &response);
1255 free_spnego_data(&response);
1257 if (len == -1) {
1258 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1259 x_fprintf(x_stdout, "BH\n");
1260 return;
1263 reply_base64 = base64_encode_data_blob(token);
1265 x_fprintf(x_stdout, "%s %s %s\n",
1266 reply_code, reply_base64, reply_argument);
1268 SAFE_FREE(reply_base64);
1269 data_blob_free(&token);
1271 return;
1274 static NTLMSSP_STATE *client_ntlmssp_state = NULL;
1276 static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego)
1278 NTSTATUS status;
1279 DATA_BLOB null_blob = data_blob(NULL, 0);
1280 DATA_BLOB to_server;
1281 char *to_server_base64;
1282 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1284 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1286 if (client_ntlmssp_state != NULL) {
1287 DEBUG(1, ("Request for initial SPNEGO request where "
1288 "we already have a state\n"));
1289 return False;
1292 if (!client_ntlmssp_state) {
1293 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1294 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1295 return False;
1300 if (opt_password == NULL) {
1302 /* Request a password from the calling process. After
1303 sending it, the calling process should retry with
1304 the negTokenInit. */
1306 DEBUG(10, ("Requesting password\n"));
1307 x_fprintf(x_stdout, "PW\n");
1308 return True;
1311 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1312 spnego.negTokenInit.mechTypes = my_mechs;
1313 spnego.negTokenInit.reqFlags = 0;
1314 spnego.negTokenInit.mechListMIC = null_blob;
1316 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1317 &spnego.negTokenInit.mechToken);
1319 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1320 NT_STATUS_IS_OK(status)) ) {
1321 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1322 nt_errstr(status)));
1323 ntlmssp_end(&client_ntlmssp_state);
1324 return False;
1327 write_spnego_data(&to_server, &spnego);
1328 data_blob_free(&spnego.negTokenInit.mechToken);
1330 to_server_base64 = base64_encode_data_blob(to_server);
1331 data_blob_free(&to_server);
1332 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1333 SAFE_FREE(to_server_base64);
1334 return True;
1337 static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
1339 NTSTATUS status;
1340 DATA_BLOB null_blob = data_blob(NULL, 0);
1341 DATA_BLOB request;
1342 DATA_BLOB to_server;
1343 char *to_server_base64;
1345 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1347 if (client_ntlmssp_state == NULL) {
1348 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1349 x_fprintf(x_stdout, "BH\n");
1350 return;
1353 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1354 x_fprintf(x_stdout, "NA\n");
1355 ntlmssp_end(&client_ntlmssp_state);
1356 return;
1359 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1360 x_fprintf(x_stdout, "AF\n");
1361 ntlmssp_end(&client_ntlmssp_state);
1362 return;
1365 status = ntlmssp_update(client_ntlmssp_state,
1366 spnego.negTokenTarg.responseToken,
1367 &request);
1369 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1370 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
1371 "ntlmssp_client_update, got: %s\n",
1372 nt_errstr(status)));
1373 x_fprintf(x_stdout, "BH\n");
1374 data_blob_free(&request);
1375 ntlmssp_end(&client_ntlmssp_state);
1376 return;
1379 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1380 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1381 spnego.negTokenTarg.supportedMech = (char *)OID_NTLMSSP;
1382 spnego.negTokenTarg.responseToken = request;
1383 spnego.negTokenTarg.mechListMIC = null_blob;
1385 write_spnego_data(&to_server, &spnego);
1386 data_blob_free(&request);
1388 to_server_base64 = base64_encode_data_blob(to_server);
1389 data_blob_free(&to_server);
1390 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1391 SAFE_FREE(to_server_base64);
1392 return;
1395 #ifdef HAVE_KRB5
1397 static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
1399 char *principal;
1400 DATA_BLOB tkt, to_server;
1401 DATA_BLOB session_key_krb5 = data_blob(NULL, 0);
1402 SPNEGO_DATA reply;
1403 char *reply_base64;
1404 int retval;
1406 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1407 ssize_t len;
1409 if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
1410 (spnego.negTokenInit.mechListMIC.length == 0) ) {
1411 DEBUG(1, ("Did not get a principal for krb5\n"));
1412 return False;
1415 principal = (char *)SMB_MALLOC(
1416 spnego.negTokenInit.mechListMIC.length+1);
1418 if (principal == NULL) {
1419 DEBUG(1, ("Could not malloc principal\n"));
1420 return False;
1423 memcpy(principal, spnego.negTokenInit.mechListMIC.data,
1424 spnego.negTokenInit.mechListMIC.length);
1425 principal[spnego.negTokenInit.mechListMIC.length] = '\0';
1427 retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL, NULL);
1429 if (retval) {
1431 pstring user;
1433 /* Let's try to first get the TGT, for that we need a
1434 password. */
1436 if (opt_password == NULL) {
1437 DEBUG(10, ("Requesting password\n"));
1438 x_fprintf(x_stdout, "PW\n");
1439 return True;
1442 pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
1444 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1445 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1446 return False;
1449 retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL, NULL);
1451 if (retval) {
1452 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1453 return False;
1457 data_blob_free(&session_key_krb5);
1459 ZERO_STRUCT(reply);
1461 reply.type = SPNEGO_NEG_TOKEN_INIT;
1462 reply.negTokenInit.mechTypes = my_mechs;
1463 reply.negTokenInit.reqFlags = 0;
1464 reply.negTokenInit.mechToken = tkt;
1465 reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
1467 len = write_spnego_data(&to_server, &reply);
1468 data_blob_free(&tkt);
1470 if (len == -1) {
1471 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1472 return False;
1475 reply_base64 = base64_encode_data_blob(to_server);
1476 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1478 SAFE_FREE(reply_base64);
1479 data_blob_free(&to_server);
1480 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1481 return True;
1484 static void manage_client_krb5_targ(SPNEGO_DATA spnego)
1486 switch (spnego.negTokenTarg.negResult) {
1487 case SPNEGO_ACCEPT_INCOMPLETE:
1488 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1489 x_fprintf(x_stdout, "BH\n");
1490 break;
1491 case SPNEGO_ACCEPT_COMPLETED:
1492 DEBUG(10, ("Accept completed\n"));
1493 x_fprintf(x_stdout, "AF\n");
1494 break;
1495 case SPNEGO_REJECT:
1496 DEBUG(10, ("Rejected\n"));
1497 x_fprintf(x_stdout, "NA\n");
1498 break;
1499 default:
1500 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1501 x_fprintf(x_stdout, "AF\n");
1505 #endif
1507 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1508 char *buf, int length)
1510 DATA_BLOB request;
1511 SPNEGO_DATA spnego;
1512 ssize_t len;
1514 if (!opt_username || !*opt_username) {
1515 x_fprintf(x_stderr, "username must be specified!\n\n");
1516 exit(1);
1519 if (strlen(buf) <= 3) {
1520 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
1521 x_fprintf(x_stdout, "BH\n");
1522 return;
1525 request = base64_decode_data_blob(buf+3);
1527 if (strncmp(buf, "PW ", 3) == 0) {
1529 /* We asked for a password and obviously got it :-) */
1531 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
1533 if (opt_password == NULL) {
1534 DEBUG(1, ("Out of memory\n"));
1535 x_fprintf(x_stdout, "BH\n");
1536 data_blob_free(&request);
1537 return;
1540 x_fprintf(x_stdout, "OK\n");
1541 data_blob_free(&request);
1542 return;
1545 if ( (strncmp(buf, "TT ", 3) != 0) &&
1546 (strncmp(buf, "AF ", 3) != 0) &&
1547 (strncmp(buf, "NA ", 3) != 0) ) {
1548 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
1549 x_fprintf(x_stdout, "BH\n");
1550 data_blob_free(&request);
1551 return;
1554 /* So we got a server challenge to generate a SPNEGO
1555 client-to-server request... */
1557 len = read_spnego_data(request, &spnego);
1558 data_blob_free(&request);
1560 if (len == -1) {
1561 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
1562 x_fprintf(x_stdout, "BH\n");
1563 return;
1566 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
1568 /* The server offers a list of mechanisms */
1570 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
1572 while (*mechType != NULL) {
1574 #ifdef HAVE_KRB5
1575 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
1576 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
1577 if (manage_client_krb5_init(spnego))
1578 goto out;
1580 #endif
1582 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
1583 if (manage_client_ntlmssp_init(spnego))
1584 goto out;
1587 mechType++;
1590 DEBUG(1, ("Server offered no compatible mechanism\n"));
1591 x_fprintf(x_stdout, "BH\n");
1592 return;
1595 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
1597 if (spnego.negTokenTarg.supportedMech == NULL) {
1598 /* On accept/reject Windows does not send the
1599 mechanism anymore. Handle that here and
1600 shut down the mechanisms. */
1602 switch (spnego.negTokenTarg.negResult) {
1603 case SPNEGO_ACCEPT_COMPLETED:
1604 x_fprintf(x_stdout, "AF\n");
1605 break;
1606 case SPNEGO_REJECT:
1607 x_fprintf(x_stdout, "NA\n");
1608 break;
1609 default:
1610 DEBUG(1, ("Got a negTokenTarg with no mech and an "
1611 "unknown negResult: %d\n",
1612 spnego.negTokenTarg.negResult));
1613 x_fprintf(x_stdout, "BH\n");
1616 ntlmssp_end(&client_ntlmssp_state);
1617 goto out;
1620 if (strcmp(spnego.negTokenTarg.supportedMech,
1621 OID_NTLMSSP) == 0) {
1622 manage_client_ntlmssp_targ(spnego);
1623 goto out;
1626 #if HAVE_KRB5
1627 if (strcmp(spnego.negTokenTarg.supportedMech,
1628 OID_KERBEROS5_OLD) == 0) {
1629 manage_client_krb5_targ(spnego);
1630 goto out;
1632 #endif
1636 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
1637 x_fprintf(x_stdout, "BH\n");
1638 return;
1640 out:
1641 free_spnego_data(&spnego);
1642 return;
1645 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1646 char *buf, int length)
1648 char *request, *parameter;
1649 static DATA_BLOB challenge;
1650 static DATA_BLOB lm_response;
1651 static DATA_BLOB nt_response;
1652 static char *full_username;
1653 static char *username;
1654 static char *domain;
1655 static char *plaintext_password;
1656 static BOOL ntlm_server_1_user_session_key;
1657 static BOOL ntlm_server_1_lm_session_key;
1659 if (strequal(buf, ".")) {
1660 if (!full_username && !username) {
1661 x_fprintf(x_stdout, "Error: No username supplied!\n");
1662 } else if (plaintext_password) {
1663 /* handle this request as plaintext */
1664 if (!full_username) {
1665 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1666 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1667 return;
1670 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1671 x_fprintf(x_stdout, "Authenticated: Yes\n");
1672 } else {
1673 x_fprintf(x_stdout, "Authenticated: No\n");
1675 } else if (!lm_response.data && !nt_response.data) {
1676 x_fprintf(x_stdout, "Error: No password supplied!\n");
1677 } else if (!challenge.data) {
1678 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
1679 } else {
1680 char *error_string = NULL;
1681 uchar lm_key[8];
1682 uchar user_session_key[16];
1683 uint32 flags = 0;
1685 if (full_username && !username) {
1686 fstring fstr_user;
1687 fstring fstr_domain;
1689 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1690 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1691 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
1693 SAFE_FREE(username);
1694 SAFE_FREE(domain);
1695 username = smb_xstrdup(fstr_user);
1696 domain = smb_xstrdup(fstr_domain);
1699 if (!domain) {
1700 domain = smb_xstrdup(get_winbind_domain());
1703 if (ntlm_server_1_lm_session_key)
1704 flags |= WBFLAG_PAM_LMKEY;
1706 if (ntlm_server_1_user_session_key)
1707 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1709 if (!NT_STATUS_IS_OK(
1710 contact_winbind_auth_crap(username,
1711 domain,
1712 global_myname(),
1713 &challenge,
1714 &lm_response,
1715 &nt_response,
1716 flags,
1717 lm_key,
1718 user_session_key,
1719 &error_string,
1720 NULL))) {
1722 x_fprintf(x_stdout, "Authenticated: No\n");
1723 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
1724 SAFE_FREE(error_string);
1725 } else {
1726 static char zeros[16];
1727 char *hex_lm_key;
1728 char *hex_user_session_key;
1730 x_fprintf(x_stdout, "Authenticated: Yes\n");
1732 if (ntlm_server_1_lm_session_key
1733 && (memcmp(zeros, lm_key,
1734 sizeof(lm_key)) != 0)) {
1735 hex_lm_key = hex_encode(NULL,
1736 (const unsigned char *)lm_key,
1737 sizeof(lm_key));
1738 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
1739 TALLOC_FREE(hex_lm_key);
1742 if (ntlm_server_1_user_session_key
1743 && (memcmp(zeros, user_session_key,
1744 sizeof(user_session_key)) != 0)) {
1745 hex_user_session_key = hex_encode(NULL,
1746 (const unsigned char *)user_session_key,
1747 sizeof(user_session_key));
1748 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
1749 TALLOC_FREE(hex_user_session_key);
1753 /* clear out the state */
1754 challenge = data_blob(NULL, 0);
1755 nt_response = data_blob(NULL, 0);
1756 lm_response = data_blob(NULL, 0);
1757 SAFE_FREE(full_username);
1758 SAFE_FREE(username);
1759 SAFE_FREE(domain);
1760 SAFE_FREE(plaintext_password);
1761 ntlm_server_1_user_session_key = False;
1762 ntlm_server_1_lm_session_key = False;
1763 x_fprintf(x_stdout, ".\n");
1765 return;
1768 request = buf;
1770 /* Indicates a base64 encoded structure */
1771 parameter = strstr_m(request, ":: ");
1772 if (!parameter) {
1773 parameter = strstr_m(request, ": ");
1775 if (!parameter) {
1776 DEBUG(0, ("Parameter not found!\n"));
1777 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1778 return;
1781 parameter[0] ='\0';
1782 parameter++;
1783 parameter[0] ='\0';
1784 parameter++;
1786 } else {
1787 parameter[0] ='\0';
1788 parameter++;
1789 parameter[0] ='\0';
1790 parameter++;
1791 parameter[0] ='\0';
1792 parameter++;
1794 base64_decode_inplace(parameter);
1797 if (strequal(request, "LANMAN-Challenge")) {
1798 challenge = strhex_to_data_blob(NULL, parameter);
1799 if (challenge.length != 8) {
1800 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
1801 parameter,
1802 (int)challenge.length);
1803 challenge = data_blob(NULL, 0);
1805 } else if (strequal(request, "NT-Response")) {
1806 nt_response = strhex_to_data_blob(NULL, parameter);
1807 if (nt_response.length < 24) {
1808 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
1809 parameter,
1810 (int)nt_response.length);
1811 nt_response = data_blob(NULL, 0);
1813 } else if (strequal(request, "LANMAN-Response")) {
1814 lm_response = strhex_to_data_blob(NULL, parameter);
1815 if (lm_response.length != 24) {
1816 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
1817 parameter,
1818 (int)lm_response.length);
1819 lm_response = data_blob(NULL, 0);
1821 } else if (strequal(request, "Password")) {
1822 plaintext_password = smb_xstrdup(parameter);
1823 } else if (strequal(request, "NT-Domain")) {
1824 domain = smb_xstrdup(parameter);
1825 } else if (strequal(request, "Username")) {
1826 username = smb_xstrdup(parameter);
1827 } else if (strequal(request, "Full-Username")) {
1828 full_username = smb_xstrdup(parameter);
1829 } else if (strequal(request, "Request-User-Session-Key")) {
1830 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1831 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1832 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1833 } else {
1834 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
1838 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode helper_mode, char *buf, int length)
1840 char *request, *parameter;
1841 static DATA_BLOB new_nt_pswd;
1842 static DATA_BLOB old_nt_hash_enc;
1843 static DATA_BLOB new_lm_pswd;
1844 static DATA_BLOB old_lm_hash_enc;
1845 static char *full_username = NULL;
1846 static char *username = NULL;
1847 static char *domain = NULL;
1848 static char *newpswd = NULL;
1849 static char *oldpswd = NULL;
1851 if (strequal(buf, ".")) {
1852 if(newpswd && oldpswd) {
1853 uchar old_nt_hash[16];
1854 uchar old_lm_hash[16];
1855 uchar new_nt_hash[16];
1856 uchar new_lm_hash[16];
1858 new_nt_pswd = data_blob(NULL, 516);
1859 old_nt_hash_enc = data_blob(NULL, 16);
1861 /* Calculate the MD4 hash (NT compatible) of the
1862 * password */
1863 E_md4hash(oldpswd, old_nt_hash);
1864 E_md4hash(newpswd, new_nt_hash);
1866 /* E_deshash returns false for 'long'
1867 passwords (> 14 DOS chars).
1869 Therefore, don't send a buffer
1870 encrypted with the truncated hash
1871 (it could allow an even easier
1872 attack on the password)
1874 Likewise, obey the admin's restriction
1877 if (lp_client_lanman_auth() &&
1878 E_deshash(newpswd, new_lm_hash) &&
1879 E_deshash(oldpswd, old_lm_hash)) {
1880 new_lm_pswd = data_blob(NULL, 516);
1881 old_lm_hash_enc = data_blob(NULL, 16);
1882 encode_pw_buffer(new_lm_pswd.data, newpswd,
1883 STR_UNICODE);
1885 SamOEMhash(new_lm_pswd.data, old_nt_hash, 516);
1886 E_old_pw_hash(new_nt_hash, old_lm_hash,
1887 old_lm_hash_enc.data);
1888 } else {
1889 new_lm_pswd.data = NULL;
1890 new_lm_pswd.length = 0;
1891 old_lm_hash_enc.data = NULL;
1892 old_lm_hash_enc.length = 0;
1895 encode_pw_buffer(new_nt_pswd.data, newpswd,
1896 STR_UNICODE);
1898 SamOEMhash(new_nt_pswd.data, old_nt_hash, 516);
1899 E_old_pw_hash(new_nt_hash, old_nt_hash,
1900 old_nt_hash_enc.data);
1903 if (!full_username && !username) {
1904 x_fprintf(x_stdout, "Error: No username supplied!\n");
1905 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1906 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1907 x_fprintf(x_stdout, "Error: No NT or LM password "
1908 "blobs supplied!\n");
1909 } else {
1910 char *error_string = NULL;
1912 if (full_username && !username) {
1913 fstring fstr_user;
1914 fstring fstr_domain;
1916 if (!parse_ntlm_auth_domain_user(full_username,
1917 fstr_user,
1918 fstr_domain)) {
1919 /* username might be 'tainted', don't
1920 * print into our new-line
1921 * deleimianted stream */
1922 x_fprintf(x_stdout, "Error: Could not "
1923 "parse into domain and "
1924 "username\n");
1925 SAFE_FREE(username);
1926 username = smb_xstrdup(full_username);
1927 } else {
1928 SAFE_FREE(username);
1929 SAFE_FREE(domain);
1930 username = smb_xstrdup(fstr_user);
1931 domain = smb_xstrdup(fstr_domain);
1936 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1937 username, domain,
1938 new_nt_pswd,
1939 old_nt_hash_enc,
1940 new_lm_pswd,
1941 old_lm_hash_enc,
1942 &error_string))) {
1943 x_fprintf(x_stdout, "Password-Change: No\n");
1944 x_fprintf(x_stdout, "Password-Change-Error: "
1945 "%s\n.\n", error_string);
1946 } else {
1947 x_fprintf(x_stdout, "Password-Change: Yes\n");
1950 SAFE_FREE(error_string);
1952 /* clear out the state */
1953 new_nt_pswd = data_blob(NULL, 0);
1954 old_nt_hash_enc = data_blob(NULL, 0);
1955 new_lm_pswd = data_blob(NULL, 0);
1956 old_nt_hash_enc = data_blob(NULL, 0);
1957 SAFE_FREE(full_username);
1958 SAFE_FREE(username);
1959 SAFE_FREE(domain);
1960 SAFE_FREE(newpswd);
1961 SAFE_FREE(oldpswd);
1962 x_fprintf(x_stdout, ".\n");
1964 return;
1967 request = buf;
1969 /* Indicates a base64 encoded structure */
1970 parameter = strstr_m(request, ":: ");
1971 if (!parameter) {
1972 parameter = strstr_m(request, ": ");
1974 if (!parameter) {
1975 DEBUG(0, ("Parameter not found!\n"));
1976 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1977 return;
1980 parameter[0] ='\0';
1981 parameter++;
1982 parameter[0] ='\0';
1983 parameter++;
1984 } else {
1985 parameter[0] ='\0';
1986 parameter++;
1987 parameter[0] ='\0';
1988 parameter++;
1989 parameter[0] ='\0';
1990 parameter++;
1992 base64_decode_inplace(parameter);
1995 if (strequal(request, "new-nt-password-blob")) {
1996 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
1997 if (new_nt_pswd.length != 516) {
1998 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
1999 "(got %d bytes, expected 516)\n.\n",
2000 parameter,
2001 (int)new_nt_pswd.length);
2002 new_nt_pswd = data_blob(NULL, 0);
2004 } else if (strequal(request, "old-nt-hash-blob")) {
2005 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2006 if (old_nt_hash_enc.length != 16) {
2007 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2008 "(got %d bytes, expected 16)\n.\n",
2009 parameter,
2010 (int)old_nt_hash_enc.length);
2011 old_nt_hash_enc = data_blob(NULL, 0);
2013 } else if (strequal(request, "new-lm-password-blob")) {
2014 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2015 if (new_lm_pswd.length != 516) {
2016 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2017 "(got %d bytes, expected 516)\n.\n",
2018 parameter,
2019 (int)new_lm_pswd.length);
2020 new_lm_pswd = data_blob(NULL, 0);
2023 else if (strequal(request, "old-lm-hash-blob")) {
2024 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2025 if (old_lm_hash_enc.length != 16)
2027 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2028 "(got %d bytes, expected 16)\n.\n",
2029 parameter,
2030 (int)old_lm_hash_enc.length);
2031 old_lm_hash_enc = data_blob(NULL, 0);
2033 } else if (strequal(request, "nt-domain")) {
2034 domain = smb_xstrdup(parameter);
2035 } else if(strequal(request, "username")) {
2036 username = smb_xstrdup(parameter);
2037 } else if(strequal(request, "full-username")) {
2038 username = smb_xstrdup(parameter);
2039 } else if(strequal(request, "new-password")) {
2040 newpswd = smb_xstrdup(parameter);
2041 } else if (strequal(request, "old-password")) {
2042 oldpswd = smb_xstrdup(parameter);
2043 } else {
2044 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2048 static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn)
2050 char buf[SQUID_BUFFER_SIZE+1];
2051 int length;
2052 char *c;
2053 static BOOL err;
2055 /* this is not a typo - x_fgets doesn't work too well under squid */
2056 if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
2057 if (ferror(stdin)) {
2058 DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
2059 strerror(ferror(stdin))));
2061 exit(1); /* BIIG buffer */
2063 exit(0);
2066 c=(char *)memchr(buf,'\n',sizeof(buf)-1);
2067 if (c) {
2068 *c = '\0';
2069 length = c-buf;
2070 } else {
2071 err = 1;
2072 return;
2074 if (err) {
2075 DEBUG(2, ("Oversized message\n"));
2076 x_fprintf(x_stderr, "ERR\n");
2077 err = 0;
2078 return;
2081 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2083 if (buf[0] == '\0') {
2084 DEBUG(2, ("Invalid Request\n"));
2085 x_fprintf(x_stderr, "ERR\n");
2086 return;
2089 fn(helper_mode, buf, length);
2093 static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
2094 /* initialize FDescs */
2095 x_setbuf(x_stdout, NULL);
2096 x_setbuf(x_stderr, NULL);
2097 while(1) {
2098 manage_squid_request(stdio_mode, fn);
2103 /* Authenticate a user with a challenge/response */
2105 static BOOL check_auth_crap(void)
2107 NTSTATUS nt_status;
2108 uint32 flags = 0;
2109 char lm_key[8];
2110 char user_session_key[16];
2111 char *hex_lm_key;
2112 char *hex_user_session_key;
2113 char *error_string;
2114 static uint8 zeros[16];
2116 x_setbuf(x_stdout, NULL);
2118 if (request_lm_key)
2119 flags |= WBFLAG_PAM_LMKEY;
2121 if (request_user_session_key)
2122 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2124 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2126 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2127 opt_workstation,
2128 &opt_challenge,
2129 &opt_lm_response,
2130 &opt_nt_response,
2131 flags,
2132 (unsigned char *)lm_key,
2133 (unsigned char *)user_session_key,
2134 &error_string, NULL);
2136 if (!NT_STATUS_IS_OK(nt_status)) {
2137 x_fprintf(x_stdout, "%s (0x%x)\n",
2138 error_string,
2139 NT_STATUS_V(nt_status));
2140 SAFE_FREE(error_string);
2141 return False;
2144 if (request_lm_key
2145 && (memcmp(zeros, lm_key,
2146 sizeof(lm_key)) != 0)) {
2147 hex_lm_key = hex_encode(NULL, (const unsigned char *)lm_key,
2148 sizeof(lm_key));
2149 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2150 TALLOC_FREE(hex_lm_key);
2152 if (request_user_session_key
2153 && (memcmp(zeros, user_session_key,
2154 sizeof(user_session_key)) != 0)) {
2155 hex_user_session_key = hex_encode(NULL, (const unsigned char *)user_session_key,
2156 sizeof(user_session_key));
2157 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2158 TALLOC_FREE(hex_user_session_key);
2161 return True;
2164 /* Main program */
2166 enum {
2167 OPT_USERNAME = 1000,
2168 OPT_DOMAIN,
2169 OPT_WORKSTATION,
2170 OPT_CHALLENGE,
2171 OPT_RESPONSE,
2172 OPT_LM,
2173 OPT_NT,
2174 OPT_PASSWORD,
2175 OPT_LM_KEY,
2176 OPT_USER_SESSION_KEY,
2177 OPT_DIAGNOSTICS,
2178 OPT_REQUIRE_MEMBERSHIP,
2179 OPT_USE_CACHED_CREDS
2182 int main(int argc, const char **argv)
2184 int opt;
2185 static const char *helper_protocol;
2186 static int diagnostics;
2188 static const char *hex_challenge;
2189 static const char *hex_lm_response;
2190 static const char *hex_nt_response;
2192 poptContext pc;
2194 /* NOTE: DO NOT change this interface without considering the implications!
2195 This is an external interface, which other programs will use to interact
2196 with this helper.
2199 /* We do not use single-letter command abbreviations, because they harm future
2200 interface stability. */
2202 struct poptOption long_options[] = {
2203 POPT_AUTOHELP
2204 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2205 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2206 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2207 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2208 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2209 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2210 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2211 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2212 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2213 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2214 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2215 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
2216 { "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" },
2217 POPT_COMMON_SAMBA
2218 POPT_TABLEEND
2221 /* Samba client initialisation */
2222 load_case_tables();
2224 dbf = x_stderr;
2226 /* Samba client initialisation */
2228 if (!lp_load(dyn_CONFIGFILE, True, False, False, True)) {
2229 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2230 dyn_CONFIGFILE, strerror(errno));
2231 exit(1);
2234 /* Parse options */
2236 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2238 /* Parse command line options */
2240 if (argc == 1) {
2241 poptPrintHelp(pc, stderr, 0);
2242 return 1;
2245 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2246 POPT_CONTEXT_KEEP_FIRST);
2248 while((opt = poptGetNextOpt(pc)) != -1) {
2249 switch (opt) {
2250 case OPT_CHALLENGE:
2251 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2252 if (opt_challenge.length != 8) {
2253 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2254 hex_challenge,
2255 (int)opt_challenge.length);
2256 exit(1);
2258 break;
2259 case OPT_LM:
2260 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2261 if (opt_lm_response.length != 24) {
2262 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2263 hex_lm_response,
2264 (int)opt_lm_response.length);
2265 exit(1);
2267 break;
2269 case OPT_NT:
2270 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2271 if (opt_nt_response.length < 24) {
2272 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2273 hex_nt_response,
2274 (int)opt_nt_response.length);
2275 exit(1);
2277 break;
2279 case OPT_REQUIRE_MEMBERSHIP:
2280 if (StrnCaseCmp("S-", require_membership_of, 2) == 0) {
2281 require_membership_of_sid = require_membership_of;
2283 break;
2287 if (opt_username) {
2288 char *domain = SMB_STRDUP(opt_username);
2289 char *p = strchr_m(domain, *lp_winbind_separator());
2290 if (p) {
2291 opt_username = p+1;
2292 *p = '\0';
2293 if (opt_domain && !strequal(opt_domain, domain)) {
2294 x_fprintf(x_stderr, "Domain specified in username (%s) "
2295 "doesn't match specified domain (%s)!\n\n",
2296 domain, opt_domain);
2297 poptPrintHelp(pc, stderr, 0);
2298 exit(1);
2300 opt_domain = domain;
2301 } else {
2302 SAFE_FREE(domain);
2306 /* Note: if opt_domain is "" then send no domain */
2307 if (opt_domain == NULL) {
2308 opt_domain = get_winbind_domain();
2311 if (opt_workstation == NULL) {
2312 opt_workstation = "";
2315 if (helper_protocol) {
2316 int i;
2317 for (i=0; i<NUM_HELPER_MODES; i++) {
2318 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2319 squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
2320 exit(0);
2323 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2325 for (i=0; i<NUM_HELPER_MODES; i++) {
2326 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2329 exit(1);
2332 if (!opt_username || !*opt_username) {
2333 x_fprintf(x_stderr, "username must be specified!\n\n");
2334 poptPrintHelp(pc, stderr, 0);
2335 exit(1);
2338 if (opt_challenge.length) {
2339 if (!check_auth_crap()) {
2340 exit(1);
2342 exit(0);
2345 if (!opt_password) {
2346 opt_password = getpass("password: ");
2349 if (diagnostics) {
2350 if (!diagnose_ntlm_auth()) {
2351 return 1;
2353 } else {
2354 fstring user;
2356 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2357 if (!check_plaintext_auth(user, opt_password, True)) {
2358 return 1;
2362 /* Exit code */
2364 poptFreeContext(pc);
2365 return 0;