s4-lsaprc: Don't call lsa_OpenPolicy2 in lsa_LookupNames4.
[Samba/gebeck_regimport.git] / source3 / utils / ntlm_auth.c
bloba832b5bc60f97a7c3e609da4a27dd70d1b03082b
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).
10 Copyright (C) Kai Blin <kai@samba.org> 2008
11 Copyright (C) Simo Sorce 2010
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 3 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "includes.h"
28 #include "lib/param/param.h"
29 #include "popt_common.h"
30 #include "utils/ntlm_auth.h"
31 #include "../libcli/auth/libcli_auth.h"
32 #include "../libcli/auth/spnego.h"
33 #include "auth/ntlmssp/ntlmssp.h"
34 #include "auth/gensec/gensec.h"
35 #include "auth/credentials/credentials.h"
36 #include "librpc/crypto/gse.h"
37 #include "smb_krb5.h"
38 #include <iniparser.h>
39 #include "../lib/crypto/arcfour.h"
40 #include "libads/kerberos_proto.h"
41 #include "nsswitch/winbind_client.h"
42 #include "librpc/gen_ndr/krb5pac.h"
43 #include "../lib/util/asn1.h"
44 #include "auth/common_auth.h"
45 #include "source3/include/auth.h"
46 #include "source3/auth/proto.h"
47 #include "nsswitch/libwbclient/wbclient.h"
49 #if HAVE_KRB5
50 #include "auth/kerberos/pac_utils.h"
51 #endif
53 #ifndef PAM_WINBIND_CONFIG_FILE
54 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
55 #endif
57 #define WINBIND_KRB5_AUTH 0x00000080
59 #undef DBGC_CLASS
60 #define DBGC_CLASS DBGC_WINBIND
62 #define INITIAL_BUFFER_SIZE 300
63 #define MAX_BUFFER_SIZE 630000
65 enum stdio_helper_mode {
66 SQUID_2_4_BASIC,
67 SQUID_2_5_BASIC,
68 SQUID_2_5_NTLMSSP,
69 NTLMSSP_CLIENT_1,
70 GSS_SPNEGO_SERVER,
71 GSS_SPNEGO_CLIENT,
72 NTLM_SERVER_1,
73 NTLM_CHANGE_PASSWORD_1,
74 NUM_HELPER_MODES
77 enum ntlm_auth_cli_state {
78 CLIENT_INITIAL = 0,
79 CLIENT_RESPONSE,
80 CLIENT_FINISHED,
81 CLIENT_ERROR
84 struct ntlm_auth_state {
85 TALLOC_CTX *mem_ctx;
86 enum stdio_helper_mode helper_mode;
87 enum ntlm_auth_cli_state cli_state;
88 struct ntlmssp_state *ntlmssp_state;
89 uint32_t neg_flags;
90 char *want_feature_list;
91 bool have_session_key;
92 DATA_BLOB session_key;
93 DATA_BLOB initial_message;
94 void *gensec_private_1;
96 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
97 struct loadparm_context *lp_ctx,
98 struct ntlm_auth_state *state, char *buf,
99 int length, void **private2);
101 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
102 struct loadparm_context *lp_ctx,
103 struct ntlm_auth_state *state,
104 stdio_helper_function fn, void **private2);
106 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
107 struct loadparm_context *lp_ctx,
108 struct ntlm_auth_state *state,
109 char *buf, int length, void **private2);
111 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
112 struct loadparm_context *lp_ctx,
113 struct ntlm_auth_state *state,
114 char *buf, int length, void **private2);
116 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
117 struct loadparm_context *lp_ctx,
118 struct ntlm_auth_state *state,
119 char *buf, int length, void **private2);
121 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
122 struct loadparm_context *lp_ctx,
123 struct ntlm_auth_state *state,
124 char *buf, int length, void **private2);
126 static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
127 struct loadparm_context *lp_ctx,
128 struct ntlm_auth_state *state,
129 char *buf, int length, void **private2);
131 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
132 struct loadparm_context *lp_ctx,
133 struct ntlm_auth_state *state,
134 char *buf, int length, void **private2);
136 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
137 struct loadparm_context *lp_ctx,
138 struct ntlm_auth_state *state,
139 char *buf, int length, void **private2);
141 static const struct {
142 enum stdio_helper_mode mode;
143 const char *name;
144 stdio_helper_function fn;
145 } stdio_helper_protocols[] = {
146 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
147 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
148 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
149 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
150 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
151 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
152 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
153 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
154 { NUM_HELPER_MODES, NULL, NULL}
157 const char *opt_username;
158 const char *opt_domain;
159 const char *opt_workstation;
160 const char *opt_password;
161 static DATA_BLOB opt_challenge;
162 static DATA_BLOB opt_lm_response;
163 static DATA_BLOB opt_nt_response;
164 static int request_lm_key;
165 static int request_user_session_key;
166 static int use_cached_creds;
168 static const char *require_membership_of;
169 static const char *require_membership_of_sid;
170 static const char *opt_pam_winbind_conf;
172 const char *opt_target_service;
173 const char *opt_target_hostname;
176 /* This is a bit hairy, but the basic idea is to do a password callback
177 to the calling application. The callback comes from within gensec */
179 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
180 struct loadparm_context *lp_ctx,
181 struct ntlm_auth_state *state, char *buf, int length,
182 void **password)
184 DATA_BLOB in;
185 if (strlen(buf) < 2) {
186 DEBUG(1, ("query [%s] invalid", buf));
187 x_fprintf(x_stdout, "BH Query invalid\n");
188 return;
191 if (strlen(buf) > 3) {
192 in = base64_decode_data_blob(buf + 3);
193 } else {
194 in = data_blob(NULL, 0);
197 if (strncmp(buf, "PW ", 3) == 0) {
199 *password = talloc_strndup(NULL,
200 (const char *)in.data, in.length);
202 if (*password == NULL) {
203 DEBUG(1, ("Out of memory\n"));
204 x_fprintf(x_stdout, "BH Out of memory\n");
205 data_blob_free(&in);
206 return;
209 x_fprintf(x_stdout, "OK\n");
210 data_blob_free(&in);
211 return;
213 DEBUG(1, ("Asked for (and expected) a password\n"));
214 x_fprintf(x_stdout, "BH Expected a password\n");
215 data_blob_free(&in);
219 * Callback for password credentials. This is not async, and when
220 * GENSEC and the credentials code is made async, it will look rather
221 * different.
224 static const char *get_password(struct cli_credentials *credentials)
226 char *password = NULL;
228 /* Ask for a password */
229 x_fprintf(x_stdout, "PW\n");
230 credentials->priv_data = NULL;
232 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, NULL, manage_gensec_get_pw_request, (void **)&password);
233 talloc_steal(credentials, password);
234 return password;
238 * A limited set of features are defined with text strings as needed
239 * by ntlm_auth
242 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
244 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
245 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
246 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
248 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
249 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
250 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
252 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
253 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
254 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
258 static char winbind_separator(void)
260 struct winbindd_response response;
261 static bool got_sep;
262 static char sep;
264 if (got_sep)
265 return sep;
267 ZERO_STRUCT(response);
269 /* Send off request */
271 if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
272 NSS_STATUS_SUCCESS) {
273 d_printf("could not obtain winbind separator!\n");
274 return *lp_winbind_separator();
277 sep = response.data.info.winbind_separator;
278 got_sep = True;
280 if (!sep) {
281 d_printf("winbind separator was NULL!\n");
282 return *lp_winbind_separator();
285 return sep;
288 const char *get_winbind_domain(void)
290 struct winbindd_response response;
292 static fstring winbind_domain;
293 if (*winbind_domain) {
294 return winbind_domain;
297 ZERO_STRUCT(response);
299 /* Send off request */
301 if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
302 NSS_STATUS_SUCCESS) {
303 DEBUG(0, ("could not obtain winbind domain name!\n"));
304 return lp_workgroup();
307 fstrcpy(winbind_domain, response.data.domain_name);
309 return winbind_domain;
313 const char *get_winbind_netbios_name(void)
315 struct winbindd_response response;
317 static fstring winbind_netbios_name;
319 if (*winbind_netbios_name) {
320 return winbind_netbios_name;
323 ZERO_STRUCT(response);
325 /* Send off request */
327 if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) !=
328 NSS_STATUS_SUCCESS) {
329 DEBUG(0, ("could not obtain winbind netbios name!\n"));
330 return lp_netbios_name();
333 fstrcpy(winbind_netbios_name, response.data.netbios_name);
335 return winbind_netbios_name;
339 DATA_BLOB get_challenge(void)
341 static DATA_BLOB chal;
342 if (opt_challenge.length)
343 return opt_challenge;
345 chal = data_blob(NULL, 8);
347 generate_random_buffer(chal.data, chal.length);
348 return chal;
351 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
352 form DOMAIN/user into a domain and a user */
354 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
355 fstring user)
358 char *p = strchr(domuser,winbind_separator());
360 if (!p) {
361 return False;
364 fstrcpy(user, p+1);
365 fstrcpy(domain, domuser);
366 domain[PTR_DIFF(p, domuser)] = 0;
367 strupper_m(domain);
369 return True;
372 static bool get_require_membership_sid(void) {
373 struct winbindd_request request;
374 struct winbindd_response response;
376 if (!require_membership_of) {
377 return True;
380 if (require_membership_of_sid) {
381 return True;
384 /* Otherwise, ask winbindd for the name->sid request */
386 ZERO_STRUCT(request);
387 ZERO_STRUCT(response);
389 if (!parse_ntlm_auth_domain_user(require_membership_of,
390 request.data.name.dom_name,
391 request.data.name.name)) {
392 DEBUG(0, ("Could not parse %s into seperate domain/name parts!\n",
393 require_membership_of));
394 return False;
397 if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
398 NSS_STATUS_SUCCESS) {
399 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
400 require_membership_of));
401 return False;
404 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
406 if (require_membership_of_sid)
407 return True;
409 return False;
413 * Get some configuration from pam_winbind.conf to see if we
414 * need to contact trusted domain
417 int get_pam_winbind_config()
419 int ctrl = 0;
420 dictionary *d = NULL;
422 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
423 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
426 d = iniparser_load(discard_const_p(char, opt_pam_winbind_conf));
428 if (!d) {
429 return 0;
432 if (iniparser_getboolean(d, discard_const_p(char, "global:krb5_auth"), false)) {
433 ctrl |= WINBIND_KRB5_AUTH;
436 iniparser_freedict(d);
438 return ctrl;
441 /* Authenticate a user with a plaintext password */
443 static bool check_plaintext_auth(const char *user, const char *pass,
444 bool stdout_diagnostics)
446 struct winbindd_request request;
447 struct winbindd_response response;
448 NSS_STATUS result;
450 if (!get_require_membership_sid()) {
451 return False;
454 /* Send off request */
456 ZERO_STRUCT(request);
457 ZERO_STRUCT(response);
459 fstrcpy(request.data.auth.user, user);
460 fstrcpy(request.data.auth.pass, pass);
461 if (require_membership_of_sid) {
462 strlcpy(request.data.auth.require_membership_of_sid,
463 require_membership_of_sid,
464 sizeof(request.data.auth.require_membership_of_sid));
467 result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
469 /* Display response */
471 if (stdout_diagnostics) {
472 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
473 d_printf("Reading winbind reply failed! (0x01)\n");
476 d_printf("%s: %s (0x%x)\n",
477 response.data.auth.nt_status_string,
478 response.data.auth.error_string,
479 response.data.auth.nt_status);
480 } else {
481 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
482 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
485 DEBUG(3, ("%s: %s (0x%x)\n",
486 response.data.auth.nt_status_string,
487 response.data.auth.error_string,
488 response.data.auth.nt_status));
491 return (result == NSS_STATUS_SUCCESS);
494 /* authenticate a user with an encrypted username/password */
496 NTSTATUS contact_winbind_auth_crap(const char *username,
497 const char *domain,
498 const char *workstation,
499 const DATA_BLOB *challenge,
500 const DATA_BLOB *lm_response,
501 const DATA_BLOB *nt_response,
502 uint32 flags,
503 uint32 extra_logon_parameters,
504 uint8 lm_key[8],
505 uint8 user_session_key[16],
506 char **error_string,
507 char **unix_name)
509 NTSTATUS nt_status;
510 NSS_STATUS result;
511 struct winbindd_request request;
512 struct winbindd_response response;
514 if (!get_require_membership_sid()) {
515 return NT_STATUS_INVALID_PARAMETER;
518 ZERO_STRUCT(request);
519 ZERO_STRUCT(response);
521 request.flags = flags;
523 request.data.auth_crap.logon_parameters = extra_logon_parameters
524 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
526 if (require_membership_of_sid)
527 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
529 fstrcpy(request.data.auth_crap.user, username);
530 fstrcpy(request.data.auth_crap.domain, domain);
532 fstrcpy(request.data.auth_crap.workstation,
533 workstation);
535 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
537 if (lm_response && lm_response->length) {
538 memcpy(request.data.auth_crap.lm_resp,
539 lm_response->data,
540 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
541 request.data.auth_crap.lm_resp_len = lm_response->length;
544 if (nt_response && nt_response->length) {
545 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
546 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
547 request.extra_len = nt_response->length;
548 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
549 if (request.extra_data.data == NULL) {
550 return NT_STATUS_NO_MEMORY;
552 memcpy(request.extra_data.data, nt_response->data,
553 nt_response->length);
555 } else {
556 memcpy(request.data.auth_crap.nt_resp,
557 nt_response->data, nt_response->length);
559 request.data.auth_crap.nt_resp_len = nt_response->length;
562 result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
563 SAFE_FREE(request.extra_data.data);
565 /* Display response */
567 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
568 nt_status = NT_STATUS_UNSUCCESSFUL;
569 if (error_string)
570 *error_string = smb_xstrdup("Reading winbind reply failed!");
571 winbindd_free_response(&response);
572 return nt_status;
575 nt_status = (NT_STATUS(response.data.auth.nt_status));
576 if (!NT_STATUS_IS_OK(nt_status)) {
577 if (error_string)
578 *error_string = smb_xstrdup(response.data.auth.error_string);
579 winbindd_free_response(&response);
580 return nt_status;
583 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
584 memcpy(lm_key, response.data.auth.first_8_lm_hash,
585 sizeof(response.data.auth.first_8_lm_hash));
587 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
588 memcpy(user_session_key, response.data.auth.user_session_key,
589 sizeof(response.data.auth.user_session_key));
592 if (flags & WBFLAG_PAM_UNIX_NAME) {
593 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
594 if (!*unix_name) {
595 winbindd_free_response(&response);
596 return NT_STATUS_NO_MEMORY;
600 winbindd_free_response(&response);
601 return nt_status;
604 /* contact server to change user password using auth crap */
605 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
606 const char *domain,
607 const DATA_BLOB new_nt_pswd,
608 const DATA_BLOB old_nt_hash_enc,
609 const DATA_BLOB new_lm_pswd,
610 const DATA_BLOB old_lm_hash_enc,
611 char **error_string)
613 NTSTATUS nt_status;
614 NSS_STATUS result;
615 struct winbindd_request request;
616 struct winbindd_response response;
618 if (!get_require_membership_sid())
620 if(error_string)
621 *error_string = smb_xstrdup("Can't get membership sid.");
622 return NT_STATUS_INVALID_PARAMETER;
625 ZERO_STRUCT(request);
626 ZERO_STRUCT(response);
628 if(username != NULL)
629 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
630 if(domain != NULL)
631 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
633 if(new_nt_pswd.length)
635 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
636 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
639 if(old_nt_hash_enc.length)
641 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));
642 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
645 if(new_lm_pswd.length)
647 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
648 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
651 if(old_lm_hash_enc.length)
653 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));
654 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
657 result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
659 /* Display response */
661 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
663 nt_status = NT_STATUS_UNSUCCESSFUL;
664 if (error_string)
665 *error_string = smb_xstrdup("Reading winbind reply failed!");
666 winbindd_free_response(&response);
667 return nt_status;
670 nt_status = (NT_STATUS(response.data.auth.nt_status));
671 if (!NT_STATUS_IS_OK(nt_status))
673 if (error_string)
674 *error_string = smb_xstrdup(response.data.auth.error_string);
675 winbindd_free_response(&response);
676 return nt_status;
679 winbindd_free_response(&response);
681 return nt_status;
684 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
685 TALLOC_CTX *mem_ctx,
686 void *server_returned_info,
687 const char *original_user_name,
688 uint32_t session_info_flags,
689 struct auth_session_info **session_info_out)
691 char *unix_username = (char *)server_returned_info;
692 struct auth_session_info *session_info = talloc_zero(mem_ctx, struct auth_session_info);
693 if (!session_info) {
694 return NT_STATUS_NO_MEMORY;
697 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
698 if (!session_info->unix_info) {
699 TALLOC_FREE(session_info);
700 return NT_STATUS_NO_MEMORY;
702 session_info->unix_info->unix_name = talloc_steal(session_info->unix_info, unix_username);
704 *session_info_out = session_info;
706 return NT_STATUS_OK;
709 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
710 TALLOC_CTX *mem_ctx,
711 struct smb_krb5_context *smb_krb5_context,
712 DATA_BLOB *pac_blob,
713 const char *princ_name,
714 const struct tsocket_address *remote_address,
715 uint32_t session_info_flags,
716 struct auth_session_info **session_info)
718 TALLOC_CTX *tmp_ctx;
719 struct PAC_DATA *pac_data = NULL;
720 struct PAC_LOGON_INFO *logon_info = NULL;
721 unsigned int i;
722 char *unixuser;
723 NTSTATUS status;
724 char *domain = NULL;
725 char *realm = NULL;
726 char *user = NULL;
727 char *p;
729 tmp_ctx = talloc_new(mem_ctx);
730 if (!tmp_ctx) {
731 return NT_STATUS_NO_MEMORY;
734 if (pac_blob) {
735 #ifdef HAVE_KRB5
736 status = kerberos_decode_pac(tmp_ctx,
737 *pac_blob,
738 NULL, NULL, NULL, NULL, 0, &pac_data);
739 #else
740 status = NT_STATUS_ACCESS_DENIED;
741 #endif
742 if (!NT_STATUS_IS_OK(status)) {
743 goto done;
746 /* get logon name and logon info */
747 for (i = 0; i < pac_data->num_buffers; i++) {
748 struct PAC_BUFFER *data_buf = &pac_data->buffers[i];
750 switch (data_buf->type) {
751 case PAC_TYPE_LOGON_INFO:
752 if (!data_buf->info) {
753 break;
755 logon_info = data_buf->info->logon_info.info;
756 break;
757 default:
758 break;
761 if (!logon_info) {
762 DEBUG(1, ("Invalid PAC data, missing logon info!\n"));
763 status = NT_STATUS_NOT_FOUND;
764 goto done;
768 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
770 p = strchr_m(princ_name, '@');
771 if (!p) {
772 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
773 princ_name));
774 return NT_STATUS_LOGON_FAILURE;
777 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
778 if (!user) {
779 return NT_STATUS_NO_MEMORY;
782 realm = talloc_strdup(talloc_tos(), p + 1);
783 if (!realm) {
784 return NT_STATUS_NO_MEMORY;
787 if (!strequal(realm, lp_realm())) {
788 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
789 if (!lp_allow_trusted_domains()) {
790 return NT_STATUS_LOGON_FAILURE;
794 if (logon_info && logon_info->info3.base.logon_domain.string) {
795 domain = talloc_strdup(mem_ctx,
796 logon_info->info3.base.logon_domain.string);
797 if (!domain) {
798 return NT_STATUS_NO_MEMORY;
800 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
801 } else {
803 /* If we have winbind running, we can (and must) shorten the
804 username by using the short netbios name. Otherwise we will
805 have inconsistent user names. With Kerberos, we get the
806 fully qualified realm, with ntlmssp we get the short
807 name. And even w2k3 does use ntlmssp if you for example
808 connect to an ip address. */
810 wbcErr wbc_status;
811 struct wbcDomainInfo *info = NULL;
813 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
814 realm));
816 wbc_status = wbcDomainInfo(realm, &info);
818 if (WBC_ERROR_IS_OK(wbc_status)) {
819 domain = talloc_strdup(mem_ctx,
820 info->short_name);
821 wbcFreeMemory(info);
822 } else {
823 DEBUG(3, ("Could not find short name: %s\n",
824 wbcErrorString(wbc_status)));
825 domain = talloc_strdup(mem_ctx, realm);
827 if (!domain) {
828 return NT_STATUS_NO_MEMORY;
830 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
833 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
834 if (!unixuser) {
835 status = NT_STATUS_NO_MEMORY;
836 goto done;
839 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
841 done:
842 TALLOC_FREE(tmp_ctx);
843 return status;
849 * Return the challenge as determined by the authentication subsystem
850 * @return an 8 byte random challenge
853 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
854 uint8_t chal[8])
856 if (auth_ctx->challenge.data.length == 8) {
857 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
858 auth_ctx->challenge.set_by));
859 memcpy(chal, auth_ctx->challenge.data.data, 8);
860 return NT_STATUS_OK;
863 if (!auth_ctx->challenge.set_by) {
864 generate_random_buffer(chal, 8);
866 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
867 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
868 auth_ctx->challenge.set_by = "random";
871 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
872 auth_ctx->challenge.set_by));
874 return NT_STATUS_OK;
878 * NTLM2 authentication modifies the effective challenge,
879 * @param challenge The new challenge value
881 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
883 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
884 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
886 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
887 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
889 return NT_STATUS_OK;
893 * Check the password on an NTLMSSP login.
895 * Return the session keys used on the connection.
898 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
899 TALLOC_CTX *mem_ctx,
900 const struct auth_usersupplied_info *user_info,
901 void **server_returned_info,
902 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
904 static const char zeros[16] = { 0, };
905 NTSTATUS nt_status;
906 char *error_string = NULL;
907 uint8 lm_key[8];
908 uint8 user_sess_key[16];
909 char *unix_name = NULL;
911 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
912 user_info->workstation_name,
913 &auth4_context->challenge.data,
914 &user_info->password.response.lanman,
915 &user_info->password.response.nt,
916 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
918 lm_key, user_sess_key,
919 &error_string, &unix_name);
921 if (NT_STATUS_IS_OK(nt_status)) {
922 if (memcmp(lm_key, zeros, 8) != 0) {
923 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
924 memcpy(lm_session_key->data, lm_key, 8);
925 memset(lm_session_key->data+8, '\0', 8);
928 if (memcmp(user_sess_key, zeros, 16) != 0) {
929 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
931 *server_returned_info = talloc_strdup(mem_ctx,
932 unix_name);
933 } else {
934 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
935 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
936 user_info->client.domain_name, user_info->client.account_name,
937 user_info->workstation_name,
938 error_string ? error_string : "unknown error (NULL)"));
941 SAFE_FREE(error_string);
942 SAFE_FREE(unix_name);
943 return nt_status;
946 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
947 TALLOC_CTX *mem_ctx,
948 const struct auth_usersupplied_info *user_info,
949 void **server_returned_info,
950 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
952 NTSTATUS nt_status;
953 struct samr_Password lm_pw, nt_pw;
955 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
957 nt_status = ntlm_password_check(mem_ctx,
958 true, true, 0,
959 &auth4_context->challenge.data,
960 &user_info->password.response.lanman,
961 &user_info->password.response.nt,
962 user_info->client.account_name,
963 user_info->client.account_name,
964 user_info->client.domain_name,
965 &lm_pw, &nt_pw, session_key, lm_session_key);
967 if (NT_STATUS_IS_OK(nt_status)) {
968 *server_returned_info = talloc_asprintf(mem_ctx,
969 "%s%c%s", user_info->client.domain_name,
970 *lp_winbind_separator(),
971 user_info->client.account_name);
972 } else {
973 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
974 user_info->client.domain_name, user_info->client.account_name,
975 user_info->workstation_name,
976 nt_errstr(nt_status)));
978 return nt_status;
981 static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state)
983 NTSTATUS status;
984 if ( (opt_username == NULL) || (opt_domain == NULL) ) {
985 status = NT_STATUS_UNSUCCESSFUL;
986 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
987 return NT_STATUS_INVALID_PARAMETER;
990 status = ntlmssp_client_start(NULL,
991 lp_netbios_name(),
992 lp_workgroup(),
993 lp_client_ntlmv2_auth(),
994 client_ntlmssp_state);
996 if (!NT_STATUS_IS_OK(status)) {
997 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
998 nt_errstr(status)));
999 TALLOC_FREE(*client_ntlmssp_state);
1000 return status;
1003 status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
1005 if (!NT_STATUS_IS_OK(status)) {
1006 DEBUG(1, ("Could not set username: %s\n",
1007 nt_errstr(status)));
1008 TALLOC_FREE(*client_ntlmssp_state);
1009 return status;
1012 status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
1014 if (!NT_STATUS_IS_OK(status)) {
1015 DEBUG(1, ("Could not set domain: %s\n",
1016 nt_errstr(status)));
1017 TALLOC_FREE(*client_ntlmssp_state);
1018 return status;
1021 if (opt_password) {
1022 status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
1024 if (!NT_STATUS_IS_OK(status)) {
1025 DEBUG(1, ("Could not set password: %s\n",
1026 nt_errstr(status)));
1027 TALLOC_FREE(*client_ntlmssp_state);
1028 return status;
1032 return NT_STATUS_OK;
1035 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1037 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1038 if (auth4_context == NULL) {
1039 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1040 return NULL;
1042 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1043 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1044 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1045 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1046 if (local_pw) {
1047 auth4_context->check_ntlm_password = local_pw_check;
1048 } else {
1049 auth4_context->check_ntlm_password = winbind_pw_check;
1051 auth4_context->private_data = NULL;
1052 return auth4_context;
1055 static NTSTATUS ntlm_auth_start_ntlmssp_server(TALLOC_CTX *mem_ctx,
1056 struct loadparm_context *lp_ctx,
1057 struct gensec_security **gensec_security_out)
1059 struct gensec_security *gensec_security;
1060 NTSTATUS nt_status;
1062 TALLOC_CTX *tmp_ctx;
1064 struct gensec_settings *gensec_settings;
1065 size_t idx = 0;
1066 struct cli_credentials *server_credentials;
1068 struct auth4_context *auth4_context;
1070 tmp_ctx = talloc_new(mem_ctx);
1071 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1073 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1074 if (auth4_context == NULL) {
1075 TALLOC_FREE(tmp_ctx);
1076 return NT_STATUS_NO_MEMORY;
1079 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1080 if (lp_ctx == NULL) {
1081 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1082 TALLOC_FREE(tmp_ctx);
1083 return NT_STATUS_NO_MEMORY;
1087 * This should be a 'netbios domain -> DNS domain'
1088 * mapping, and can currently validly return NULL on
1089 * poorly configured systems.
1091 * This is used for the NTLMSSP server
1094 if (opt_password) {
1095 gensec_settings->server_netbios_name = lp_netbios_name();
1096 gensec_settings->server_netbios_domain = lp_workgroup();
1097 } else {
1098 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1099 gensec_settings->server_netbios_domain = get_winbind_domain();
1102 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1103 get_mydnsdomname(talloc_tos()));
1104 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1105 get_mydnsfullname());
1107 gensec_settings->backends = talloc_zero_array(gensec_settings,
1108 struct gensec_security_ops *, 4);
1110 if (gensec_settings->backends == NULL) {
1111 TALLOC_FREE(tmp_ctx);
1112 return NT_STATUS_NO_MEMORY;
1115 gensec_init();
1117 /* These need to be in priority order, krb5 before NTLMSSP */
1118 #if defined(HAVE_KRB5)
1119 gensec_settings->backends[idx++] = &gensec_gse_krb5_security_ops;
1120 #endif
1122 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1124 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL,
1125 GENSEC_OID_SPNEGO);
1128 * This is anonymous for now, because we just use it
1129 * to set the kerberos state at the moment
1131 server_credentials = cli_credentials_init_anon(tmp_ctx);
1132 if (!server_credentials) {
1133 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1134 return NT_STATUS_NO_MEMORY;
1137 cli_credentials_set_conf(server_credentials, lp_ctx);
1139 if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1140 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1141 } else {
1142 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1145 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1146 auth4_context, &gensec_security);
1148 if (!NT_STATUS_IS_OK(nt_status)) {
1149 TALLOC_FREE(tmp_ctx);
1150 return nt_status;
1153 gensec_set_credentials(gensec_security, server_credentials);
1155 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
1156 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
1158 talloc_unlink(tmp_ctx, lp_ctx);
1159 talloc_unlink(tmp_ctx, server_credentials);
1160 talloc_unlink(tmp_ctx, gensec_settings);
1161 talloc_unlink(tmp_ctx, auth4_context);
1163 nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP);
1164 if (!NT_STATUS_IS_OK(nt_status)) {
1165 TALLOC_FREE(tmp_ctx);
1166 return nt_status;
1169 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1170 TALLOC_FREE(tmp_ctx);
1171 return NT_STATUS_OK;
1174 /*******************************************************************
1175 Used by firefox to drive NTLM auth to IIS servers.
1176 *******************************************************************/
1178 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
1179 DATA_BLOB *reply)
1181 struct winbindd_request wb_request;
1182 struct winbindd_response wb_response;
1183 int ctrl = 0;
1184 NSS_STATUS result;
1186 /* get winbindd to do the ntlmssp step on our behalf */
1187 ZERO_STRUCT(wb_request);
1188 ZERO_STRUCT(wb_response);
1191 * This is tricky here. If we set krb5_auth in pam_winbind.conf
1192 * creds for users in trusted domain will be stored the winbindd
1193 * child of the trusted domain. If we ask the primary domain for
1194 * ntlm_ccache_auth, it will fail. So, we have to ask the trusted
1195 * domain's child for ccache_ntlm_auth. that is to say, we have to
1196 * set WBFLAG_PAM_CONTACT_TRUSTDOM in request.flags.
1198 ctrl = get_pam_winbind_config();
1200 if (ctrl & WINBIND_KRB5_AUTH) {
1201 wb_request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1204 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
1205 "%s%c%s", opt_domain, winbind_separator(), opt_username);
1206 wb_request.data.ccache_ntlm_auth.uid = geteuid();
1207 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
1208 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
1209 wb_request.extra_len = initial_msg.length + challenge_msg.length;
1211 if (wb_request.extra_len > 0) {
1212 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
1213 if (wb_request.extra_data.data == NULL) {
1214 return NT_STATUS_NO_MEMORY;
1217 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
1218 memcpy(wb_request.extra_data.data + initial_msg.length,
1219 challenge_msg.data, challenge_msg.length);
1222 result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
1223 SAFE_FREE(wb_request.extra_data.data);
1225 if (result != NSS_STATUS_SUCCESS) {
1226 winbindd_free_response(&wb_response);
1227 return NT_STATUS_UNSUCCESSFUL;
1230 if (reply) {
1231 *reply = data_blob(wb_response.extra_data.data,
1232 wb_response.data.ccache_ntlm_auth.auth_blob_len);
1233 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
1234 reply->data == NULL) {
1235 winbindd_free_response(&wb_response);
1236 return NT_STATUS_NO_MEMORY;
1240 winbindd_free_response(&wb_response);
1241 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1244 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1245 struct loadparm_context *lp_ctx,
1246 struct ntlm_auth_state *state,
1247 char *buf, int length, void **private2)
1249 DATA_BLOB request, reply;
1250 NTSTATUS nt_status;
1252 if (!opt_username || !*opt_username) {
1253 x_fprintf(x_stderr, "username must be specified!\n\n");
1254 exit(1);
1257 if (strlen(buf) < 2) {
1258 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1259 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1260 return;
1263 if (strlen(buf) > 3) {
1264 if(strncmp(buf, "SF ", 3) == 0) {
1265 DEBUG(10, ("Looking for flags to negotiate\n"));
1266 talloc_free(state->want_feature_list);
1267 state->want_feature_list = talloc_strdup(state->mem_ctx,
1268 buf+3);
1269 x_fprintf(x_stdout, "OK\n");
1270 return;
1272 request = base64_decode_data_blob(buf + 3);
1273 } else {
1274 request = data_blob_null;
1277 if (strncmp(buf, "PW ", 3) == 0) {
1278 /* We asked for a password and obviously got it :-) */
1280 opt_password = SMB_STRNDUP((const char *)request.data,
1281 request.length);
1283 if (opt_password == NULL) {
1284 DEBUG(1, ("Out of memory\n"));
1285 x_fprintf(x_stdout, "BH Out of memory\n");
1286 data_blob_free(&request);
1287 return;
1290 x_fprintf(x_stdout, "OK\n");
1291 data_blob_free(&request);
1292 return;
1295 if (!state->ntlmssp_state && use_cached_creds) {
1296 /* check whether cached credentials are usable. */
1297 DATA_BLOB empty_blob = data_blob_null;
1299 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
1300 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1301 /* failed to use cached creds */
1302 use_cached_creds = False;
1306 if (opt_password == NULL && !use_cached_creds) {
1307 /* Request a password from the calling process. After
1308 sending it, the calling process should retry asking for the
1309 negotiate. */
1311 DEBUG(10, ("Requesting password\n"));
1312 x_fprintf(x_stdout, "PW\n");
1313 return;
1316 if (strncmp(buf, "YR", 2) == 0) {
1317 TALLOC_FREE(state->ntlmssp_state);
1318 state->cli_state = CLIENT_INITIAL;
1319 } else if (strncmp(buf, "TT", 2) == 0) {
1320 /* No special preprocessing required */
1321 } else if (strncmp(buf, "GF", 2) == 0) {
1322 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
1324 if(state->cli_state == CLIENT_FINISHED) {
1325 x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
1327 else {
1328 x_fprintf(x_stdout, "BH\n");
1331 data_blob_free(&request);
1332 return;
1333 } else if (strncmp(buf, "GK", 2) == 0 ) {
1334 DEBUG(10, ("Requested session key\n"));
1336 if(state->cli_state == CLIENT_FINISHED) {
1337 char *key64 = base64_encode_data_blob(state->mem_ctx,
1338 state->session_key);
1339 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
1340 TALLOC_FREE(key64);
1342 else {
1343 x_fprintf(x_stdout, "BH\n");
1346 data_blob_free(&request);
1347 return;
1348 } else {
1349 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1350 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1351 return;
1354 if (!state->ntlmssp_state) {
1355 nt_status = ntlm_auth_start_ntlmssp_client(
1356 &state->ntlmssp_state);
1357 if (!NT_STATUS_IS_OK(nt_status)) {
1358 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1359 return;
1361 ntlmssp_want_feature_list(state->ntlmssp_state,
1362 state->want_feature_list);
1363 state->initial_message = data_blob_null;
1366 DEBUG(10, ("got NTLMSSP packet:\n"));
1367 dump_data(10, request.data, request.length);
1369 if (use_cached_creds && !opt_password &&
1370 (state->cli_state == CLIENT_RESPONSE)) {
1371 nt_status = do_ccache_ntlm_auth(state->initial_message, request,
1372 &reply);
1373 } else {
1374 nt_status = ntlmssp_update(state->ntlmssp_state, request,
1375 &reply);
1378 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1379 char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
1380 reply);
1381 if (state->cli_state == CLIENT_INITIAL) {
1382 x_fprintf(x_stdout, "YR %s\n", reply_base64);
1383 state->initial_message = reply;
1384 state->cli_state = CLIENT_RESPONSE;
1385 } else {
1386 x_fprintf(x_stdout, "KK %s\n", reply_base64);
1387 data_blob_free(&reply);
1389 TALLOC_FREE(reply_base64);
1390 DEBUG(10, ("NTLMSSP challenge\n"));
1391 } else if (NT_STATUS_IS_OK(nt_status)) {
1392 char *reply_base64 = base64_encode_data_blob(talloc_tos(),
1393 reply);
1394 x_fprintf(x_stdout, "AF %s\n", reply_base64);
1395 TALLOC_FREE(reply_base64);
1397 if(state->have_session_key)
1398 data_blob_free(&state->session_key);
1400 state->session_key = data_blob(
1401 state->ntlmssp_state->session_key.data,
1402 state->ntlmssp_state->session_key.length);
1403 state->neg_flags = state->ntlmssp_state->neg_flags;
1404 state->have_session_key = true;
1406 DEBUG(10, ("NTLMSSP OK!\n"));
1407 state->cli_state = CLIENT_FINISHED;
1408 TALLOC_FREE(state->ntlmssp_state);
1409 } else {
1410 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1411 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
1412 state->cli_state = CLIENT_ERROR;
1413 TALLOC_FREE(state->ntlmssp_state);
1416 data_blob_free(&request);
1419 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1420 struct loadparm_context *lp_ctx,
1421 struct ntlm_auth_state *state,
1422 char *buf, int length, void **private2)
1424 char *user, *pass;
1425 user=buf;
1427 pass=(char *)memchr(buf,' ',length);
1428 if (!pass) {
1429 DEBUG(2, ("Password not found. Denying access\n"));
1430 x_fprintf(x_stdout, "ERR\n");
1431 return;
1433 *pass='\0';
1434 pass++;
1436 if (state->helper_mode == SQUID_2_5_BASIC) {
1437 rfc1738_unescape(user);
1438 rfc1738_unescape(pass);
1441 if (check_plaintext_auth(user, pass, False)) {
1442 x_fprintf(x_stdout, "OK\n");
1443 } else {
1444 x_fprintf(x_stdout, "ERR\n");
1448 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1449 struct loadparm_context *lp_ctx,
1450 char *buf, int length, void **private1)
1452 DATA_BLOB in;
1453 DATA_BLOB out = data_blob(NULL, 0);
1454 char *out_base64 = NULL;
1455 const char *reply_arg = NULL;
1456 struct gensec_ntlm_state {
1457 struct gensec_security *gensec_state;
1458 const char *set_password;
1460 struct gensec_ntlm_state *state;
1462 NTSTATUS nt_status;
1463 bool first = false;
1464 const char *reply_code;
1465 struct cli_credentials *creds;
1467 static char *want_feature_list = NULL;
1468 static DATA_BLOB session_key;
1470 TALLOC_CTX *mem_ctx;
1472 if (*private1) {
1473 state = (struct gensec_ntlm_state *)*private1;
1474 } else {
1475 state = talloc_zero(NULL, struct gensec_ntlm_state);
1476 if (!state) {
1477 x_fprintf(x_stdout, "BH No Memory\n");
1478 exit(1);
1480 *private1 = state;
1481 if (opt_password) {
1482 state->set_password = opt_password;
1486 if (strlen(buf) < 2) {
1487 DEBUG(1, ("query [%s] invalid", buf));
1488 x_fprintf(x_stdout, "BH Query invalid\n");
1489 return;
1492 if (strlen(buf) > 3) {
1493 if(strncmp(buf, "SF ", 3) == 0) {
1494 DEBUG(10, ("Setting flags to negotiate\n"));
1495 talloc_free(want_feature_list);
1496 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1497 x_fprintf(x_stdout, "OK\n");
1498 return;
1500 in = base64_decode_data_blob(buf + 3);
1501 } else {
1502 in = data_blob(NULL, 0);
1505 if (strncmp(buf, "YR", 2) == 0) {
1506 if (state->gensec_state) {
1507 talloc_free(state->gensec_state);
1508 state->gensec_state = NULL;
1510 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1511 /* Just return BH, like ntlm_auth from Samba 3 does. */
1512 x_fprintf(x_stdout, "BH Command expected\n");
1513 data_blob_free(&in);
1514 return;
1515 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1516 (strncmp(buf, "KK ", 3) != 0) &&
1517 (strncmp(buf, "AF ", 3) != 0) &&
1518 (strncmp(buf, "NA ", 3) != 0) &&
1519 (strncmp(buf, "UG", 2) != 0) &&
1520 (strncmp(buf, "PW ", 3) != 0) &&
1521 (strncmp(buf, "GK", 2) != 0) &&
1522 (strncmp(buf, "GF", 2) != 0)) {
1523 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1524 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1525 data_blob_free(&in);
1526 return;
1529 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1531 /* setup gensec */
1532 if (!(state->gensec_state)) {
1533 switch (stdio_helper_mode) {
1534 case GSS_SPNEGO_CLIENT:
1535 case NTLMSSP_CLIENT_1:
1536 /* setup the client side */
1538 nt_status = gensec_client_start(NULL, &state->gensec_state,
1539 lpcfg_gensec_settings(NULL, lp_ctx));
1540 if (!NT_STATUS_IS_OK(nt_status)) {
1541 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1542 talloc_free(mem_ctx);
1543 return;
1546 creds = cli_credentials_init(state->gensec_state);
1547 cli_credentials_set_conf(creds, lp_ctx);
1548 if (opt_username) {
1549 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1551 if (opt_domain) {
1552 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1554 if (state->set_password) {
1555 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1556 } else {
1557 cli_credentials_set_password_callback(creds, get_password);
1559 if (opt_workstation) {
1560 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1563 gensec_set_credentials(state->gensec_state, creds);
1565 break;
1566 case GSS_SPNEGO_SERVER:
1567 case SQUID_2_5_NTLMSSP:
1569 nt_status = ntlm_auth_start_ntlmssp_server(state, lp_ctx,
1570 &state->gensec_state);
1571 if (!NT_STATUS_IS_OK(nt_status)) {
1572 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1573 talloc_free(mem_ctx);
1574 return;
1576 break;
1578 default:
1579 talloc_free(mem_ctx);
1580 abort();
1583 gensec_want_feature_list(state->gensec_state, want_feature_list);
1585 switch (stdio_helper_mode) {
1586 case GSS_SPNEGO_CLIENT:
1587 case GSS_SPNEGO_SERVER:
1588 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1589 if (!in.length) {
1590 first = true;
1592 break;
1593 case NTLMSSP_CLIENT_1:
1594 if (!in.length) {
1595 first = true;
1597 /* fall through */
1598 case SQUID_2_5_NTLMSSP:
1599 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1600 break;
1601 default:
1602 talloc_free(mem_ctx);
1603 abort();
1606 if (!NT_STATUS_IS_OK(nt_status)) {
1607 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1608 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1609 talloc_free(mem_ctx);
1610 return;
1615 /* update */
1617 if (strncmp(buf, "PW ", 3) == 0) {
1618 state->set_password = talloc_strndup(state,
1619 (const char *)in.data,
1620 in.length);
1622 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1623 state->set_password,
1624 CRED_SPECIFIED);
1625 x_fprintf(x_stdout, "OK\n");
1626 data_blob_free(&in);
1627 talloc_free(mem_ctx);
1628 return;
1631 if (strncmp(buf, "GK", 2) == 0) {
1632 char *base64_key;
1633 DEBUG(10, ("Requested session key\n"));
1634 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1635 if(!NT_STATUS_IS_OK(nt_status)) {
1636 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1637 x_fprintf(x_stdout, "BH No session key\n");
1638 talloc_free(mem_ctx);
1639 return;
1640 } else {
1641 base64_key = base64_encode_data_blob(state, session_key);
1642 x_fprintf(x_stdout, "GK %s\n", base64_key);
1643 talloc_free(base64_key);
1645 talloc_free(mem_ctx);
1646 return;
1649 if (stdio_helper_mode == SQUID_2_5_NTLMSSP && strncmp(buf, "GF", 2) == 0) {
1650 uint32_t neg_flags;
1652 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1654 DEBUG(10, ("Requested negotiated feature flags\n"));
1655 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1656 return;
1659 nt_status = gensec_update(state->gensec_state, mem_ctx, NULL, in, &out);
1661 /* don't leak 'bad password'/'no such user' info to the network client */
1662 nt_status = nt_status_squash(nt_status);
1664 if (out.length) {
1665 out_base64 = base64_encode_data_blob(mem_ctx, out);
1666 } else {
1667 out_base64 = NULL;
1670 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1671 reply_arg = "*";
1672 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1673 reply_code = "YR";
1674 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1675 reply_code = "KK";
1676 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1677 reply_code = "TT";
1678 } else {
1679 abort();
1683 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1684 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1685 reply_arg = nt_errstr(nt_status);
1686 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1687 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1688 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1689 reply_arg = nt_errstr(nt_status);
1690 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1691 } else if (!NT_STATUS_IS_OK(nt_status)) {
1692 reply_code = "NA";
1693 reply_arg = nt_errstr(nt_status);
1694 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1695 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1696 struct auth_session_info *session_info;
1698 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1699 if (!NT_STATUS_IS_OK(nt_status)) {
1700 reply_code = "BH Failed to retrive session info";
1701 reply_arg = nt_errstr(nt_status);
1702 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1703 } else {
1705 reply_code = "AF";
1706 reply_arg = session_info->unix_info->unix_name;
1707 talloc_free(session_info);
1709 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1710 reply_code = "AF";
1711 reply_arg = out_base64;
1712 } else {
1713 abort();
1716 switch (stdio_helper_mode) {
1717 case GSS_SPNEGO_SERVER:
1718 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1719 out_base64 ? out_base64 : "*",
1720 reply_arg ? reply_arg : "*");
1721 break;
1722 default:
1723 if (out_base64) {
1724 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1725 } else if (reply_arg) {
1726 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1727 } else {
1728 x_fprintf(x_stdout, "%s\n", reply_code);
1732 talloc_free(mem_ctx);
1733 return;
1736 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1737 struct loadparm_context *lp_ctx,
1738 struct ntlm_auth_state *state,
1739 char *buf, int length, void **private2)
1741 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1742 return;
1745 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1746 struct loadparm_context *lp_ctx,
1747 struct ntlm_auth_state *state,
1748 char *buf, int length, void **private2)
1750 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1751 return;
1754 static struct ntlmssp_state *client_ntlmssp_state = NULL;
1756 static bool manage_client_ntlmssp_init(struct spnego_data spnego)
1758 NTSTATUS status;
1759 DATA_BLOB null_blob = data_blob_null;
1760 DATA_BLOB to_server;
1761 char *to_server_base64;
1762 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1763 TALLOC_CTX *ctx = talloc_tos();
1765 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1767 if (client_ntlmssp_state != NULL) {
1768 DEBUG(1, ("Request for initial SPNEGO request where "
1769 "we already have a state\n"));
1770 return False;
1773 if (!client_ntlmssp_state) {
1774 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1775 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1776 return False;
1781 if (opt_password == NULL) {
1783 /* Request a password from the calling process. After
1784 sending it, the calling process should retry with
1785 the negTokenInit. */
1787 DEBUG(10, ("Requesting password\n"));
1788 x_fprintf(x_stdout, "PW\n");
1789 return True;
1792 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1793 spnego.negTokenInit.mechTypes = my_mechs;
1794 spnego.negTokenInit.reqFlags = data_blob_null;
1795 spnego.negTokenInit.reqFlagsPadding = 0;
1796 spnego.negTokenInit.mechListMIC = null_blob;
1798 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1799 &spnego.negTokenInit.mechToken);
1801 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1802 NT_STATUS_IS_OK(status)) ) {
1803 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1804 nt_errstr(status)));
1805 TALLOC_FREE(client_ntlmssp_state);
1806 return False;
1809 spnego_write_data(ctx, &to_server, &spnego);
1810 data_blob_free(&spnego.negTokenInit.mechToken);
1812 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1813 data_blob_free(&to_server);
1814 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1815 TALLOC_FREE(to_server_base64);
1816 return True;
1819 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
1821 NTSTATUS status;
1822 DATA_BLOB null_blob = data_blob_null;
1823 DATA_BLOB request;
1824 DATA_BLOB to_server;
1825 char *to_server_base64;
1826 TALLOC_CTX *ctx = talloc_tos();
1828 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1830 if (client_ntlmssp_state == NULL) {
1831 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1832 x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
1833 return;
1836 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1837 x_fprintf(x_stdout, "NA\n");
1838 TALLOC_FREE(client_ntlmssp_state);
1839 return;
1842 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1843 x_fprintf(x_stdout, "AF\n");
1844 TALLOC_FREE(client_ntlmssp_state);
1845 return;
1848 status = ntlmssp_update(client_ntlmssp_state,
1849 spnego.negTokenTarg.responseToken,
1850 &request);
1852 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
1853 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED or OK from "
1854 "ntlmssp_client_update, got: %s\n",
1855 nt_errstr(status)));
1856 x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
1857 "ntlmssp_client_update\n");
1858 data_blob_free(&request);
1859 TALLOC_FREE(client_ntlmssp_state);
1860 return;
1863 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1864 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1865 spnego.negTokenTarg.supportedMech = (const char *)OID_NTLMSSP;
1866 spnego.negTokenTarg.responseToken = request;
1867 spnego.negTokenTarg.mechListMIC = null_blob;
1869 spnego_write_data(ctx, &to_server, &spnego);
1870 data_blob_free(&request);
1872 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1873 data_blob_free(&to_server);
1874 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1875 TALLOC_FREE(to_server_base64);
1876 return;
1879 #ifdef HAVE_KRB5
1881 static bool manage_client_krb5_init(struct spnego_data spnego)
1883 char *principal;
1884 DATA_BLOB tkt, tkt_wrapped, to_server;
1885 DATA_BLOB session_key_krb5 = data_blob_null;
1886 struct spnego_data reply;
1887 char *reply_base64;
1888 int retval;
1890 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1891 ssize_t len;
1892 TALLOC_CTX *ctx = talloc_tos();
1894 principal = spnego.negTokenInit.targetPrincipal;
1896 /* We may not be allowed to use the server-supplied SPNEGO principal, or it may not have been supplied to us
1898 if (!lp_client_use_spnego_principal() || strequal(principal, ADS_IGNORE_PRINCIPAL)) {
1899 principal = NULL;
1902 if (principal == NULL &&
1903 opt_target_service && opt_target_hostname && !is_ipaddress(opt_target_hostname)) {
1904 DEBUG(3,("manage_client_krb5_init: using target "
1905 "hostname not SPNEGO principal\n"));
1907 principal = kerberos_get_principal_from_service_hostname(talloc_tos(),
1908 opt_target_service,
1909 opt_target_hostname,
1910 lp_realm());
1912 if (!principal) {
1913 return false;
1916 DEBUG(3,("manage_client_krb5_init: guessed "
1917 "server principal=%s\n",
1918 principal ? principal : "<null>"));
1921 if (principal == NULL) {
1922 DEBUG(3,("manage_client_krb5_init: could not guess server principal\n"));
1923 return false;
1926 retval = cli_krb5_get_ticket(ctx, principal, 0,
1927 &tkt, &session_key_krb5,
1928 0, NULL, NULL, NULL);
1929 if (retval) {
1930 char *user = NULL;
1932 /* Let's try to first get the TGT, for that we need a
1933 password. */
1935 if (opt_password == NULL) {
1936 DEBUG(10, ("Requesting password\n"));
1937 x_fprintf(x_stdout, "PW\n");
1938 return True;
1941 user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
1942 if (!user) {
1943 return false;
1946 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1947 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1948 return False;
1951 retval = cli_krb5_get_ticket(ctx, principal, 0,
1952 &tkt, &session_key_krb5,
1953 0, NULL, NULL, NULL);
1954 if (retval) {
1955 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1956 return False;
1961 /* wrap that up in a nice GSS-API wrapping */
1962 tkt_wrapped = spnego_gen_krb5_wrap(ctx, tkt, TOK_ID_KRB_AP_REQ);
1964 data_blob_free(&session_key_krb5);
1966 ZERO_STRUCT(reply);
1968 reply.type = SPNEGO_NEG_TOKEN_INIT;
1969 reply.negTokenInit.mechTypes = my_mechs;
1970 reply.negTokenInit.reqFlags = data_blob_null;
1971 reply.negTokenInit.reqFlagsPadding = 0;
1972 reply.negTokenInit.mechToken = tkt_wrapped;
1973 reply.negTokenInit.mechListMIC = data_blob_null;
1975 len = spnego_write_data(ctx, &to_server, &reply);
1976 data_blob_free(&tkt);
1978 if (len == -1) {
1979 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1980 return False;
1983 reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1984 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1986 TALLOC_FREE(reply_base64);
1987 data_blob_free(&to_server);
1988 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1989 return True;
1992 static void manage_client_krb5_targ(struct spnego_data spnego)
1994 switch (spnego.negTokenTarg.negResult) {
1995 case SPNEGO_ACCEPT_INCOMPLETE:
1996 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1997 x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
1998 "ACCEPT_INCOMPLETE\n");
1999 break;
2000 case SPNEGO_ACCEPT_COMPLETED:
2001 DEBUG(10, ("Accept completed\n"));
2002 x_fprintf(x_stdout, "AF\n");
2003 break;
2004 case SPNEGO_REJECT:
2005 DEBUG(10, ("Rejected\n"));
2006 x_fprintf(x_stdout, "NA\n");
2007 break;
2008 default:
2009 DEBUG(1, ("Got an invalid negTokenTarg\n"));
2010 x_fprintf(x_stdout, "AF\n");
2014 #endif
2016 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
2017 struct loadparm_context *lp_ctx,
2018 struct ntlm_auth_state *state,
2019 char *buf, int length, void **private2)
2021 DATA_BLOB request;
2022 struct spnego_data spnego;
2023 ssize_t len;
2024 TALLOC_CTX *ctx = talloc_tos();
2026 if (!opt_username || !*opt_username) {
2027 x_fprintf(x_stderr, "username must be specified!\n\n");
2028 exit(1);
2031 if (strlen(buf) <= 3) {
2032 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
2033 x_fprintf(x_stdout, "BH SPNEGO query too short\n");
2034 return;
2037 request = base64_decode_data_blob(buf+3);
2039 if (strncmp(buf, "PW ", 3) == 0) {
2041 /* We asked for a password and obviously got it :-) */
2043 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
2045 if (opt_password == NULL) {
2046 DEBUG(1, ("Out of memory\n"));
2047 x_fprintf(x_stdout, "BH Out of memory\n");
2048 data_blob_free(&request);
2049 return;
2052 x_fprintf(x_stdout, "OK\n");
2053 data_blob_free(&request);
2054 return;
2057 if ( (strncmp(buf, "TT ", 3) != 0) &&
2058 (strncmp(buf, "AF ", 3) != 0) &&
2059 (strncmp(buf, "NA ", 3) != 0) ) {
2060 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
2061 x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
2062 data_blob_free(&request);
2063 return;
2066 /* So we got a server challenge to generate a SPNEGO
2067 client-to-server request... */
2069 len = spnego_read_data(ctx, request, &spnego);
2070 data_blob_free(&request);
2072 if (len == -1) {
2073 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
2074 x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
2075 return;
2078 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
2080 /* The server offers a list of mechanisms */
2082 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
2084 while (*mechType != NULL) {
2086 #ifdef HAVE_KRB5
2087 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
2088 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
2089 if (manage_client_krb5_init(spnego))
2090 goto out;
2092 #endif
2094 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
2095 if (manage_client_ntlmssp_init(spnego))
2096 goto out;
2099 mechType++;
2102 DEBUG(1, ("Server offered no compatible mechanism\n"));
2103 x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
2104 return;
2107 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
2109 if (spnego.negTokenTarg.supportedMech == NULL) {
2110 /* On accept/reject Windows does not send the
2111 mechanism anymore. Handle that here and
2112 shut down the mechanisms. */
2114 switch (spnego.negTokenTarg.negResult) {
2115 case SPNEGO_ACCEPT_COMPLETED:
2116 x_fprintf(x_stdout, "AF\n");
2117 break;
2118 case SPNEGO_REJECT:
2119 x_fprintf(x_stdout, "NA\n");
2120 break;
2121 default:
2122 DEBUG(1, ("Got a negTokenTarg with no mech and an "
2123 "unknown negResult: %d\n",
2124 spnego.negTokenTarg.negResult));
2125 x_fprintf(x_stdout, "BH Got a negTokenTarg with"
2126 " no mech and an unknown "
2127 "negResult\n");
2130 TALLOC_FREE(client_ntlmssp_state);
2131 goto out;
2134 if (strcmp(spnego.negTokenTarg.supportedMech,
2135 OID_NTLMSSP) == 0) {
2136 manage_client_ntlmssp_targ(spnego);
2137 goto out;
2140 #if HAVE_KRB5
2141 if (strcmp(spnego.negTokenTarg.supportedMech,
2142 OID_KERBEROS5_OLD) == 0) {
2143 manage_client_krb5_targ(spnego);
2144 goto out;
2146 #endif
2150 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
2151 x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
2152 return;
2154 out:
2155 spnego_free_data(&spnego);
2156 return;
2159 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
2160 struct loadparm_context *lp_ctx,
2161 struct ntlm_auth_state *state,
2162 char *buf, int length, void **private2)
2164 char *request, *parameter;
2165 static DATA_BLOB challenge;
2166 static DATA_BLOB lm_response;
2167 static DATA_BLOB nt_response;
2168 static char *full_username;
2169 static char *username;
2170 static char *domain;
2171 static char *plaintext_password;
2172 static bool ntlm_server_1_user_session_key;
2173 static bool ntlm_server_1_lm_session_key;
2175 if (strequal(buf, ".")) {
2176 if (!full_username && !username) {
2177 x_fprintf(x_stdout, "Error: No username supplied!\n");
2178 } else if (plaintext_password) {
2179 /* handle this request as plaintext */
2180 if (!full_username) {
2181 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
2182 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
2183 return;
2186 if (check_plaintext_auth(full_username, plaintext_password, False)) {
2187 x_fprintf(x_stdout, "Authenticated: Yes\n");
2188 } else {
2189 x_fprintf(x_stdout, "Authenticated: No\n");
2191 } else if (!lm_response.data && !nt_response.data) {
2192 x_fprintf(x_stdout, "Error: No password supplied!\n");
2193 } else if (!challenge.data) {
2194 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
2195 } else {
2196 char *error_string = NULL;
2197 uchar lm_key[8];
2198 uchar user_session_key[16];
2199 uint32 flags = 0;
2201 if (full_username && !username) {
2202 fstring fstr_user;
2203 fstring fstr_domain;
2205 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
2206 /* username might be 'tainted', don't print into our new-line deleimianted stream */
2207 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
2209 SAFE_FREE(username);
2210 SAFE_FREE(domain);
2211 username = smb_xstrdup(fstr_user);
2212 domain = smb_xstrdup(fstr_domain);
2215 if (!domain) {
2216 domain = smb_xstrdup(get_winbind_domain());
2219 if (ntlm_server_1_lm_session_key)
2220 flags |= WBFLAG_PAM_LMKEY;
2222 if (ntlm_server_1_user_session_key)
2223 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2225 if (!NT_STATUS_IS_OK(
2226 contact_winbind_auth_crap(username,
2227 domain,
2228 lp_netbios_name(),
2229 &challenge,
2230 &lm_response,
2231 &nt_response,
2232 flags, 0,
2233 lm_key,
2234 user_session_key,
2235 &error_string,
2236 NULL))) {
2238 x_fprintf(x_stdout, "Authenticated: No\n");
2239 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
2240 } else {
2241 static char zeros[16];
2242 char *hex_lm_key;
2243 char *hex_user_session_key;
2245 x_fprintf(x_stdout, "Authenticated: Yes\n");
2247 if (ntlm_server_1_lm_session_key
2248 && (memcmp(zeros, lm_key,
2249 sizeof(lm_key)) != 0)) {
2250 hex_lm_key = hex_encode_talloc(NULL,
2251 (const unsigned char *)lm_key,
2252 sizeof(lm_key));
2253 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
2254 TALLOC_FREE(hex_lm_key);
2257 if (ntlm_server_1_user_session_key
2258 && (memcmp(zeros, user_session_key,
2259 sizeof(user_session_key)) != 0)) {
2260 hex_user_session_key = hex_encode_talloc(NULL,
2261 (const unsigned char *)user_session_key,
2262 sizeof(user_session_key));
2263 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
2264 TALLOC_FREE(hex_user_session_key);
2267 SAFE_FREE(error_string);
2269 /* clear out the state */
2270 challenge = data_blob_null;
2271 nt_response = data_blob_null;
2272 lm_response = data_blob_null;
2273 SAFE_FREE(full_username);
2274 SAFE_FREE(username);
2275 SAFE_FREE(domain);
2276 SAFE_FREE(plaintext_password);
2277 ntlm_server_1_user_session_key = False;
2278 ntlm_server_1_lm_session_key = False;
2279 x_fprintf(x_stdout, ".\n");
2281 return;
2284 request = buf;
2286 /* Indicates a base64 encoded structure */
2287 parameter = strstr_m(request, ":: ");
2288 if (!parameter) {
2289 parameter = strstr_m(request, ": ");
2291 if (!parameter) {
2292 DEBUG(0, ("Parameter not found!\n"));
2293 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2294 return;
2297 parameter[0] ='\0';
2298 parameter++;
2299 parameter[0] ='\0';
2300 parameter++;
2302 } else {
2303 parameter[0] ='\0';
2304 parameter++;
2305 parameter[0] ='\0';
2306 parameter++;
2307 parameter[0] ='\0';
2308 parameter++;
2310 base64_decode_inplace(parameter);
2313 if (strequal(request, "LANMAN-Challenge")) {
2314 challenge = strhex_to_data_blob(NULL, parameter);
2315 if (challenge.length != 8) {
2316 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
2317 parameter,
2318 (int)challenge.length);
2319 challenge = data_blob_null;
2321 } else if (strequal(request, "NT-Response")) {
2322 nt_response = strhex_to_data_blob(NULL, parameter);
2323 if (nt_response.length < 24) {
2324 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
2325 parameter,
2326 (int)nt_response.length);
2327 nt_response = data_blob_null;
2329 } else if (strequal(request, "LANMAN-Response")) {
2330 lm_response = strhex_to_data_blob(NULL, parameter);
2331 if (lm_response.length != 24) {
2332 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
2333 parameter,
2334 (int)lm_response.length);
2335 lm_response = data_blob_null;
2337 } else if (strequal(request, "Password")) {
2338 plaintext_password = smb_xstrdup(parameter);
2339 } else if (strequal(request, "NT-Domain")) {
2340 domain = smb_xstrdup(parameter);
2341 } else if (strequal(request, "Username")) {
2342 username = smb_xstrdup(parameter);
2343 } else if (strequal(request, "Full-Username")) {
2344 full_username = smb_xstrdup(parameter);
2345 } else if (strequal(request, "Request-User-Session-Key")) {
2346 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2347 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2348 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2349 } else {
2350 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2354 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2355 struct loadparm_context *lp_ctx,
2356 struct ntlm_auth_state *state,
2357 char *buf, int length, void **private2)
2359 char *request, *parameter;
2360 static DATA_BLOB new_nt_pswd;
2361 static DATA_BLOB old_nt_hash_enc;
2362 static DATA_BLOB new_lm_pswd;
2363 static DATA_BLOB old_lm_hash_enc;
2364 static char *full_username = NULL;
2365 static char *username = NULL;
2366 static char *domain = NULL;
2367 static char *newpswd = NULL;
2368 static char *oldpswd = NULL;
2370 if (strequal(buf, ".")) {
2371 if(newpswd && oldpswd) {
2372 uchar old_nt_hash[16];
2373 uchar old_lm_hash[16];
2374 uchar new_nt_hash[16];
2375 uchar new_lm_hash[16];
2377 new_nt_pswd = data_blob(NULL, 516);
2378 old_nt_hash_enc = data_blob(NULL, 16);
2380 /* Calculate the MD4 hash (NT compatible) of the
2381 * password */
2382 E_md4hash(oldpswd, old_nt_hash);
2383 E_md4hash(newpswd, new_nt_hash);
2385 /* E_deshash returns false for 'long'
2386 passwords (> 14 DOS chars).
2388 Therefore, don't send a buffer
2389 encrypted with the truncated hash
2390 (it could allow an even easier
2391 attack on the password)
2393 Likewise, obey the admin's restriction
2396 if (lp_client_lanman_auth() &&
2397 E_deshash(newpswd, new_lm_hash) &&
2398 E_deshash(oldpswd, old_lm_hash)) {
2399 new_lm_pswd = data_blob(NULL, 516);
2400 old_lm_hash_enc = data_blob(NULL, 16);
2401 encode_pw_buffer(new_lm_pswd.data, newpswd,
2402 STR_UNICODE);
2404 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
2405 E_old_pw_hash(new_nt_hash, old_lm_hash,
2406 old_lm_hash_enc.data);
2407 } else {
2408 new_lm_pswd.data = NULL;
2409 new_lm_pswd.length = 0;
2410 old_lm_hash_enc.data = NULL;
2411 old_lm_hash_enc.length = 0;
2414 encode_pw_buffer(new_nt_pswd.data, newpswd,
2415 STR_UNICODE);
2417 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
2418 E_old_pw_hash(new_nt_hash, old_nt_hash,
2419 old_nt_hash_enc.data);
2422 if (!full_username && !username) {
2423 x_fprintf(x_stdout, "Error: No username supplied!\n");
2424 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2425 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2426 x_fprintf(x_stdout, "Error: No NT or LM password "
2427 "blobs supplied!\n");
2428 } else {
2429 char *error_string = NULL;
2431 if (full_username && !username) {
2432 fstring fstr_user;
2433 fstring fstr_domain;
2435 if (!parse_ntlm_auth_domain_user(full_username,
2436 fstr_user,
2437 fstr_domain)) {
2438 /* username might be 'tainted', don't
2439 * print into our new-line
2440 * deleimianted stream */
2441 x_fprintf(x_stdout, "Error: Could not "
2442 "parse into domain and "
2443 "username\n");
2444 SAFE_FREE(username);
2445 username = smb_xstrdup(full_username);
2446 } else {
2447 SAFE_FREE(username);
2448 SAFE_FREE(domain);
2449 username = smb_xstrdup(fstr_user);
2450 domain = smb_xstrdup(fstr_domain);
2455 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2456 username, domain,
2457 new_nt_pswd,
2458 old_nt_hash_enc,
2459 new_lm_pswd,
2460 old_lm_hash_enc,
2461 &error_string))) {
2462 x_fprintf(x_stdout, "Password-Change: No\n");
2463 x_fprintf(x_stdout, "Password-Change-Error: "
2464 "%s\n.\n", error_string);
2465 } else {
2466 x_fprintf(x_stdout, "Password-Change: Yes\n");
2469 SAFE_FREE(error_string);
2471 /* clear out the state */
2472 new_nt_pswd = data_blob_null;
2473 old_nt_hash_enc = data_blob_null;
2474 new_lm_pswd = data_blob_null;
2475 old_nt_hash_enc = data_blob_null;
2476 SAFE_FREE(full_username);
2477 SAFE_FREE(username);
2478 SAFE_FREE(domain);
2479 SAFE_FREE(newpswd);
2480 SAFE_FREE(oldpswd);
2481 x_fprintf(x_stdout, ".\n");
2483 return;
2486 request = buf;
2488 /* Indicates a base64 encoded structure */
2489 parameter = strstr_m(request, ":: ");
2490 if (!parameter) {
2491 parameter = strstr_m(request, ": ");
2493 if (!parameter) {
2494 DEBUG(0, ("Parameter not found!\n"));
2495 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2496 return;
2499 parameter[0] ='\0';
2500 parameter++;
2501 parameter[0] ='\0';
2502 parameter++;
2503 } else {
2504 parameter[0] ='\0';
2505 parameter++;
2506 parameter[0] ='\0';
2507 parameter++;
2508 parameter[0] ='\0';
2509 parameter++;
2511 base64_decode_inplace(parameter);
2514 if (strequal(request, "new-nt-password-blob")) {
2515 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2516 if (new_nt_pswd.length != 516) {
2517 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2518 "(got %d bytes, expected 516)\n.\n",
2519 parameter,
2520 (int)new_nt_pswd.length);
2521 new_nt_pswd = data_blob_null;
2523 } else if (strequal(request, "old-nt-hash-blob")) {
2524 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2525 if (old_nt_hash_enc.length != 16) {
2526 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2527 "(got %d bytes, expected 16)\n.\n",
2528 parameter,
2529 (int)old_nt_hash_enc.length);
2530 old_nt_hash_enc = data_blob_null;
2532 } else if (strequal(request, "new-lm-password-blob")) {
2533 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2534 if (new_lm_pswd.length != 516) {
2535 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2536 "(got %d bytes, expected 516)\n.\n",
2537 parameter,
2538 (int)new_lm_pswd.length);
2539 new_lm_pswd = data_blob_null;
2542 else if (strequal(request, "old-lm-hash-blob")) {
2543 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2544 if (old_lm_hash_enc.length != 16)
2546 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2547 "(got %d bytes, expected 16)\n.\n",
2548 parameter,
2549 (int)old_lm_hash_enc.length);
2550 old_lm_hash_enc = data_blob_null;
2552 } else if (strequal(request, "nt-domain")) {
2553 domain = smb_xstrdup(parameter);
2554 } else if(strequal(request, "username")) {
2555 username = smb_xstrdup(parameter);
2556 } else if(strequal(request, "full-username")) {
2557 username = smb_xstrdup(parameter);
2558 } else if(strequal(request, "new-password")) {
2559 newpswd = smb_xstrdup(parameter);
2560 } else if (strequal(request, "old-password")) {
2561 oldpswd = smb_xstrdup(parameter);
2562 } else {
2563 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2567 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2568 struct loadparm_context *lp_ctx,
2569 struct ntlm_auth_state *state,
2570 stdio_helper_function fn, void **private2)
2572 char *buf;
2573 char tmp[INITIAL_BUFFER_SIZE+1];
2574 int length, buf_size = 0;
2575 char *c;
2577 buf = talloc_strdup(state->mem_ctx, "");
2578 if (!buf) {
2579 DEBUG(0, ("Failed to allocate input buffer.\n"));
2580 x_fprintf(x_stderr, "ERR\n");
2581 exit(1);
2584 do {
2586 /* this is not a typo - x_fgets doesn't work too well under
2587 * squid */
2588 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2589 if (ferror(stdin)) {
2590 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2591 "(%s)\n", ferror(stdin),
2592 strerror(ferror(stdin))));
2594 exit(1);
2596 exit(0);
2599 buf = talloc_strdup_append_buffer(buf, tmp);
2600 buf_size += INITIAL_BUFFER_SIZE;
2602 if (buf_size > MAX_BUFFER_SIZE) {
2603 DEBUG(2, ("Oversized message\n"));
2604 x_fprintf(x_stderr, "ERR\n");
2605 talloc_free(buf);
2606 return;
2609 c = strchr(buf, '\n');
2610 } while (c == NULL);
2612 *c = '\0';
2613 length = c-buf;
2615 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2617 if (buf[0] == '\0') {
2618 DEBUG(2, ("Invalid Request\n"));
2619 x_fprintf(x_stderr, "ERR\n");
2620 talloc_free(buf);
2621 return;
2624 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2625 talloc_free(buf);
2629 static void squid_stream(enum stdio_helper_mode stdio_mode,
2630 struct loadparm_context *lp_ctx,
2631 stdio_helper_function fn) {
2632 TALLOC_CTX *mem_ctx;
2633 struct ntlm_auth_state *state;
2635 /* initialize FDescs */
2636 x_setbuf(x_stdout, NULL);
2637 x_setbuf(x_stderr, NULL);
2639 mem_ctx = talloc_init("ntlm_auth");
2640 if (!mem_ctx) {
2641 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2642 x_fprintf(x_stderr, "ERR\n");
2643 exit(1);
2646 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2647 if (!state) {
2648 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2649 x_fprintf(x_stderr, "ERR\n");
2650 exit(1);
2653 state->mem_ctx = mem_ctx;
2654 state->helper_mode = stdio_mode;
2656 while(1) {
2657 TALLOC_CTX *frame = talloc_stackframe();
2658 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2659 TALLOC_FREE(frame);
2664 /* Authenticate a user with a challenge/response */
2666 static bool check_auth_crap(void)
2668 NTSTATUS nt_status;
2669 uint32 flags = 0;
2670 char lm_key[8];
2671 char user_session_key[16];
2672 char *hex_lm_key;
2673 char *hex_user_session_key;
2674 char *error_string;
2675 static uint8 zeros[16];
2677 x_setbuf(x_stdout, NULL);
2679 if (request_lm_key)
2680 flags |= WBFLAG_PAM_LMKEY;
2682 if (request_user_session_key)
2683 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2685 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2687 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2688 opt_workstation,
2689 &opt_challenge,
2690 &opt_lm_response,
2691 &opt_nt_response,
2692 flags, 0,
2693 (unsigned char *)lm_key,
2694 (unsigned char *)user_session_key,
2695 &error_string, NULL);
2697 if (!NT_STATUS_IS_OK(nt_status)) {
2698 x_fprintf(x_stdout, "%s (0x%x)\n",
2699 error_string,
2700 NT_STATUS_V(nt_status));
2701 SAFE_FREE(error_string);
2702 return False;
2705 if (request_lm_key
2706 && (memcmp(zeros, lm_key,
2707 sizeof(lm_key)) != 0)) {
2708 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2709 sizeof(lm_key));
2710 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2711 TALLOC_FREE(hex_lm_key);
2713 if (request_user_session_key
2714 && (memcmp(zeros, user_session_key,
2715 sizeof(user_session_key)) != 0)) {
2716 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2717 sizeof(user_session_key));
2718 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2719 TALLOC_FREE(hex_user_session_key);
2722 return True;
2725 /* Main program */
2727 enum {
2728 OPT_USERNAME = 1000,
2729 OPT_DOMAIN,
2730 OPT_WORKSTATION,
2731 OPT_CHALLENGE,
2732 OPT_RESPONSE,
2733 OPT_LM,
2734 OPT_NT,
2735 OPT_PASSWORD,
2736 OPT_LM_KEY,
2737 OPT_USER_SESSION_KEY,
2738 OPT_DIAGNOSTICS,
2739 OPT_REQUIRE_MEMBERSHIP,
2740 OPT_USE_CACHED_CREDS,
2741 OPT_PAM_WINBIND_CONF,
2742 OPT_TARGET_SERVICE,
2743 OPT_TARGET_HOSTNAME
2746 int main(int argc, const char **argv)
2748 TALLOC_CTX *frame = talloc_stackframe();
2749 int opt;
2750 static const char *helper_protocol;
2751 static int diagnostics;
2753 static const char *hex_challenge;
2754 static const char *hex_lm_response;
2755 static const char *hex_nt_response;
2756 struct loadparm_context *lp_ctx;
2757 poptContext pc;
2759 /* NOTE: DO NOT change this interface without considering the implications!
2760 This is an external interface, which other programs will use to interact
2761 with this helper.
2764 /* We do not use single-letter command abbreviations, because they harm future
2765 interface stability. */
2767 struct poptOption long_options[] = {
2768 POPT_AUTOHELP
2769 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2770 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2771 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2772 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2773 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2774 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2775 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2776 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2777 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2778 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2779 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2780 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2781 OPT_DIAGNOSTICS,
2782 "Perform diagnostics on the authentication chain"},
2783 { "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" },
2784 { "pam-winbind-conf", 0, POPT_ARG_STRING, &opt_pam_winbind_conf, OPT_PAM_WINBIND_CONF, "Require that request must set WBFLAG_PAM_CONTACT_TRUSTDOM when krb5 auth is required" },
2785 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2786 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2787 POPT_COMMON_CONFIGFILE
2788 POPT_COMMON_VERSION
2789 POPT_TABLEEND
2792 /* Samba client initialisation */
2793 load_case_tables();
2795 setup_logging("ntlm_auth", DEBUG_STDERR);
2797 /* Parse options */
2799 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2801 /* Parse command line options */
2803 if (argc == 1) {
2804 poptPrintHelp(pc, stderr, 0);
2805 return 1;
2808 while((opt = poptGetNextOpt(pc)) != -1) {
2809 /* Get generic config options like --configfile */
2812 poptFreeContext(pc);
2814 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2815 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2816 get_dyn_CONFIGFILE(), strerror(errno));
2817 exit(1);
2820 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2821 POPT_CONTEXT_KEEP_FIRST);
2823 while((opt = poptGetNextOpt(pc)) != -1) {
2824 switch (opt) {
2825 case OPT_CHALLENGE:
2826 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2827 if (opt_challenge.length != 8) {
2828 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2829 hex_challenge,
2830 (int)opt_challenge.length);
2831 exit(1);
2833 break;
2834 case OPT_LM:
2835 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2836 if (opt_lm_response.length != 24) {
2837 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2838 hex_lm_response,
2839 (int)opt_lm_response.length);
2840 exit(1);
2842 break;
2844 case OPT_NT:
2845 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2846 if (opt_nt_response.length < 24) {
2847 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2848 hex_nt_response,
2849 (int)opt_nt_response.length);
2850 exit(1);
2852 break;
2854 case OPT_REQUIRE_MEMBERSHIP:
2855 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2856 require_membership_of_sid = require_membership_of;
2858 break;
2862 if (opt_username) {
2863 char *domain = SMB_STRDUP(opt_username);
2864 char *p = strchr_m(domain, *lp_winbind_separator());
2865 if (p) {
2866 opt_username = p+1;
2867 *p = '\0';
2868 if (opt_domain && !strequal(opt_domain, domain)) {
2869 x_fprintf(x_stderr, "Domain specified in username (%s) "
2870 "doesn't match specified domain (%s)!\n\n",
2871 domain, opt_domain);
2872 poptPrintHelp(pc, stderr, 0);
2873 exit(1);
2875 opt_domain = domain;
2876 } else {
2877 SAFE_FREE(domain);
2881 /* Note: if opt_domain is "" then send no domain */
2882 if (opt_domain == NULL) {
2883 opt_domain = get_winbind_domain();
2886 if (opt_workstation == NULL) {
2887 opt_workstation = "";
2890 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2891 if (lp_ctx == NULL) {
2892 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2893 exit(1);
2896 if (helper_protocol) {
2897 int i;
2898 for (i=0; i<NUM_HELPER_MODES; i++) {
2899 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2900 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2901 exit(0);
2904 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2906 for (i=0; i<NUM_HELPER_MODES; i++) {
2907 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2910 exit(1);
2913 if (!opt_username || !*opt_username) {
2914 x_fprintf(x_stderr, "username must be specified!\n\n");
2915 poptPrintHelp(pc, stderr, 0);
2916 exit(1);
2919 if (opt_challenge.length) {
2920 if (!check_auth_crap()) {
2921 exit(1);
2923 exit(0);
2926 if (!opt_password) {
2927 opt_password = getpass("password: ");
2930 if (diagnostics) {
2931 if (!diagnose_ntlm_auth()) {
2932 return 1;
2934 } else {
2935 fstring user;
2937 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2938 if (!check_plaintext_auth(user, opt_password, True)) {
2939 return 1;
2943 /* Exit code */
2945 poptFreeContext(pc);
2946 TALLOC_FREE(frame);
2947 return 0;