dbwrap: dbwrap_trans_store_int32->dbwrap_trans_store_int32_bystring
[Samba.git] / source3 / utils / ntlm_auth.c
blob0f8e54460bb02140731021f6f16023cf23c78650
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";
870 auth_ctx->challenge.may_be_modified = true;
873 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
874 auth_ctx->challenge.set_by));
876 return NT_STATUS_OK;
880 * Some authentication methods 'fix' the challenge, so we may not be able to set it
882 * @return If the effective challenge used by the auth subsystem may be modified
884 static bool ntlm_auth_may_set_challenge(struct auth4_context *auth_ctx)
886 return auth_ctx->challenge.may_be_modified;
890 * NTLM2 authentication modifies the effective challenge,
891 * @param challenge The new challenge value
893 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
895 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
896 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
898 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
899 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
901 return NT_STATUS_OK;
905 * Check the password on an NTLMSSP login.
907 * Return the session keys used on the connection.
910 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
911 TALLOC_CTX *mem_ctx,
912 const struct auth_usersupplied_info *user_info,
913 void **server_returned_info,
914 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
916 static const char zeros[16] = { 0, };
917 NTSTATUS nt_status;
918 char *error_string = NULL;
919 uint8 lm_key[8];
920 uint8 user_sess_key[16];
921 char *unix_name = NULL;
923 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
924 user_info->workstation_name,
925 &auth4_context->challenge.data,
926 &user_info->password.response.lanman,
927 &user_info->password.response.nt,
928 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
930 lm_key, user_sess_key,
931 &error_string, &unix_name);
933 if (NT_STATUS_IS_OK(nt_status)) {
934 if (memcmp(lm_key, zeros, 8) != 0) {
935 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
936 memcpy(lm_session_key->data, lm_key, 8);
937 memset(lm_session_key->data+8, '\0', 8);
940 if (memcmp(user_sess_key, zeros, 16) != 0) {
941 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
943 *server_returned_info = talloc_strdup(mem_ctx,
944 unix_name);
945 } else {
946 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
947 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
948 user_info->client.domain_name, user_info->client.account_name,
949 user_info->workstation_name,
950 error_string ? error_string : "unknown error (NULL)"));
953 SAFE_FREE(error_string);
954 SAFE_FREE(unix_name);
955 return nt_status;
958 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
959 TALLOC_CTX *mem_ctx,
960 const struct auth_usersupplied_info *user_info,
961 void **server_returned_info,
962 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
964 NTSTATUS nt_status;
965 struct samr_Password lm_pw, nt_pw;
967 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
969 nt_status = ntlm_password_check(mem_ctx,
970 true, true, 0,
971 &auth4_context->challenge.data,
972 &user_info->password.response.lanman,
973 &user_info->password.response.nt,
974 user_info->client.account_name,
975 user_info->client.account_name,
976 user_info->client.domain_name,
977 &lm_pw, &nt_pw, session_key, lm_session_key);
979 if (NT_STATUS_IS_OK(nt_status)) {
980 *server_returned_info = talloc_asprintf(mem_ctx,
981 "%s%c%s", user_info->client.domain_name,
982 *lp_winbind_separator(),
983 user_info->client.account_name);
984 } else {
985 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
986 user_info->client.domain_name, user_info->client.account_name,
987 user_info->workstation_name,
988 nt_errstr(nt_status)));
990 return nt_status;
993 static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state)
995 NTSTATUS status;
996 if ( (opt_username == NULL) || (opt_domain == NULL) ) {
997 status = NT_STATUS_UNSUCCESSFUL;
998 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
999 return NT_STATUS_INVALID_PARAMETER;
1002 status = ntlmssp_client_start(NULL,
1003 lp_netbios_name(),
1004 lp_workgroup(),
1005 lp_client_ntlmv2_auth(),
1006 client_ntlmssp_state);
1008 if (!NT_STATUS_IS_OK(status)) {
1009 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
1010 nt_errstr(status)));
1011 TALLOC_FREE(*client_ntlmssp_state);
1012 return status;
1015 status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
1017 if (!NT_STATUS_IS_OK(status)) {
1018 DEBUG(1, ("Could not set username: %s\n",
1019 nt_errstr(status)));
1020 TALLOC_FREE(*client_ntlmssp_state);
1021 return status;
1024 status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
1026 if (!NT_STATUS_IS_OK(status)) {
1027 DEBUG(1, ("Could not set domain: %s\n",
1028 nt_errstr(status)));
1029 TALLOC_FREE(*client_ntlmssp_state);
1030 return status;
1033 if (opt_password) {
1034 status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
1036 if (!NT_STATUS_IS_OK(status)) {
1037 DEBUG(1, ("Could not set password: %s\n",
1038 nt_errstr(status)));
1039 TALLOC_FREE(*client_ntlmssp_state);
1040 return status;
1044 return NT_STATUS_OK;
1047 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1049 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1050 if (auth4_context == NULL) {
1051 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1052 return NULL;
1054 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1055 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1056 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1057 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1058 auth4_context->challenge_may_be_modified = ntlm_auth_may_set_challenge;
1059 if (local_pw) {
1060 auth4_context->check_ntlm_password = local_pw_check;
1061 } else {
1062 auth4_context->check_ntlm_password = winbind_pw_check;
1064 auth4_context->private_data = NULL;
1065 return auth4_context;
1068 static NTSTATUS ntlm_auth_start_ntlmssp_server(TALLOC_CTX *mem_ctx,
1069 struct loadparm_context *lp_ctx,
1070 struct gensec_security **gensec_security_out)
1072 struct gensec_security *gensec_security;
1073 NTSTATUS nt_status;
1075 TALLOC_CTX *tmp_ctx;
1077 struct gensec_settings *gensec_settings;
1078 size_t idx = 0;
1079 struct cli_credentials *server_credentials;
1081 struct auth4_context *auth4_context;
1083 tmp_ctx = talloc_new(mem_ctx);
1084 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1086 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1087 if (auth4_context == NULL) {
1088 TALLOC_FREE(tmp_ctx);
1089 return NT_STATUS_NO_MEMORY;
1092 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1093 if (lp_ctx == NULL) {
1094 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1095 TALLOC_FREE(tmp_ctx);
1096 return NT_STATUS_NO_MEMORY;
1100 * This should be a 'netbios domain -> DNS domain'
1101 * mapping, and can currently validly return NULL on
1102 * poorly configured systems.
1104 * This is used for the NTLMSSP server
1107 if (opt_password) {
1108 gensec_settings->server_netbios_name = lp_netbios_name();
1109 gensec_settings->server_netbios_domain = lp_workgroup();
1110 } else {
1111 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1112 gensec_settings->server_netbios_domain = get_winbind_domain();
1115 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1116 get_mydnsdomname(talloc_tos()));
1117 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1118 get_mydnsfullname());
1120 gensec_settings->backends = talloc_zero_array(gensec_settings,
1121 struct gensec_security_ops *, 4);
1123 if (gensec_settings->backends == NULL) {
1124 TALLOC_FREE(tmp_ctx);
1125 return NT_STATUS_NO_MEMORY;
1128 gensec_init();
1130 /* These need to be in priority order, krb5 before NTLMSSP */
1131 #if defined(HAVE_KRB5)
1132 gensec_settings->backends[idx++] = &gensec_gse_krb5_security_ops;
1133 #endif
1135 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1137 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL,
1138 GENSEC_OID_SPNEGO);
1141 * This is anonymous for now, because we just use it
1142 * to set the kerberos state at the moment
1144 server_credentials = cli_credentials_init_anon(tmp_ctx);
1145 if (!server_credentials) {
1146 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1147 return NT_STATUS_NO_MEMORY;
1150 cli_credentials_set_conf(server_credentials, lp_ctx);
1152 if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1153 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1154 } else {
1155 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1158 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1159 auth4_context, &gensec_security);
1161 if (!NT_STATUS_IS_OK(nt_status)) {
1162 TALLOC_FREE(tmp_ctx);
1163 return nt_status;
1166 gensec_set_credentials(gensec_security, server_credentials);
1168 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
1169 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
1171 talloc_unlink(tmp_ctx, lp_ctx);
1172 talloc_unlink(tmp_ctx, server_credentials);
1173 talloc_unlink(tmp_ctx, gensec_settings);
1174 talloc_unlink(tmp_ctx, auth4_context);
1176 nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP);
1177 if (!NT_STATUS_IS_OK(nt_status)) {
1178 TALLOC_FREE(tmp_ctx);
1179 return nt_status;
1182 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1183 TALLOC_FREE(tmp_ctx);
1184 return NT_STATUS_OK;
1187 /*******************************************************************
1188 Used by firefox to drive NTLM auth to IIS servers.
1189 *******************************************************************/
1191 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
1192 DATA_BLOB *reply)
1194 struct winbindd_request wb_request;
1195 struct winbindd_response wb_response;
1196 int ctrl = 0;
1197 NSS_STATUS result;
1199 /* get winbindd to do the ntlmssp step on our behalf */
1200 ZERO_STRUCT(wb_request);
1201 ZERO_STRUCT(wb_response);
1204 * This is tricky here. If we set krb5_auth in pam_winbind.conf
1205 * creds for users in trusted domain will be stored the winbindd
1206 * child of the trusted domain. If we ask the primary domain for
1207 * ntlm_ccache_auth, it will fail. So, we have to ask the trusted
1208 * domain's child for ccache_ntlm_auth. that is to say, we have to
1209 * set WBFLAG_PAM_CONTACT_TRUSTDOM in request.flags.
1211 ctrl = get_pam_winbind_config();
1213 if (ctrl & WINBIND_KRB5_AUTH) {
1214 wb_request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1217 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
1218 "%s%c%s", opt_domain, winbind_separator(), opt_username);
1219 wb_request.data.ccache_ntlm_auth.uid = geteuid();
1220 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
1221 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
1222 wb_request.extra_len = initial_msg.length + challenge_msg.length;
1224 if (wb_request.extra_len > 0) {
1225 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
1226 if (wb_request.extra_data.data == NULL) {
1227 return NT_STATUS_NO_MEMORY;
1230 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
1231 memcpy(wb_request.extra_data.data + initial_msg.length,
1232 challenge_msg.data, challenge_msg.length);
1235 result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
1236 SAFE_FREE(wb_request.extra_data.data);
1238 if (result != NSS_STATUS_SUCCESS) {
1239 winbindd_free_response(&wb_response);
1240 return NT_STATUS_UNSUCCESSFUL;
1243 if (reply) {
1244 *reply = data_blob(wb_response.extra_data.data,
1245 wb_response.data.ccache_ntlm_auth.auth_blob_len);
1246 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
1247 reply->data == NULL) {
1248 winbindd_free_response(&wb_response);
1249 return NT_STATUS_NO_MEMORY;
1253 winbindd_free_response(&wb_response);
1254 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1257 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1258 struct loadparm_context *lp_ctx,
1259 struct ntlm_auth_state *state,
1260 char *buf, int length, void **private2)
1262 DATA_BLOB request, reply;
1263 NTSTATUS nt_status;
1265 if (!opt_username || !*opt_username) {
1266 x_fprintf(x_stderr, "username must be specified!\n\n");
1267 exit(1);
1270 if (strlen(buf) < 2) {
1271 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1272 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1273 return;
1276 if (strlen(buf) > 3) {
1277 if(strncmp(buf, "SF ", 3) == 0) {
1278 DEBUG(10, ("Looking for flags to negotiate\n"));
1279 talloc_free(state->want_feature_list);
1280 state->want_feature_list = talloc_strdup(state->mem_ctx,
1281 buf+3);
1282 x_fprintf(x_stdout, "OK\n");
1283 return;
1285 request = base64_decode_data_blob(buf + 3);
1286 } else {
1287 request = data_blob_null;
1290 if (strncmp(buf, "PW ", 3) == 0) {
1291 /* We asked for a password and obviously got it :-) */
1293 opt_password = SMB_STRNDUP((const char *)request.data,
1294 request.length);
1296 if (opt_password == NULL) {
1297 DEBUG(1, ("Out of memory\n"));
1298 x_fprintf(x_stdout, "BH Out of memory\n");
1299 data_blob_free(&request);
1300 return;
1303 x_fprintf(x_stdout, "OK\n");
1304 data_blob_free(&request);
1305 return;
1308 if (!state->ntlmssp_state && use_cached_creds) {
1309 /* check whether cached credentials are usable. */
1310 DATA_BLOB empty_blob = data_blob_null;
1312 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
1313 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1314 /* failed to use cached creds */
1315 use_cached_creds = False;
1319 if (opt_password == NULL && !use_cached_creds) {
1320 /* Request a password from the calling process. After
1321 sending it, the calling process should retry asking for the
1322 negotiate. */
1324 DEBUG(10, ("Requesting password\n"));
1325 x_fprintf(x_stdout, "PW\n");
1326 return;
1329 if (strncmp(buf, "YR", 2) == 0) {
1330 TALLOC_FREE(state->ntlmssp_state);
1331 state->cli_state = CLIENT_INITIAL;
1332 } else if (strncmp(buf, "TT", 2) == 0) {
1333 /* No special preprocessing required */
1334 } else if (strncmp(buf, "GF", 2) == 0) {
1335 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
1337 if(state->cli_state == CLIENT_FINISHED) {
1338 x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
1340 else {
1341 x_fprintf(x_stdout, "BH\n");
1344 data_blob_free(&request);
1345 return;
1346 } else if (strncmp(buf, "GK", 2) == 0 ) {
1347 DEBUG(10, ("Requested session key\n"));
1349 if(state->cli_state == CLIENT_FINISHED) {
1350 char *key64 = base64_encode_data_blob(state->mem_ctx,
1351 state->session_key);
1352 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
1353 TALLOC_FREE(key64);
1355 else {
1356 x_fprintf(x_stdout, "BH\n");
1359 data_blob_free(&request);
1360 return;
1361 } else {
1362 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1363 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1364 return;
1367 if (!state->ntlmssp_state) {
1368 nt_status = ntlm_auth_start_ntlmssp_client(
1369 &state->ntlmssp_state);
1370 if (!NT_STATUS_IS_OK(nt_status)) {
1371 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1372 return;
1374 ntlmssp_want_feature_list(state->ntlmssp_state,
1375 state->want_feature_list);
1376 state->initial_message = data_blob_null;
1379 DEBUG(10, ("got NTLMSSP packet:\n"));
1380 dump_data(10, request.data, request.length);
1382 if (use_cached_creds && !opt_password &&
1383 (state->cli_state == CLIENT_RESPONSE)) {
1384 nt_status = do_ccache_ntlm_auth(state->initial_message, request,
1385 &reply);
1386 } else {
1387 nt_status = ntlmssp_update(state->ntlmssp_state, request,
1388 &reply);
1391 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1392 char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
1393 reply);
1394 if (state->cli_state == CLIENT_INITIAL) {
1395 x_fprintf(x_stdout, "YR %s\n", reply_base64);
1396 state->initial_message = reply;
1397 state->cli_state = CLIENT_RESPONSE;
1398 } else {
1399 x_fprintf(x_stdout, "KK %s\n", reply_base64);
1400 data_blob_free(&reply);
1402 TALLOC_FREE(reply_base64);
1403 DEBUG(10, ("NTLMSSP challenge\n"));
1404 } else if (NT_STATUS_IS_OK(nt_status)) {
1405 char *reply_base64 = base64_encode_data_blob(talloc_tos(),
1406 reply);
1407 x_fprintf(x_stdout, "AF %s\n", reply_base64);
1408 TALLOC_FREE(reply_base64);
1410 if(state->have_session_key)
1411 data_blob_free(&state->session_key);
1413 state->session_key = data_blob(
1414 state->ntlmssp_state->session_key.data,
1415 state->ntlmssp_state->session_key.length);
1416 state->neg_flags = state->ntlmssp_state->neg_flags;
1417 state->have_session_key = true;
1419 DEBUG(10, ("NTLMSSP OK!\n"));
1420 state->cli_state = CLIENT_FINISHED;
1421 TALLOC_FREE(state->ntlmssp_state);
1422 } else {
1423 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1424 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
1425 state->cli_state = CLIENT_ERROR;
1426 TALLOC_FREE(state->ntlmssp_state);
1429 data_blob_free(&request);
1432 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1433 struct loadparm_context *lp_ctx,
1434 struct ntlm_auth_state *state,
1435 char *buf, int length, void **private2)
1437 char *user, *pass;
1438 user=buf;
1440 pass=(char *)memchr(buf,' ',length);
1441 if (!pass) {
1442 DEBUG(2, ("Password not found. Denying access\n"));
1443 x_fprintf(x_stdout, "ERR\n");
1444 return;
1446 *pass='\0';
1447 pass++;
1449 if (state->helper_mode == SQUID_2_5_BASIC) {
1450 rfc1738_unescape(user);
1451 rfc1738_unescape(pass);
1454 if (check_plaintext_auth(user, pass, False)) {
1455 x_fprintf(x_stdout, "OK\n");
1456 } else {
1457 x_fprintf(x_stdout, "ERR\n");
1461 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1462 struct loadparm_context *lp_ctx,
1463 char *buf, int length, void **private1)
1465 DATA_BLOB in;
1466 DATA_BLOB out = data_blob(NULL, 0);
1467 char *out_base64 = NULL;
1468 const char *reply_arg = NULL;
1469 struct gensec_ntlm_state {
1470 struct gensec_security *gensec_state;
1471 const char *set_password;
1473 struct gensec_ntlm_state *state;
1475 NTSTATUS nt_status;
1476 bool first = false;
1477 const char *reply_code;
1478 struct cli_credentials *creds;
1480 static char *want_feature_list = NULL;
1481 static DATA_BLOB session_key;
1483 TALLOC_CTX *mem_ctx;
1485 if (*private1) {
1486 state = (struct gensec_ntlm_state *)*private1;
1487 } else {
1488 state = talloc_zero(NULL, struct gensec_ntlm_state);
1489 if (!state) {
1490 x_fprintf(x_stdout, "BH No Memory\n");
1491 exit(1);
1493 *private1 = state;
1494 if (opt_password) {
1495 state->set_password = opt_password;
1499 if (strlen(buf) < 2) {
1500 DEBUG(1, ("query [%s] invalid", buf));
1501 x_fprintf(x_stdout, "BH Query invalid\n");
1502 return;
1505 if (strlen(buf) > 3) {
1506 if(strncmp(buf, "SF ", 3) == 0) {
1507 DEBUG(10, ("Setting flags to negotiate\n"));
1508 talloc_free(want_feature_list);
1509 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1510 x_fprintf(x_stdout, "OK\n");
1511 return;
1513 in = base64_decode_data_blob(buf + 3);
1514 } else {
1515 in = data_blob(NULL, 0);
1518 if (strncmp(buf, "YR", 2) == 0) {
1519 if (state->gensec_state) {
1520 talloc_free(state->gensec_state);
1521 state->gensec_state = NULL;
1523 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1524 /* Just return BH, like ntlm_auth from Samba 3 does. */
1525 x_fprintf(x_stdout, "BH Command expected\n");
1526 data_blob_free(&in);
1527 return;
1528 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1529 (strncmp(buf, "KK ", 3) != 0) &&
1530 (strncmp(buf, "AF ", 3) != 0) &&
1531 (strncmp(buf, "NA ", 3) != 0) &&
1532 (strncmp(buf, "UG", 2) != 0) &&
1533 (strncmp(buf, "PW ", 3) != 0) &&
1534 (strncmp(buf, "GK", 2) != 0) &&
1535 (strncmp(buf, "GF", 2) != 0)) {
1536 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1537 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1538 data_blob_free(&in);
1539 return;
1542 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1544 /* setup gensec */
1545 if (!(state->gensec_state)) {
1546 switch (stdio_helper_mode) {
1547 case GSS_SPNEGO_CLIENT:
1548 case NTLMSSP_CLIENT_1:
1549 /* setup the client side */
1551 nt_status = gensec_client_start(NULL, &state->gensec_state,
1552 lpcfg_gensec_settings(NULL, lp_ctx));
1553 if (!NT_STATUS_IS_OK(nt_status)) {
1554 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1555 talloc_free(mem_ctx);
1556 return;
1559 creds = cli_credentials_init(state->gensec_state);
1560 cli_credentials_set_conf(creds, lp_ctx);
1561 if (opt_username) {
1562 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1564 if (opt_domain) {
1565 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1567 if (state->set_password) {
1568 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1569 } else {
1570 cli_credentials_set_password_callback(creds, get_password);
1572 if (opt_workstation) {
1573 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1576 gensec_set_credentials(state->gensec_state, creds);
1578 break;
1579 case GSS_SPNEGO_SERVER:
1580 case SQUID_2_5_NTLMSSP:
1582 nt_status = ntlm_auth_start_ntlmssp_server(state, lp_ctx,
1583 &state->gensec_state);
1584 if (!NT_STATUS_IS_OK(nt_status)) {
1585 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1586 talloc_free(mem_ctx);
1587 return;
1589 break;
1591 default:
1592 talloc_free(mem_ctx);
1593 abort();
1596 gensec_want_feature_list(state->gensec_state, want_feature_list);
1598 switch (stdio_helper_mode) {
1599 case GSS_SPNEGO_CLIENT:
1600 case GSS_SPNEGO_SERVER:
1601 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1602 if (!in.length) {
1603 first = true;
1605 break;
1606 case NTLMSSP_CLIENT_1:
1607 if (!in.length) {
1608 first = true;
1610 /* fall through */
1611 case SQUID_2_5_NTLMSSP:
1612 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1613 break;
1614 default:
1615 talloc_free(mem_ctx);
1616 abort();
1619 if (!NT_STATUS_IS_OK(nt_status)) {
1620 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1621 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1622 talloc_free(mem_ctx);
1623 return;
1628 /* update */
1630 if (strncmp(buf, "PW ", 3) == 0) {
1631 state->set_password = talloc_strndup(state,
1632 (const char *)in.data,
1633 in.length);
1635 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1636 state->set_password,
1637 CRED_SPECIFIED);
1638 x_fprintf(x_stdout, "OK\n");
1639 data_blob_free(&in);
1640 talloc_free(mem_ctx);
1641 return;
1644 if (strncmp(buf, "GK", 2) == 0) {
1645 char *base64_key;
1646 DEBUG(10, ("Requested session key\n"));
1647 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1648 if(!NT_STATUS_IS_OK(nt_status)) {
1649 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1650 x_fprintf(x_stdout, "BH No session key\n");
1651 talloc_free(mem_ctx);
1652 return;
1653 } else {
1654 base64_key = base64_encode_data_blob(state, session_key);
1655 x_fprintf(x_stdout, "GK %s\n", base64_key);
1656 talloc_free(base64_key);
1658 talloc_free(mem_ctx);
1659 return;
1662 if (stdio_helper_mode == SQUID_2_5_NTLMSSP && strncmp(buf, "GF", 2) == 0) {
1663 uint32_t neg_flags;
1665 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1667 DEBUG(10, ("Requested negotiated feature flags\n"));
1668 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1669 return;
1672 nt_status = gensec_update(state->gensec_state, mem_ctx, NULL, in, &out);
1674 /* don't leak 'bad password'/'no such user' info to the network client */
1675 nt_status = nt_status_squash(nt_status);
1677 if (out.length) {
1678 out_base64 = base64_encode_data_blob(mem_ctx, out);
1679 } else {
1680 out_base64 = NULL;
1683 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1684 reply_arg = "*";
1685 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1686 reply_code = "YR";
1687 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1688 reply_code = "KK";
1689 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1690 reply_code = "TT";
1691 } else {
1692 abort();
1696 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1697 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1698 reply_arg = nt_errstr(nt_status);
1699 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1700 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1701 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1702 reply_arg = nt_errstr(nt_status);
1703 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1704 } else if (!NT_STATUS_IS_OK(nt_status)) {
1705 reply_code = "NA";
1706 reply_arg = nt_errstr(nt_status);
1707 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1708 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1709 struct auth_session_info *session_info;
1711 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1712 if (!NT_STATUS_IS_OK(nt_status)) {
1713 reply_code = "BH Failed to retrive session info";
1714 reply_arg = nt_errstr(nt_status);
1715 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1716 } else {
1718 reply_code = "AF";
1719 reply_arg = session_info->unix_info->unix_name;
1720 talloc_free(session_info);
1722 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1723 reply_code = "AF";
1724 reply_arg = out_base64;
1725 } else {
1726 abort();
1729 switch (stdio_helper_mode) {
1730 case GSS_SPNEGO_SERVER:
1731 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1732 out_base64 ? out_base64 : "*",
1733 reply_arg ? reply_arg : "*");
1734 break;
1735 default:
1736 if (out_base64) {
1737 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1738 } else if (reply_arg) {
1739 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1740 } else {
1741 x_fprintf(x_stdout, "%s\n", reply_code);
1745 talloc_free(mem_ctx);
1746 return;
1749 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1750 struct loadparm_context *lp_ctx,
1751 struct ntlm_auth_state *state,
1752 char *buf, int length, void **private2)
1754 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1755 return;
1758 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1759 struct loadparm_context *lp_ctx,
1760 struct ntlm_auth_state *state,
1761 char *buf, int length, void **private2)
1763 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1764 return;
1767 static struct ntlmssp_state *client_ntlmssp_state = NULL;
1769 static bool manage_client_ntlmssp_init(struct spnego_data spnego)
1771 NTSTATUS status;
1772 DATA_BLOB null_blob = data_blob_null;
1773 DATA_BLOB to_server;
1774 char *to_server_base64;
1775 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1776 TALLOC_CTX *ctx = talloc_tos();
1778 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1780 if (client_ntlmssp_state != NULL) {
1781 DEBUG(1, ("Request for initial SPNEGO request where "
1782 "we already have a state\n"));
1783 return False;
1786 if (!client_ntlmssp_state) {
1787 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1788 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1789 return False;
1794 if (opt_password == NULL) {
1796 /* Request a password from the calling process. After
1797 sending it, the calling process should retry with
1798 the negTokenInit. */
1800 DEBUG(10, ("Requesting password\n"));
1801 x_fprintf(x_stdout, "PW\n");
1802 return True;
1805 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1806 spnego.negTokenInit.mechTypes = my_mechs;
1807 spnego.negTokenInit.reqFlags = data_blob_null;
1808 spnego.negTokenInit.reqFlagsPadding = 0;
1809 spnego.negTokenInit.mechListMIC = null_blob;
1811 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1812 &spnego.negTokenInit.mechToken);
1814 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1815 NT_STATUS_IS_OK(status)) ) {
1816 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1817 nt_errstr(status)));
1818 TALLOC_FREE(client_ntlmssp_state);
1819 return False;
1822 spnego_write_data(ctx, &to_server, &spnego);
1823 data_blob_free(&spnego.negTokenInit.mechToken);
1825 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1826 data_blob_free(&to_server);
1827 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1828 TALLOC_FREE(to_server_base64);
1829 return True;
1832 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
1834 NTSTATUS status;
1835 DATA_BLOB null_blob = data_blob_null;
1836 DATA_BLOB request;
1837 DATA_BLOB to_server;
1838 char *to_server_base64;
1839 TALLOC_CTX *ctx = talloc_tos();
1841 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1843 if (client_ntlmssp_state == NULL) {
1844 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1845 x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
1846 return;
1849 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1850 x_fprintf(x_stdout, "NA\n");
1851 TALLOC_FREE(client_ntlmssp_state);
1852 return;
1855 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1856 x_fprintf(x_stdout, "AF\n");
1857 TALLOC_FREE(client_ntlmssp_state);
1858 return;
1861 status = ntlmssp_update(client_ntlmssp_state,
1862 spnego.negTokenTarg.responseToken,
1863 &request);
1865 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
1866 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED or OK from "
1867 "ntlmssp_client_update, got: %s\n",
1868 nt_errstr(status)));
1869 x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
1870 "ntlmssp_client_update\n");
1871 data_blob_free(&request);
1872 TALLOC_FREE(client_ntlmssp_state);
1873 return;
1876 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1877 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1878 spnego.negTokenTarg.supportedMech = (const char *)OID_NTLMSSP;
1879 spnego.negTokenTarg.responseToken = request;
1880 spnego.negTokenTarg.mechListMIC = null_blob;
1882 spnego_write_data(ctx, &to_server, &spnego);
1883 data_blob_free(&request);
1885 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1886 data_blob_free(&to_server);
1887 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1888 TALLOC_FREE(to_server_base64);
1889 return;
1892 #ifdef HAVE_KRB5
1894 static bool manage_client_krb5_init(struct spnego_data spnego)
1896 char *principal;
1897 DATA_BLOB tkt, tkt_wrapped, to_server;
1898 DATA_BLOB session_key_krb5 = data_blob_null;
1899 struct spnego_data reply;
1900 char *reply_base64;
1901 int retval;
1903 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1904 ssize_t len;
1905 TALLOC_CTX *ctx = talloc_tos();
1907 principal = spnego.negTokenInit.targetPrincipal;
1909 /* We may not be allowed to use the server-supplied SPNEGO principal, or it may not have been supplied to us
1911 if (!lp_client_use_spnego_principal() || strequal(principal, ADS_IGNORE_PRINCIPAL)) {
1912 principal = NULL;
1915 if (principal == NULL &&
1916 opt_target_service && opt_target_hostname && !is_ipaddress(opt_target_hostname)) {
1917 DEBUG(3,("manage_client_krb5_init: using target "
1918 "hostname not SPNEGO principal\n"));
1920 principal = kerberos_get_principal_from_service_hostname(talloc_tos(),
1921 opt_target_service,
1922 opt_target_hostname,
1923 lp_realm());
1925 if (!principal) {
1926 return false;
1929 DEBUG(3,("manage_client_krb5_init: guessed "
1930 "server principal=%s\n",
1931 principal ? principal : "<null>"));
1934 if (principal == NULL) {
1935 DEBUG(3,("manage_client_krb5_init: could not guess server principal\n"));
1936 return false;
1939 retval = cli_krb5_get_ticket(ctx, principal, 0,
1940 &tkt, &session_key_krb5,
1941 0, NULL, NULL, NULL);
1942 if (retval) {
1943 char *user = NULL;
1945 /* Let's try to first get the TGT, for that we need a
1946 password. */
1948 if (opt_password == NULL) {
1949 DEBUG(10, ("Requesting password\n"));
1950 x_fprintf(x_stdout, "PW\n");
1951 return True;
1954 user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
1955 if (!user) {
1956 return false;
1959 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1960 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1961 return False;
1964 retval = cli_krb5_get_ticket(ctx, principal, 0,
1965 &tkt, &session_key_krb5,
1966 0, NULL, NULL, NULL);
1967 if (retval) {
1968 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1969 return False;
1974 /* wrap that up in a nice GSS-API wrapping */
1975 tkt_wrapped = spnego_gen_krb5_wrap(ctx, tkt, TOK_ID_KRB_AP_REQ);
1977 data_blob_free(&session_key_krb5);
1979 ZERO_STRUCT(reply);
1981 reply.type = SPNEGO_NEG_TOKEN_INIT;
1982 reply.negTokenInit.mechTypes = my_mechs;
1983 reply.negTokenInit.reqFlags = data_blob_null;
1984 reply.negTokenInit.reqFlagsPadding = 0;
1985 reply.negTokenInit.mechToken = tkt_wrapped;
1986 reply.negTokenInit.mechListMIC = data_blob_null;
1988 len = spnego_write_data(ctx, &to_server, &reply);
1989 data_blob_free(&tkt);
1991 if (len == -1) {
1992 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1993 return False;
1996 reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1997 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1999 TALLOC_FREE(reply_base64);
2000 data_blob_free(&to_server);
2001 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
2002 return True;
2005 static void manage_client_krb5_targ(struct spnego_data spnego)
2007 switch (spnego.negTokenTarg.negResult) {
2008 case SPNEGO_ACCEPT_INCOMPLETE:
2009 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
2010 x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
2011 "ACCEPT_INCOMPLETE\n");
2012 break;
2013 case SPNEGO_ACCEPT_COMPLETED:
2014 DEBUG(10, ("Accept completed\n"));
2015 x_fprintf(x_stdout, "AF\n");
2016 break;
2017 case SPNEGO_REJECT:
2018 DEBUG(10, ("Rejected\n"));
2019 x_fprintf(x_stdout, "NA\n");
2020 break;
2021 default:
2022 DEBUG(1, ("Got an invalid negTokenTarg\n"));
2023 x_fprintf(x_stdout, "AF\n");
2027 #endif
2029 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
2030 struct loadparm_context *lp_ctx,
2031 struct ntlm_auth_state *state,
2032 char *buf, int length, void **private2)
2034 DATA_BLOB request;
2035 struct spnego_data spnego;
2036 ssize_t len;
2037 TALLOC_CTX *ctx = talloc_tos();
2039 if (!opt_username || !*opt_username) {
2040 x_fprintf(x_stderr, "username must be specified!\n\n");
2041 exit(1);
2044 if (strlen(buf) <= 3) {
2045 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
2046 x_fprintf(x_stdout, "BH SPNEGO query too short\n");
2047 return;
2050 request = base64_decode_data_blob(buf+3);
2052 if (strncmp(buf, "PW ", 3) == 0) {
2054 /* We asked for a password and obviously got it :-) */
2056 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
2058 if (opt_password == NULL) {
2059 DEBUG(1, ("Out of memory\n"));
2060 x_fprintf(x_stdout, "BH Out of memory\n");
2061 data_blob_free(&request);
2062 return;
2065 x_fprintf(x_stdout, "OK\n");
2066 data_blob_free(&request);
2067 return;
2070 if ( (strncmp(buf, "TT ", 3) != 0) &&
2071 (strncmp(buf, "AF ", 3) != 0) &&
2072 (strncmp(buf, "NA ", 3) != 0) ) {
2073 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
2074 x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
2075 data_blob_free(&request);
2076 return;
2079 /* So we got a server challenge to generate a SPNEGO
2080 client-to-server request... */
2082 len = spnego_read_data(ctx, request, &spnego);
2083 data_blob_free(&request);
2085 if (len == -1) {
2086 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
2087 x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
2088 return;
2091 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
2093 /* The server offers a list of mechanisms */
2095 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
2097 while (*mechType != NULL) {
2099 #ifdef HAVE_KRB5
2100 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
2101 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
2102 if (manage_client_krb5_init(spnego))
2103 goto out;
2105 #endif
2107 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
2108 if (manage_client_ntlmssp_init(spnego))
2109 goto out;
2112 mechType++;
2115 DEBUG(1, ("Server offered no compatible mechanism\n"));
2116 x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
2117 return;
2120 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
2122 if (spnego.negTokenTarg.supportedMech == NULL) {
2123 /* On accept/reject Windows does not send the
2124 mechanism anymore. Handle that here and
2125 shut down the mechanisms. */
2127 switch (spnego.negTokenTarg.negResult) {
2128 case SPNEGO_ACCEPT_COMPLETED:
2129 x_fprintf(x_stdout, "AF\n");
2130 break;
2131 case SPNEGO_REJECT:
2132 x_fprintf(x_stdout, "NA\n");
2133 break;
2134 default:
2135 DEBUG(1, ("Got a negTokenTarg with no mech and an "
2136 "unknown negResult: %d\n",
2137 spnego.negTokenTarg.negResult));
2138 x_fprintf(x_stdout, "BH Got a negTokenTarg with"
2139 " no mech and an unknown "
2140 "negResult\n");
2143 TALLOC_FREE(client_ntlmssp_state);
2144 goto out;
2147 if (strcmp(spnego.negTokenTarg.supportedMech,
2148 OID_NTLMSSP) == 0) {
2149 manage_client_ntlmssp_targ(spnego);
2150 goto out;
2153 #if HAVE_KRB5
2154 if (strcmp(spnego.negTokenTarg.supportedMech,
2155 OID_KERBEROS5_OLD) == 0) {
2156 manage_client_krb5_targ(spnego);
2157 goto out;
2159 #endif
2163 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
2164 x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
2165 return;
2167 out:
2168 spnego_free_data(&spnego);
2169 return;
2172 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
2173 struct loadparm_context *lp_ctx,
2174 struct ntlm_auth_state *state,
2175 char *buf, int length, void **private2)
2177 char *request, *parameter;
2178 static DATA_BLOB challenge;
2179 static DATA_BLOB lm_response;
2180 static DATA_BLOB nt_response;
2181 static char *full_username;
2182 static char *username;
2183 static char *domain;
2184 static char *plaintext_password;
2185 static bool ntlm_server_1_user_session_key;
2186 static bool ntlm_server_1_lm_session_key;
2188 if (strequal(buf, ".")) {
2189 if (!full_username && !username) {
2190 x_fprintf(x_stdout, "Error: No username supplied!\n");
2191 } else if (plaintext_password) {
2192 /* handle this request as plaintext */
2193 if (!full_username) {
2194 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
2195 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
2196 return;
2199 if (check_plaintext_auth(full_username, plaintext_password, False)) {
2200 x_fprintf(x_stdout, "Authenticated: Yes\n");
2201 } else {
2202 x_fprintf(x_stdout, "Authenticated: No\n");
2204 } else if (!lm_response.data && !nt_response.data) {
2205 x_fprintf(x_stdout, "Error: No password supplied!\n");
2206 } else if (!challenge.data) {
2207 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
2208 } else {
2209 char *error_string = NULL;
2210 uchar lm_key[8];
2211 uchar user_session_key[16];
2212 uint32 flags = 0;
2214 if (full_username && !username) {
2215 fstring fstr_user;
2216 fstring fstr_domain;
2218 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
2219 /* username might be 'tainted', don't print into our new-line deleimianted stream */
2220 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
2222 SAFE_FREE(username);
2223 SAFE_FREE(domain);
2224 username = smb_xstrdup(fstr_user);
2225 domain = smb_xstrdup(fstr_domain);
2228 if (!domain) {
2229 domain = smb_xstrdup(get_winbind_domain());
2232 if (ntlm_server_1_lm_session_key)
2233 flags |= WBFLAG_PAM_LMKEY;
2235 if (ntlm_server_1_user_session_key)
2236 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2238 if (!NT_STATUS_IS_OK(
2239 contact_winbind_auth_crap(username,
2240 domain,
2241 lp_netbios_name(),
2242 &challenge,
2243 &lm_response,
2244 &nt_response,
2245 flags, 0,
2246 lm_key,
2247 user_session_key,
2248 &error_string,
2249 NULL))) {
2251 x_fprintf(x_stdout, "Authenticated: No\n");
2252 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
2253 } else {
2254 static char zeros[16];
2255 char *hex_lm_key;
2256 char *hex_user_session_key;
2258 x_fprintf(x_stdout, "Authenticated: Yes\n");
2260 if (ntlm_server_1_lm_session_key
2261 && (memcmp(zeros, lm_key,
2262 sizeof(lm_key)) != 0)) {
2263 hex_lm_key = hex_encode_talloc(NULL,
2264 (const unsigned char *)lm_key,
2265 sizeof(lm_key));
2266 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
2267 TALLOC_FREE(hex_lm_key);
2270 if (ntlm_server_1_user_session_key
2271 && (memcmp(zeros, user_session_key,
2272 sizeof(user_session_key)) != 0)) {
2273 hex_user_session_key = hex_encode_talloc(NULL,
2274 (const unsigned char *)user_session_key,
2275 sizeof(user_session_key));
2276 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
2277 TALLOC_FREE(hex_user_session_key);
2280 SAFE_FREE(error_string);
2282 /* clear out the state */
2283 challenge = data_blob_null;
2284 nt_response = data_blob_null;
2285 lm_response = data_blob_null;
2286 SAFE_FREE(full_username);
2287 SAFE_FREE(username);
2288 SAFE_FREE(domain);
2289 SAFE_FREE(plaintext_password);
2290 ntlm_server_1_user_session_key = False;
2291 ntlm_server_1_lm_session_key = False;
2292 x_fprintf(x_stdout, ".\n");
2294 return;
2297 request = buf;
2299 /* Indicates a base64 encoded structure */
2300 parameter = strstr_m(request, ":: ");
2301 if (!parameter) {
2302 parameter = strstr_m(request, ": ");
2304 if (!parameter) {
2305 DEBUG(0, ("Parameter not found!\n"));
2306 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2307 return;
2310 parameter[0] ='\0';
2311 parameter++;
2312 parameter[0] ='\0';
2313 parameter++;
2315 } else {
2316 parameter[0] ='\0';
2317 parameter++;
2318 parameter[0] ='\0';
2319 parameter++;
2320 parameter[0] ='\0';
2321 parameter++;
2323 base64_decode_inplace(parameter);
2326 if (strequal(request, "LANMAN-Challenge")) {
2327 challenge = strhex_to_data_blob(NULL, parameter);
2328 if (challenge.length != 8) {
2329 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
2330 parameter,
2331 (int)challenge.length);
2332 challenge = data_blob_null;
2334 } else if (strequal(request, "NT-Response")) {
2335 nt_response = strhex_to_data_blob(NULL, parameter);
2336 if (nt_response.length < 24) {
2337 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
2338 parameter,
2339 (int)nt_response.length);
2340 nt_response = data_blob_null;
2342 } else if (strequal(request, "LANMAN-Response")) {
2343 lm_response = strhex_to_data_blob(NULL, parameter);
2344 if (lm_response.length != 24) {
2345 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
2346 parameter,
2347 (int)lm_response.length);
2348 lm_response = data_blob_null;
2350 } else if (strequal(request, "Password")) {
2351 plaintext_password = smb_xstrdup(parameter);
2352 } else if (strequal(request, "NT-Domain")) {
2353 domain = smb_xstrdup(parameter);
2354 } else if (strequal(request, "Username")) {
2355 username = smb_xstrdup(parameter);
2356 } else if (strequal(request, "Full-Username")) {
2357 full_username = smb_xstrdup(parameter);
2358 } else if (strequal(request, "Request-User-Session-Key")) {
2359 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2360 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2361 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2362 } else {
2363 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2367 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2368 struct loadparm_context *lp_ctx,
2369 struct ntlm_auth_state *state,
2370 char *buf, int length, void **private2)
2372 char *request, *parameter;
2373 static DATA_BLOB new_nt_pswd;
2374 static DATA_BLOB old_nt_hash_enc;
2375 static DATA_BLOB new_lm_pswd;
2376 static DATA_BLOB old_lm_hash_enc;
2377 static char *full_username = NULL;
2378 static char *username = NULL;
2379 static char *domain = NULL;
2380 static char *newpswd = NULL;
2381 static char *oldpswd = NULL;
2383 if (strequal(buf, ".")) {
2384 if(newpswd && oldpswd) {
2385 uchar old_nt_hash[16];
2386 uchar old_lm_hash[16];
2387 uchar new_nt_hash[16];
2388 uchar new_lm_hash[16];
2390 new_nt_pswd = data_blob(NULL, 516);
2391 old_nt_hash_enc = data_blob(NULL, 16);
2393 /* Calculate the MD4 hash (NT compatible) of the
2394 * password */
2395 E_md4hash(oldpswd, old_nt_hash);
2396 E_md4hash(newpswd, new_nt_hash);
2398 /* E_deshash returns false for 'long'
2399 passwords (> 14 DOS chars).
2401 Therefore, don't send a buffer
2402 encrypted with the truncated hash
2403 (it could allow an even easier
2404 attack on the password)
2406 Likewise, obey the admin's restriction
2409 if (lp_client_lanman_auth() &&
2410 E_deshash(newpswd, new_lm_hash) &&
2411 E_deshash(oldpswd, old_lm_hash)) {
2412 new_lm_pswd = data_blob(NULL, 516);
2413 old_lm_hash_enc = data_blob(NULL, 16);
2414 encode_pw_buffer(new_lm_pswd.data, newpswd,
2415 STR_UNICODE);
2417 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
2418 E_old_pw_hash(new_nt_hash, old_lm_hash,
2419 old_lm_hash_enc.data);
2420 } else {
2421 new_lm_pswd.data = NULL;
2422 new_lm_pswd.length = 0;
2423 old_lm_hash_enc.data = NULL;
2424 old_lm_hash_enc.length = 0;
2427 encode_pw_buffer(new_nt_pswd.data, newpswd,
2428 STR_UNICODE);
2430 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
2431 E_old_pw_hash(new_nt_hash, old_nt_hash,
2432 old_nt_hash_enc.data);
2435 if (!full_username && !username) {
2436 x_fprintf(x_stdout, "Error: No username supplied!\n");
2437 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2438 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2439 x_fprintf(x_stdout, "Error: No NT or LM password "
2440 "blobs supplied!\n");
2441 } else {
2442 char *error_string = NULL;
2444 if (full_username && !username) {
2445 fstring fstr_user;
2446 fstring fstr_domain;
2448 if (!parse_ntlm_auth_domain_user(full_username,
2449 fstr_user,
2450 fstr_domain)) {
2451 /* username might be 'tainted', don't
2452 * print into our new-line
2453 * deleimianted stream */
2454 x_fprintf(x_stdout, "Error: Could not "
2455 "parse into domain and "
2456 "username\n");
2457 SAFE_FREE(username);
2458 username = smb_xstrdup(full_username);
2459 } else {
2460 SAFE_FREE(username);
2461 SAFE_FREE(domain);
2462 username = smb_xstrdup(fstr_user);
2463 domain = smb_xstrdup(fstr_domain);
2468 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2469 username, domain,
2470 new_nt_pswd,
2471 old_nt_hash_enc,
2472 new_lm_pswd,
2473 old_lm_hash_enc,
2474 &error_string))) {
2475 x_fprintf(x_stdout, "Password-Change: No\n");
2476 x_fprintf(x_stdout, "Password-Change-Error: "
2477 "%s\n.\n", error_string);
2478 } else {
2479 x_fprintf(x_stdout, "Password-Change: Yes\n");
2482 SAFE_FREE(error_string);
2484 /* clear out the state */
2485 new_nt_pswd = data_blob_null;
2486 old_nt_hash_enc = data_blob_null;
2487 new_lm_pswd = data_blob_null;
2488 old_nt_hash_enc = data_blob_null;
2489 SAFE_FREE(full_username);
2490 SAFE_FREE(username);
2491 SAFE_FREE(domain);
2492 SAFE_FREE(newpswd);
2493 SAFE_FREE(oldpswd);
2494 x_fprintf(x_stdout, ".\n");
2496 return;
2499 request = buf;
2501 /* Indicates a base64 encoded structure */
2502 parameter = strstr_m(request, ":: ");
2503 if (!parameter) {
2504 parameter = strstr_m(request, ": ");
2506 if (!parameter) {
2507 DEBUG(0, ("Parameter not found!\n"));
2508 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2509 return;
2512 parameter[0] ='\0';
2513 parameter++;
2514 parameter[0] ='\0';
2515 parameter++;
2516 } else {
2517 parameter[0] ='\0';
2518 parameter++;
2519 parameter[0] ='\0';
2520 parameter++;
2521 parameter[0] ='\0';
2522 parameter++;
2524 base64_decode_inplace(parameter);
2527 if (strequal(request, "new-nt-password-blob")) {
2528 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2529 if (new_nt_pswd.length != 516) {
2530 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2531 "(got %d bytes, expected 516)\n.\n",
2532 parameter,
2533 (int)new_nt_pswd.length);
2534 new_nt_pswd = data_blob_null;
2536 } else if (strequal(request, "old-nt-hash-blob")) {
2537 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2538 if (old_nt_hash_enc.length != 16) {
2539 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2540 "(got %d bytes, expected 16)\n.\n",
2541 parameter,
2542 (int)old_nt_hash_enc.length);
2543 old_nt_hash_enc = data_blob_null;
2545 } else if (strequal(request, "new-lm-password-blob")) {
2546 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2547 if (new_lm_pswd.length != 516) {
2548 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2549 "(got %d bytes, expected 516)\n.\n",
2550 parameter,
2551 (int)new_lm_pswd.length);
2552 new_lm_pswd = data_blob_null;
2555 else if (strequal(request, "old-lm-hash-blob")) {
2556 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2557 if (old_lm_hash_enc.length != 16)
2559 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2560 "(got %d bytes, expected 16)\n.\n",
2561 parameter,
2562 (int)old_lm_hash_enc.length);
2563 old_lm_hash_enc = data_blob_null;
2565 } else if (strequal(request, "nt-domain")) {
2566 domain = smb_xstrdup(parameter);
2567 } else if(strequal(request, "username")) {
2568 username = smb_xstrdup(parameter);
2569 } else if(strequal(request, "full-username")) {
2570 username = smb_xstrdup(parameter);
2571 } else if(strequal(request, "new-password")) {
2572 newpswd = smb_xstrdup(parameter);
2573 } else if (strequal(request, "old-password")) {
2574 oldpswd = smb_xstrdup(parameter);
2575 } else {
2576 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2580 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2581 struct loadparm_context *lp_ctx,
2582 struct ntlm_auth_state *state,
2583 stdio_helper_function fn, void **private2)
2585 char *buf;
2586 char tmp[INITIAL_BUFFER_SIZE+1];
2587 int length, buf_size = 0;
2588 char *c;
2590 buf = talloc_strdup(state->mem_ctx, "");
2591 if (!buf) {
2592 DEBUG(0, ("Failed to allocate input buffer.\n"));
2593 x_fprintf(x_stderr, "ERR\n");
2594 exit(1);
2597 do {
2599 /* this is not a typo - x_fgets doesn't work too well under
2600 * squid */
2601 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2602 if (ferror(stdin)) {
2603 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2604 "(%s)\n", ferror(stdin),
2605 strerror(ferror(stdin))));
2607 exit(1);
2609 exit(0);
2612 buf = talloc_strdup_append_buffer(buf, tmp);
2613 buf_size += INITIAL_BUFFER_SIZE;
2615 if (buf_size > MAX_BUFFER_SIZE) {
2616 DEBUG(2, ("Oversized message\n"));
2617 x_fprintf(x_stderr, "ERR\n");
2618 talloc_free(buf);
2619 return;
2622 c = strchr(buf, '\n');
2623 } while (c == NULL);
2625 *c = '\0';
2626 length = c-buf;
2628 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2630 if (buf[0] == '\0') {
2631 DEBUG(2, ("Invalid Request\n"));
2632 x_fprintf(x_stderr, "ERR\n");
2633 talloc_free(buf);
2634 return;
2637 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2638 talloc_free(buf);
2642 static void squid_stream(enum stdio_helper_mode stdio_mode,
2643 struct loadparm_context *lp_ctx,
2644 stdio_helper_function fn) {
2645 TALLOC_CTX *mem_ctx;
2646 struct ntlm_auth_state *state;
2648 /* initialize FDescs */
2649 x_setbuf(x_stdout, NULL);
2650 x_setbuf(x_stderr, NULL);
2652 mem_ctx = talloc_init("ntlm_auth");
2653 if (!mem_ctx) {
2654 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2655 x_fprintf(x_stderr, "ERR\n");
2656 exit(1);
2659 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2660 if (!state) {
2661 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2662 x_fprintf(x_stderr, "ERR\n");
2663 exit(1);
2666 state->mem_ctx = mem_ctx;
2667 state->helper_mode = stdio_mode;
2669 while(1) {
2670 TALLOC_CTX *frame = talloc_stackframe();
2671 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2672 TALLOC_FREE(frame);
2677 /* Authenticate a user with a challenge/response */
2679 static bool check_auth_crap(void)
2681 NTSTATUS nt_status;
2682 uint32 flags = 0;
2683 char lm_key[8];
2684 char user_session_key[16];
2685 char *hex_lm_key;
2686 char *hex_user_session_key;
2687 char *error_string;
2688 static uint8 zeros[16];
2690 x_setbuf(x_stdout, NULL);
2692 if (request_lm_key)
2693 flags |= WBFLAG_PAM_LMKEY;
2695 if (request_user_session_key)
2696 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2698 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2700 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2701 opt_workstation,
2702 &opt_challenge,
2703 &opt_lm_response,
2704 &opt_nt_response,
2705 flags, 0,
2706 (unsigned char *)lm_key,
2707 (unsigned char *)user_session_key,
2708 &error_string, NULL);
2710 if (!NT_STATUS_IS_OK(nt_status)) {
2711 x_fprintf(x_stdout, "%s (0x%x)\n",
2712 error_string,
2713 NT_STATUS_V(nt_status));
2714 SAFE_FREE(error_string);
2715 return False;
2718 if (request_lm_key
2719 && (memcmp(zeros, lm_key,
2720 sizeof(lm_key)) != 0)) {
2721 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2722 sizeof(lm_key));
2723 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2724 TALLOC_FREE(hex_lm_key);
2726 if (request_user_session_key
2727 && (memcmp(zeros, user_session_key,
2728 sizeof(user_session_key)) != 0)) {
2729 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2730 sizeof(user_session_key));
2731 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2732 TALLOC_FREE(hex_user_session_key);
2735 return True;
2738 /* Main program */
2740 enum {
2741 OPT_USERNAME = 1000,
2742 OPT_DOMAIN,
2743 OPT_WORKSTATION,
2744 OPT_CHALLENGE,
2745 OPT_RESPONSE,
2746 OPT_LM,
2747 OPT_NT,
2748 OPT_PASSWORD,
2749 OPT_LM_KEY,
2750 OPT_USER_SESSION_KEY,
2751 OPT_DIAGNOSTICS,
2752 OPT_REQUIRE_MEMBERSHIP,
2753 OPT_USE_CACHED_CREDS,
2754 OPT_PAM_WINBIND_CONF,
2755 OPT_TARGET_SERVICE,
2756 OPT_TARGET_HOSTNAME
2759 int main(int argc, const char **argv)
2761 TALLOC_CTX *frame = talloc_stackframe();
2762 int opt;
2763 static const char *helper_protocol;
2764 static int diagnostics;
2766 static const char *hex_challenge;
2767 static const char *hex_lm_response;
2768 static const char *hex_nt_response;
2769 struct loadparm_context *lp_ctx;
2770 poptContext pc;
2772 /* NOTE: DO NOT change this interface without considering the implications!
2773 This is an external interface, which other programs will use to interact
2774 with this helper.
2777 /* We do not use single-letter command abbreviations, because they harm future
2778 interface stability. */
2780 struct poptOption long_options[] = {
2781 POPT_AUTOHELP
2782 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2783 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2784 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2785 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2786 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2787 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2788 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2789 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2790 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2791 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2792 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2793 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2794 OPT_DIAGNOSTICS,
2795 "Perform diagnostics on the authentication chain"},
2796 { "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" },
2797 { "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" },
2798 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2799 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2800 POPT_COMMON_CONFIGFILE
2801 POPT_COMMON_VERSION
2802 POPT_TABLEEND
2805 /* Samba client initialisation */
2806 load_case_tables();
2808 setup_logging("ntlm_auth", DEBUG_STDERR);
2810 /* Parse options */
2812 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2814 /* Parse command line options */
2816 if (argc == 1) {
2817 poptPrintHelp(pc, stderr, 0);
2818 return 1;
2821 while((opt = poptGetNextOpt(pc)) != -1) {
2822 /* Get generic config options like --configfile */
2825 poptFreeContext(pc);
2827 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2828 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2829 get_dyn_CONFIGFILE(), strerror(errno));
2830 exit(1);
2833 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2834 POPT_CONTEXT_KEEP_FIRST);
2836 while((opt = poptGetNextOpt(pc)) != -1) {
2837 switch (opt) {
2838 case OPT_CHALLENGE:
2839 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2840 if (opt_challenge.length != 8) {
2841 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2842 hex_challenge,
2843 (int)opt_challenge.length);
2844 exit(1);
2846 break;
2847 case OPT_LM:
2848 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2849 if (opt_lm_response.length != 24) {
2850 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2851 hex_lm_response,
2852 (int)opt_lm_response.length);
2853 exit(1);
2855 break;
2857 case OPT_NT:
2858 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2859 if (opt_nt_response.length < 24) {
2860 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2861 hex_nt_response,
2862 (int)opt_nt_response.length);
2863 exit(1);
2865 break;
2867 case OPT_REQUIRE_MEMBERSHIP:
2868 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2869 require_membership_of_sid = require_membership_of;
2871 break;
2875 if (opt_username) {
2876 char *domain = SMB_STRDUP(opt_username);
2877 char *p = strchr_m(domain, *lp_winbind_separator());
2878 if (p) {
2879 opt_username = p+1;
2880 *p = '\0';
2881 if (opt_domain && !strequal(opt_domain, domain)) {
2882 x_fprintf(x_stderr, "Domain specified in username (%s) "
2883 "doesn't match specified domain (%s)!\n\n",
2884 domain, opt_domain);
2885 poptPrintHelp(pc, stderr, 0);
2886 exit(1);
2888 opt_domain = domain;
2889 } else {
2890 SAFE_FREE(domain);
2894 /* Note: if opt_domain is "" then send no domain */
2895 if (opt_domain == NULL) {
2896 opt_domain = get_winbind_domain();
2899 if (opt_workstation == NULL) {
2900 opt_workstation = "";
2903 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_context());
2904 if (lp_ctx == NULL) {
2905 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2906 exit(1);
2909 if (helper_protocol) {
2910 int i;
2911 for (i=0; i<NUM_HELPER_MODES; i++) {
2912 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2913 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2914 exit(0);
2917 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2919 for (i=0; i<NUM_HELPER_MODES; i++) {
2920 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2923 exit(1);
2926 if (!opt_username || !*opt_username) {
2927 x_fprintf(x_stderr, "username must be specified!\n\n");
2928 poptPrintHelp(pc, stderr, 0);
2929 exit(1);
2932 if (opt_challenge.length) {
2933 if (!check_auth_crap()) {
2934 exit(1);
2936 exit(0);
2939 if (!opt_password) {
2940 opt_password = getpass("password: ");
2943 if (diagnostics) {
2944 if (!diagnose_ntlm_auth()) {
2945 return 1;
2947 } else {
2948 fstring user;
2950 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2951 if (!check_plaintext_auth(user, opt_password, True)) {
2952 return 1;
2956 /* Exit code */
2958 poptFreeContext(pc);
2959 TALLOC_FREE(frame);
2960 return 0;