s3: ntlm_auth: Don't corrupt the output stream with debug messages.
[Samba.git] / source3 / utils / ntlm_auth.c
blob653be45aa3c8b193558e3fd833ef46660b162501
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 "libcli/security/security.h"
31 #include "utils/ntlm_auth.h"
32 #include "../libcli/auth/libcli_auth.h"
33 #include "auth/ntlmssp/ntlmssp.h"
34 #include "auth/gensec/gensec.h"
35 #include "auth/gensec/gensec_internal.h"
36 #include "auth/credentials/credentials.h"
37 #include "librpc/crypto/gse.h"
38 #include "smb_krb5.h"
39 #include "lib/util/tiniparser.h"
40 #include "../lib/crypto/arcfour.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"
48 #include "lib/param/loadparm.h"
50 #if HAVE_KRB5
51 #include "auth/kerberos/pac_utils.h"
52 #endif
54 #ifndef PAM_WINBIND_CONFIG_FILE
55 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
56 #endif
58 #define WINBIND_KRB5_AUTH 0x00000080
60 #undef DBGC_CLASS
61 #define DBGC_CLASS DBGC_WINBIND
63 #define INITIAL_BUFFER_SIZE 300
64 #define MAX_BUFFER_SIZE 630000
66 enum stdio_helper_mode {
67 SQUID_2_4_BASIC,
68 SQUID_2_5_BASIC,
69 SQUID_2_5_NTLMSSP,
70 NTLMSSP_CLIENT_1,
71 GSS_SPNEGO_SERVER,
72 GSS_SPNEGO_CLIENT,
73 NTLM_SERVER_1,
74 NTLM_CHANGE_PASSWORD_1,
75 NUM_HELPER_MODES
78 enum ntlm_auth_cli_state {
79 CLIENT_INITIAL = 0,
80 CLIENT_RESPONSE,
81 CLIENT_FINISHED,
82 CLIENT_ERROR
85 struct ntlm_auth_state {
86 TALLOC_CTX *mem_ctx;
87 enum stdio_helper_mode helper_mode;
88 enum ntlm_auth_cli_state cli_state;
89 struct ntlmssp_state *ntlmssp_state;
90 uint32_t neg_flags;
91 char *want_feature_list;
92 bool have_session_key;
93 DATA_BLOB session_key;
94 DATA_BLOB initial_message;
95 void *gensec_private_1;
97 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
98 struct loadparm_context *lp_ctx,
99 struct ntlm_auth_state *state, char *buf,
100 int length, void **private2);
102 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
103 struct loadparm_context *lp_ctx,
104 char *buf, int length, void **private1);
106 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
107 struct loadparm_context *lp_ctx,
108 struct ntlm_auth_state *state,
109 stdio_helper_function fn, void **private2);
111 static void manage_squid_basic_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_squid_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_client_ntlmssp_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_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_gss_spnego_client_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_server_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 void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
142 struct loadparm_context *lp_ctx,
143 struct ntlm_auth_state *state,
144 char *buf, int length, void **private2);
146 static const struct {
147 enum stdio_helper_mode mode;
148 const char *name;
149 stdio_helper_function fn;
150 } stdio_helper_protocols[] = {
151 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
152 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
153 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
154 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
155 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
156 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
157 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
158 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
159 { NUM_HELPER_MODES, NULL, NULL}
162 const char *opt_username;
163 const char *opt_domain;
164 const char *opt_workstation;
165 const char *opt_password;
166 static DATA_BLOB opt_challenge;
167 static DATA_BLOB opt_lm_response;
168 static DATA_BLOB opt_nt_response;
169 static int request_lm_key;
170 static int request_user_session_key;
171 static int use_cached_creds;
172 static int offline_logon;
174 static const char *require_membership_of;
175 static const char *require_membership_of_sid;
176 static const char *opt_pam_winbind_conf;
178 const char *opt_target_service;
179 const char *opt_target_hostname;
182 /* This is a bit hairy, but the basic idea is to do a password callback
183 to the calling application. The callback comes from within gensec */
185 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
186 struct loadparm_context *lp_ctx,
187 struct ntlm_auth_state *state, char *buf, int length,
188 void **password)
190 DATA_BLOB in;
191 if (strlen(buf) < 2) {
192 DEBUG(1, ("query [%s] invalid", buf));
193 x_fprintf(x_stdout, "BH Query invalid\n");
194 return;
197 if (strlen(buf) > 3) {
198 in = base64_decode_data_blob(buf + 3);
199 } else {
200 in = data_blob(NULL, 0);
203 if (strncmp(buf, "PW ", 3) == 0) {
205 *password = talloc_strndup(NULL,
206 (const char *)in.data, in.length);
208 if (*password == NULL) {
209 DEBUG(1, ("Out of memory\n"));
210 x_fprintf(x_stdout, "BH Out of memory\n");
211 data_blob_free(&in);
212 return;
215 x_fprintf(x_stdout, "OK\n");
216 data_blob_free(&in);
217 return;
219 DEBUG(1, ("Asked for (and expected) a password\n"));
220 x_fprintf(x_stdout, "BH Expected a password\n");
221 data_blob_free(&in);
225 * Callback for password credentials. This is not async, and when
226 * GENSEC and the credentials code is made async, it will look rather
227 * different.
230 static const char *get_password(struct cli_credentials *credentials)
232 TALLOC_CTX *frame = talloc_stackframe();
233 char *password = NULL;
234 struct ntlm_auth_state *state;
236 state = talloc_zero(frame, struct ntlm_auth_state);
237 if (state == NULL) {
238 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
239 x_fprintf(x_stderr, "ERR\n");
240 exit(1);
243 state->mem_ctx = state;
245 /* Ask for a password */
246 x_fprintf(x_stdout, "PW\n");
248 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, state, manage_gensec_get_pw_request, (void **)&password);
249 talloc_steal(credentials, password);
250 TALLOC_FREE(frame);
251 return password;
255 * A limited set of features are defined with text strings as needed
256 * by ntlm_auth
259 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
261 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
262 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
263 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
265 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
266 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
267 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
269 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
270 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
271 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
273 if (in_list("NTLMSSP_FEATURE_CCACHE", feature_list, true)) {
274 DEBUG(10, ("want GENSEC_FEATURE_NTLM_CCACHE\n"));
275 gensec_want_feature(state, GENSEC_FEATURE_NTLM_CCACHE);
279 static char winbind_separator(void)
281 struct winbindd_response response;
282 static bool got_sep;
283 static char sep;
285 if (got_sep)
286 return sep;
288 ZERO_STRUCT(response);
290 /* Send off request */
292 if (winbindd_request_response(NULL, WINBINDD_INFO, NULL, &response) !=
293 NSS_STATUS_SUCCESS) {
294 d_fprintf(stderr, "could not obtain winbind separator!\n");
295 return *lp_winbind_separator();
298 sep = response.data.info.winbind_separator;
299 got_sep = True;
301 if (!sep) {
302 d_fprintf(stderr, "winbind separator was NULL!\n");
303 return *lp_winbind_separator();
306 return sep;
309 const char *get_winbind_domain(void)
311 struct winbindd_response response;
313 static fstring winbind_domain;
314 if (*winbind_domain) {
315 return winbind_domain;
318 ZERO_STRUCT(response);
320 /* Send off request */
322 if (winbindd_request_response(NULL, WINBINDD_DOMAIN_NAME, NULL, &response) !=
323 NSS_STATUS_SUCCESS) {
324 DEBUG(1, ("could not obtain winbind domain name!\n"));
325 return lp_workgroup();
328 fstrcpy(winbind_domain, response.data.domain_name);
330 return winbind_domain;
334 const char *get_winbind_netbios_name(void)
336 struct winbindd_response response;
338 static fstring winbind_netbios_name;
340 if (*winbind_netbios_name) {
341 return winbind_netbios_name;
344 ZERO_STRUCT(response);
346 /* Send off request */
348 if (winbindd_request_response(NULL, WINBINDD_NETBIOS_NAME, NULL, &response) !=
349 NSS_STATUS_SUCCESS) {
350 DEBUG(1, ("could not obtain winbind netbios name!\n"));
351 return lp_netbios_name();
354 fstrcpy(winbind_netbios_name, response.data.netbios_name);
356 return winbind_netbios_name;
360 DATA_BLOB get_challenge(void)
362 static DATA_BLOB chal;
363 if (opt_challenge.length)
364 return opt_challenge;
366 chal = data_blob(NULL, 8);
368 generate_random_buffer(chal.data, chal.length);
369 return chal;
372 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
373 form DOMAIN/user into a domain and a user */
375 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
376 fstring user)
379 char *p = strchr(domuser,winbind_separator());
381 if (!p) {
382 return False;
385 fstrcpy(user, p+1);
386 fstrcpy(domain, domuser);
387 domain[PTR_DIFF(p, domuser)] = 0;
388 return strupper_m(domain);
391 static bool get_require_membership_sid(void) {
392 struct winbindd_request request;
393 struct winbindd_response response;
395 if (!require_membership_of) {
396 return True;
399 if (require_membership_of_sid) {
400 return True;
403 /* Otherwise, ask winbindd for the name->sid request */
405 ZERO_STRUCT(request);
406 ZERO_STRUCT(response);
408 if (!parse_ntlm_auth_domain_user(require_membership_of,
409 request.data.name.dom_name,
410 request.data.name.name)) {
411 DEBUG(0, ("Could not parse %s into separate domain/name parts!\n",
412 require_membership_of));
413 return False;
416 if (winbindd_request_response(NULL, WINBINDD_LOOKUPNAME, &request, &response) !=
417 NSS_STATUS_SUCCESS) {
418 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
419 require_membership_of));
420 return False;
423 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
425 if (require_membership_of_sid)
426 return True;
428 return False;
432 * Get some configuration from pam_winbind.conf to see if we
433 * need to contact trusted domain
436 int get_pam_winbind_config()
438 int ctrl = 0;
439 struct tiniparser_dictionary *d = NULL;
441 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
442 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
445 d = tiniparser_load(opt_pam_winbind_conf);
447 if (!d) {
448 return 0;
451 if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
452 ctrl |= WINBIND_KRB5_AUTH;
455 tiniparser_freedict(d);
457 return ctrl;
460 /* Authenticate a user with a plaintext password */
462 static bool check_plaintext_auth(const char *user, const char *pass,
463 bool stdout_diagnostics)
465 struct winbindd_request request;
466 struct winbindd_response response;
467 NSS_STATUS result;
469 if (!get_require_membership_sid()) {
470 return False;
473 /* Send off request */
475 ZERO_STRUCT(request);
476 ZERO_STRUCT(response);
478 fstrcpy(request.data.auth.user, user);
479 fstrcpy(request.data.auth.pass, pass);
480 if (require_membership_of_sid) {
481 strlcpy(request.data.auth.require_membership_of_sid,
482 require_membership_of_sid,
483 sizeof(request.data.auth.require_membership_of_sid));
486 if (offline_logon) {
487 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
490 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH, &request, &response);
492 /* Display response */
494 if (stdout_diagnostics) {
495 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
496 d_fprintf(stderr, "Reading winbind reply failed! (0x01)\n");
499 d_printf("%s: %s (0x%x)\n",
500 response.data.auth.nt_status_string,
501 response.data.auth.error_string,
502 response.data.auth.nt_status);
503 } else {
504 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
505 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
508 DEBUG(3, ("%s: %s (0x%x)\n",
509 response.data.auth.nt_status_string,
510 response.data.auth.error_string,
511 response.data.auth.nt_status));
514 return (result == NSS_STATUS_SUCCESS);
517 /* authenticate a user with an encrypted username/password */
519 NTSTATUS contact_winbind_auth_crap(const char *username,
520 const char *domain,
521 const char *workstation,
522 const DATA_BLOB *challenge,
523 const DATA_BLOB *lm_response,
524 const DATA_BLOB *nt_response,
525 uint32_t flags,
526 uint32_t extra_logon_parameters,
527 uint8_t lm_key[8],
528 uint8_t user_session_key[16],
529 char **error_string,
530 char **unix_name)
532 NTSTATUS nt_status;
533 NSS_STATUS result;
534 struct winbindd_request request;
535 struct winbindd_response response;
537 if (!get_require_membership_sid()) {
538 return NT_STATUS_INVALID_PARAMETER;
541 ZERO_STRUCT(request);
542 ZERO_STRUCT(response);
544 request.flags = flags;
546 request.data.auth_crap.logon_parameters = extra_logon_parameters
547 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
549 if (require_membership_of_sid)
550 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
552 fstrcpy(request.data.auth_crap.user, username);
553 fstrcpy(request.data.auth_crap.domain, domain);
555 fstrcpy(request.data.auth_crap.workstation,
556 workstation);
558 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
560 if (lm_response && lm_response->length) {
561 memcpy(request.data.auth_crap.lm_resp,
562 lm_response->data,
563 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
564 request.data.auth_crap.lm_resp_len = lm_response->length;
567 if (nt_response && nt_response->length) {
568 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
569 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
570 request.extra_len = nt_response->length;
571 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
572 if (request.extra_data.data == NULL) {
573 return NT_STATUS_NO_MEMORY;
575 memcpy(request.extra_data.data, nt_response->data,
576 nt_response->length);
578 } else {
579 memcpy(request.data.auth_crap.nt_resp,
580 nt_response->data, nt_response->length);
582 request.data.auth_crap.nt_resp_len = nt_response->length;
585 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH_CRAP, &request, &response);
586 SAFE_FREE(request.extra_data.data);
588 /* Display response */
590 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
591 nt_status = NT_STATUS_UNSUCCESSFUL;
592 if (error_string)
593 *error_string = smb_xstrdup("Reading winbind reply failed!");
594 winbindd_free_response(&response);
595 return nt_status;
598 nt_status = (NT_STATUS(response.data.auth.nt_status));
599 if (!NT_STATUS_IS_OK(nt_status)) {
600 if (error_string)
601 *error_string = smb_xstrdup(response.data.auth.error_string);
602 winbindd_free_response(&response);
603 return nt_status;
606 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
607 memcpy(lm_key, response.data.auth.first_8_lm_hash,
608 sizeof(response.data.auth.first_8_lm_hash));
610 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
611 memcpy(user_session_key, response.data.auth.user_session_key,
612 sizeof(response.data.auth.user_session_key));
615 if (flags & WBFLAG_PAM_UNIX_NAME) {
616 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
617 if (!*unix_name) {
618 winbindd_free_response(&response);
619 return NT_STATUS_NO_MEMORY;
623 winbindd_free_response(&response);
624 return nt_status;
627 /* contact server to change user password using auth crap */
628 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
629 const char *domain,
630 const DATA_BLOB new_nt_pswd,
631 const DATA_BLOB old_nt_hash_enc,
632 const DATA_BLOB new_lm_pswd,
633 const DATA_BLOB old_lm_hash_enc,
634 char **error_string)
636 NTSTATUS nt_status;
637 NSS_STATUS result;
638 struct winbindd_request request;
639 struct winbindd_response response;
641 if (!get_require_membership_sid())
643 if(error_string)
644 *error_string = smb_xstrdup("Can't get membership sid.");
645 return NT_STATUS_INVALID_PARAMETER;
648 ZERO_STRUCT(request);
649 ZERO_STRUCT(response);
651 if(username != NULL)
652 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
653 if(domain != NULL)
654 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
656 if(new_nt_pswd.length)
658 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
659 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
662 if(old_nt_hash_enc.length)
664 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));
665 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
668 if(new_lm_pswd.length)
670 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
671 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
674 if(old_lm_hash_enc.length)
676 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));
677 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
680 result = winbindd_request_response(NULL, WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
682 /* Display response */
684 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
686 nt_status = NT_STATUS_UNSUCCESSFUL;
687 if (error_string)
688 *error_string = smb_xstrdup("Reading winbind reply failed!");
689 winbindd_free_response(&response);
690 return nt_status;
693 nt_status = (NT_STATUS(response.data.auth.nt_status));
694 if (!NT_STATUS_IS_OK(nt_status))
696 if (error_string)
697 *error_string = smb_xstrdup(response.data.auth.error_string);
698 winbindd_free_response(&response);
699 return nt_status;
702 winbindd_free_response(&response);
704 return nt_status;
707 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
708 TALLOC_CTX *mem_ctx,
709 void *server_returned_info,
710 const char *original_user_name,
711 uint32_t session_info_flags,
712 struct auth_session_info **session_info_out)
714 const char *unix_username = (const char *)server_returned_info;
715 bool ok;
716 struct dom_sid *sids = NULL;
717 struct auth_session_info *session_info = NULL;
719 session_info = talloc_zero(mem_ctx, struct auth_session_info);
720 if (session_info == NULL) {
721 return NT_STATUS_NO_MEMORY;
724 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
725 if (session_info->unix_info == NULL) {
726 TALLOC_FREE(session_info);
727 return NT_STATUS_NO_MEMORY;
729 session_info->unix_info->unix_name = talloc_strdup(session_info->unix_info,
730 unix_username);
731 if (session_info->unix_info->unix_name == NULL) {
732 TALLOC_FREE(session_info);
733 return NT_STATUS_NO_MEMORY;
736 session_info->security_token = talloc_zero(session_info, struct security_token);
737 if (session_info->security_token == NULL) {
738 TALLOC_FREE(session_info);
739 return NT_STATUS_NO_MEMORY;
742 sids = talloc_zero_array(session_info->security_token,
743 struct dom_sid, 3);
744 if (sids == NULL) {
745 TALLOC_FREE(session_info);
746 return NT_STATUS_NO_MEMORY;
748 ok = dom_sid_parse(SID_WORLD, &sids[0]);
749 if (!ok) {
750 TALLOC_FREE(session_info);
751 return NT_STATUS_INTERNAL_ERROR;
753 ok = dom_sid_parse(SID_NT_NETWORK, &sids[1]);
754 if (!ok) {
755 TALLOC_FREE(session_info);
756 return NT_STATUS_INTERNAL_ERROR;
758 ok = dom_sid_parse(SID_NT_AUTHENTICATED_USERS, &sids[2]);
759 if (!ok) {
760 TALLOC_FREE(session_info);
761 return NT_STATUS_INTERNAL_ERROR;
764 session_info->security_token->num_sids = talloc_array_length(sids);
765 session_info->security_token->sids = sids;
767 *session_info_out = session_info;
769 return NT_STATUS_OK;
772 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
773 TALLOC_CTX *mem_ctx,
774 struct smb_krb5_context *smb_krb5_context,
775 DATA_BLOB *pac_blob,
776 const char *princ_name,
777 const struct tsocket_address *remote_address,
778 uint32_t session_info_flags,
779 struct auth_session_info **session_info)
781 TALLOC_CTX *tmp_ctx;
782 struct PAC_LOGON_INFO *logon_info = NULL;
783 char *unixuser;
784 NTSTATUS status;
785 char *domain = NULL;
786 char *realm = NULL;
787 char *user = NULL;
788 char *p;
790 tmp_ctx = talloc_new(mem_ctx);
791 if (!tmp_ctx) {
792 return NT_STATUS_NO_MEMORY;
795 if (pac_blob) {
796 #ifdef HAVE_KRB5
797 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
798 NULL, NULL, 0, &logon_info);
799 #else
800 status = NT_STATUS_ACCESS_DENIED;
801 #endif
802 if (!NT_STATUS_IS_OK(status)) {
803 goto done;
807 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
809 p = strchr_m(princ_name, '@');
810 if (!p) {
811 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
812 princ_name));
813 return NT_STATUS_LOGON_FAILURE;
816 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
817 if (!user) {
818 return NT_STATUS_NO_MEMORY;
821 realm = talloc_strdup(talloc_tos(), p + 1);
822 if (!realm) {
823 return NT_STATUS_NO_MEMORY;
826 if (!strequal(realm, lp_realm())) {
827 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
828 if (!lp_allow_trusted_domains()) {
829 return NT_STATUS_LOGON_FAILURE;
833 if (logon_info && logon_info->info3.base.logon_domain.string) {
834 domain = talloc_strdup(mem_ctx,
835 logon_info->info3.base.logon_domain.string);
836 if (!domain) {
837 return NT_STATUS_NO_MEMORY;
839 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
840 } else {
842 /* If we have winbind running, we can (and must) shorten the
843 username by using the short netbios name. Otherwise we will
844 have inconsistent user names. With Kerberos, we get the
845 fully qualified realm, with ntlmssp we get the short
846 name. And even w2k3 does use ntlmssp if you for example
847 connect to an ip address. */
849 wbcErr wbc_status;
850 struct wbcDomainInfo *info = NULL;
852 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
853 realm));
855 wbc_status = wbcDomainInfo(realm, &info);
857 if (WBC_ERROR_IS_OK(wbc_status)) {
858 domain = talloc_strdup(mem_ctx,
859 info->short_name);
860 wbcFreeMemory(info);
861 } else {
862 DEBUG(3, ("Could not find short name: %s\n",
863 wbcErrorString(wbc_status)));
864 domain = talloc_strdup(mem_ctx, realm);
866 if (!domain) {
867 return NT_STATUS_NO_MEMORY;
869 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
872 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
873 if (!unixuser) {
874 status = NT_STATUS_NO_MEMORY;
875 goto done;
878 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
880 done:
881 TALLOC_FREE(tmp_ctx);
882 return status;
888 * Return the challenge as determined by the authentication subsystem
889 * @return an 8 byte random challenge
892 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
893 uint8_t chal[8])
895 if (auth_ctx->challenge.data.length == 8) {
896 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
897 auth_ctx->challenge.set_by));
898 memcpy(chal, auth_ctx->challenge.data.data, 8);
899 return NT_STATUS_OK;
902 if (!auth_ctx->challenge.set_by) {
903 generate_random_buffer(chal, 8);
905 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
906 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
907 auth_ctx->challenge.set_by = "random";
910 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
911 auth_ctx->challenge.set_by));
913 return NT_STATUS_OK;
917 * NTLM2 authentication modifies the effective challenge,
918 * @param challenge The new challenge value
920 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
922 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
923 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
925 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
926 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
928 return NT_STATUS_OK;
932 * Check the password on an NTLMSSP login.
934 * Return the session keys used on the connection.
937 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
938 TALLOC_CTX *mem_ctx,
939 const struct auth_usersupplied_info *user_info,
940 void **server_returned_info,
941 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
943 static const char zeros[16] = { 0, };
944 NTSTATUS nt_status;
945 char *error_string = NULL;
946 uint8_t lm_key[8];
947 uint8_t user_sess_key[16];
948 char *unix_name = NULL;
950 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
951 user_info->workstation_name,
952 &auth4_context->challenge.data,
953 &user_info->password.response.lanman,
954 &user_info->password.response.nt,
955 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
957 lm_key, user_sess_key,
958 &error_string, &unix_name);
960 if (NT_STATUS_IS_OK(nt_status)) {
961 if (memcmp(lm_key, zeros, 8) != 0) {
962 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
963 memcpy(lm_session_key->data, lm_key, 8);
964 memset(lm_session_key->data+8, '\0', 8);
967 if (memcmp(user_sess_key, zeros, 16) != 0) {
968 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
970 *server_returned_info = talloc_strdup(mem_ctx,
971 unix_name);
972 } else {
973 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
974 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
975 user_info->client.domain_name, user_info->client.account_name,
976 user_info->workstation_name,
977 error_string ? error_string : "unknown error (NULL)"));
980 SAFE_FREE(error_string);
981 SAFE_FREE(unix_name);
982 return nt_status;
985 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
986 TALLOC_CTX *mem_ctx,
987 const struct auth_usersupplied_info *user_info,
988 void **server_returned_info,
989 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
991 NTSTATUS nt_status;
992 struct samr_Password lm_pw, nt_pw;
994 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
996 nt_status = ntlm_password_check(mem_ctx,
997 true, true, 0,
998 &auth4_context->challenge.data,
999 &user_info->password.response.lanman,
1000 &user_info->password.response.nt,
1001 user_info->client.account_name,
1002 user_info->client.account_name,
1003 user_info->client.domain_name,
1004 &lm_pw, &nt_pw, session_key, lm_session_key);
1006 if (NT_STATUS_IS_OK(nt_status)) {
1007 *server_returned_info = talloc_asprintf(mem_ctx,
1008 "%s%c%s", user_info->client.domain_name,
1009 *lp_winbind_separator(),
1010 user_info->client.account_name);
1011 } else {
1012 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
1013 user_info->client.domain_name, user_info->client.account_name,
1014 user_info->workstation_name,
1015 nt_errstr(nt_status)));
1017 return nt_status;
1020 static NTSTATUS ntlm_auth_prepare_gensec_client(TALLOC_CTX *mem_ctx,
1021 struct loadparm_context *lp_ctx,
1022 struct gensec_security **gensec_security_out)
1024 struct gensec_security *gensec_security = NULL;
1025 NTSTATUS nt_status;
1026 TALLOC_CTX *tmp_ctx;
1027 const struct gensec_security_ops **backends = NULL;
1028 struct gensec_settings *gensec_settings = NULL;
1029 size_t idx = 0;
1031 tmp_ctx = talloc_new(mem_ctx);
1032 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1034 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1035 if (gensec_settings == NULL) {
1036 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1037 TALLOC_FREE(tmp_ctx);
1038 return NT_STATUS_NO_MEMORY;
1041 backends = talloc_zero_array(gensec_settings,
1042 const struct gensec_security_ops *, 4);
1043 if (backends == NULL) {
1044 TALLOC_FREE(tmp_ctx);
1045 return NT_STATUS_NO_MEMORY;
1047 gensec_settings->backends = backends;
1049 gensec_init();
1051 /* These need to be in priority order, krb5 before NTLMSSP */
1052 #if defined(HAVE_KRB5)
1053 backends[idx++] = &gensec_gse_krb5_security_ops;
1054 #endif
1056 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1058 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1060 nt_status = gensec_client_start(NULL, &gensec_security,
1061 gensec_settings);
1062 if (!NT_STATUS_IS_OK(nt_status)) {
1063 TALLOC_FREE(tmp_ctx);
1064 return nt_status;
1067 talloc_unlink(tmp_ctx, gensec_settings);
1069 if (opt_target_service != NULL) {
1070 nt_status = gensec_set_target_service(gensec_security,
1071 opt_target_service);
1072 if (!NT_STATUS_IS_OK(nt_status)) {
1073 TALLOC_FREE(tmp_ctx);
1074 return nt_status;
1078 if (opt_target_hostname != NULL) {
1079 nt_status = gensec_set_target_hostname(gensec_security,
1080 opt_target_hostname);
1081 if (!NT_STATUS_IS_OK(nt_status)) {
1082 TALLOC_FREE(tmp_ctx);
1083 return nt_status;
1087 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1088 TALLOC_FREE(tmp_ctx);
1089 return NT_STATUS_OK;
1092 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1094 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1095 if (auth4_context == NULL) {
1096 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1097 return NULL;
1099 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1100 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1101 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1102 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1103 if (local_pw) {
1104 auth4_context->check_ntlm_password = local_pw_check;
1105 } else {
1106 auth4_context->check_ntlm_password = winbind_pw_check;
1108 auth4_context->private_data = NULL;
1109 return auth4_context;
1112 static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx,
1113 struct loadparm_context *lp_ctx,
1114 struct gensec_security **gensec_security_out)
1116 struct gensec_security *gensec_security;
1117 NTSTATUS nt_status;
1119 TALLOC_CTX *tmp_ctx;
1120 const struct gensec_security_ops **backends;
1121 struct gensec_settings *gensec_settings;
1122 size_t idx = 0;
1123 struct cli_credentials *server_credentials;
1125 struct auth4_context *auth4_context;
1127 tmp_ctx = talloc_new(mem_ctx);
1128 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1130 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1131 if (auth4_context == NULL) {
1132 TALLOC_FREE(tmp_ctx);
1133 return NT_STATUS_NO_MEMORY;
1136 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1137 if (lp_ctx == NULL) {
1138 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1139 TALLOC_FREE(tmp_ctx);
1140 return NT_STATUS_NO_MEMORY;
1144 * This should be a 'netbios domain -> DNS domain'
1145 * mapping, and can currently validly return NULL on
1146 * poorly configured systems.
1148 * This is used for the NTLMSSP server
1151 if (opt_password) {
1152 gensec_settings->server_netbios_name = lp_netbios_name();
1153 gensec_settings->server_netbios_domain = lp_workgroup();
1154 } else {
1155 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1156 gensec_settings->server_netbios_domain = get_winbind_domain();
1159 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1160 get_mydnsdomname(talloc_tos()));
1161 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1162 get_mydnsfullname());
1164 backends = talloc_zero_array(gensec_settings,
1165 const struct gensec_security_ops *, 4);
1167 if (backends == NULL) {
1168 TALLOC_FREE(tmp_ctx);
1169 return NT_STATUS_NO_MEMORY;
1171 gensec_settings->backends = backends;
1173 gensec_init();
1175 /* These need to be in priority order, krb5 before NTLMSSP */
1176 #if defined(HAVE_KRB5)
1177 backends[idx++] = &gensec_gse_krb5_security_ops;
1178 #endif
1180 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1182 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1185 * This is anonymous for now, because we just use it
1186 * to set the kerberos state at the moment
1188 server_credentials = cli_credentials_init_anon(tmp_ctx);
1189 if (!server_credentials) {
1190 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1191 return NT_STATUS_NO_MEMORY;
1194 cli_credentials_set_conf(server_credentials, lp_ctx);
1196 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1197 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1198 } else {
1199 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1202 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1203 auth4_context, &gensec_security);
1205 if (!NT_STATUS_IS_OK(nt_status)) {
1206 TALLOC_FREE(tmp_ctx);
1207 return nt_status;
1210 gensec_set_credentials(gensec_security, server_credentials);
1212 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
1213 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
1215 talloc_unlink(tmp_ctx, lp_ctx);
1216 talloc_unlink(tmp_ctx, server_credentials);
1217 talloc_unlink(tmp_ctx, gensec_settings);
1218 talloc_unlink(tmp_ctx, auth4_context);
1220 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1221 TALLOC_FREE(tmp_ctx);
1222 return NT_STATUS_OK;
1225 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1226 struct loadparm_context *lp_ctx,
1227 struct ntlm_auth_state *state,
1228 char *buf, int length, void **private2)
1230 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1231 return;
1234 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1235 struct loadparm_context *lp_ctx,
1236 struct ntlm_auth_state *state,
1237 char *buf, int length, void **private2)
1239 char *user, *pass;
1240 user=buf;
1242 pass=(char *)memchr(buf,' ',length);
1243 if (!pass) {
1244 DEBUG(2, ("Password not found. Denying access\n"));
1245 x_fprintf(x_stdout, "ERR\n");
1246 return;
1248 *pass='\0';
1249 pass++;
1251 if (state->helper_mode == SQUID_2_5_BASIC) {
1252 rfc1738_unescape(user);
1253 rfc1738_unescape(pass);
1256 if (check_plaintext_auth(user, pass, False)) {
1257 x_fprintf(x_stdout, "OK\n");
1258 } else {
1259 x_fprintf(x_stdout, "ERR\n");
1263 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1264 struct loadparm_context *lp_ctx,
1265 char *buf, int length, void **private1)
1267 DATA_BLOB in;
1268 DATA_BLOB out = data_blob(NULL, 0);
1269 char *out_base64 = NULL;
1270 const char *reply_arg = NULL;
1271 struct gensec_ntlm_state {
1272 struct gensec_security *gensec_state;
1273 const char *set_password;
1275 struct gensec_ntlm_state *state;
1277 NTSTATUS nt_status;
1278 bool first = false;
1279 const char *reply_code;
1280 struct cli_credentials *creds;
1282 static char *want_feature_list = NULL;
1283 static DATA_BLOB session_key;
1285 TALLOC_CTX *mem_ctx;
1287 if (*private1) {
1288 state = (struct gensec_ntlm_state *)*private1;
1289 } else {
1290 state = talloc_zero(NULL, struct gensec_ntlm_state);
1291 if (!state) {
1292 x_fprintf(x_stdout, "BH No Memory\n");
1293 exit(1);
1295 *private1 = state;
1296 if (opt_password) {
1297 state->set_password = opt_password;
1301 if (strlen(buf) < 2) {
1302 DEBUG(1, ("query [%s] invalid", buf));
1303 x_fprintf(x_stdout, "BH Query invalid\n");
1304 return;
1307 if (strlen(buf) > 3) {
1308 if(strncmp(buf, "SF ", 3) == 0) {
1309 DEBUG(10, ("Setting flags to negotiate\n"));
1310 talloc_free(want_feature_list);
1311 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1312 x_fprintf(x_stdout, "OK\n");
1313 return;
1315 in = base64_decode_data_blob(buf + 3);
1316 } else {
1317 in = data_blob(NULL, 0);
1320 if (strncmp(buf, "YR", 2) == 0) {
1321 if (state->gensec_state) {
1322 talloc_free(state->gensec_state);
1323 state->gensec_state = NULL;
1325 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1326 /* Just return BH, like ntlm_auth from Samba 3 does. */
1327 x_fprintf(x_stdout, "BH Command expected\n");
1328 data_blob_free(&in);
1329 return;
1330 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1331 (strncmp(buf, "KK ", 3) != 0) &&
1332 (strncmp(buf, "AF ", 3) != 0) &&
1333 (strncmp(buf, "NA ", 3) != 0) &&
1334 (strncmp(buf, "UG", 2) != 0) &&
1335 (strncmp(buf, "PW ", 3) != 0) &&
1336 (strncmp(buf, "GK", 2) != 0) &&
1337 (strncmp(buf, "GF", 2) != 0)) {
1338 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1339 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1340 data_blob_free(&in);
1341 return;
1344 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1346 /* setup gensec */
1347 if (!(state->gensec_state)) {
1348 switch (stdio_helper_mode) {
1349 case GSS_SPNEGO_CLIENT:
1351 * cached credentials are only supported by
1352 * NTLMSSP_CLIENT_1 for now.
1354 use_cached_creds = false;
1355 /* fall through */
1356 case NTLMSSP_CLIENT_1:
1357 /* setup the client side */
1359 if (state->set_password != NULL) {
1360 use_cached_creds = false;
1363 if (use_cached_creds) {
1364 struct wbcCredentialCacheParams params;
1365 struct wbcCredentialCacheInfo *info = NULL;
1366 struct wbcAuthErrorInfo *error = NULL;
1367 wbcErr wbc_status;
1369 params.account_name = opt_username;
1370 params.domain_name = opt_domain;
1371 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1372 params.num_blobs = 0;
1373 params.blobs = NULL;
1375 wbc_status = wbcCredentialCache(&params, &info,
1376 &error);
1377 wbcFreeMemory(error);
1378 if (!WBC_ERROR_IS_OK(wbc_status)) {
1379 use_cached_creds = false;
1381 wbcFreeMemory(info);
1384 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1385 &state->gensec_state);
1386 if (!NT_STATUS_IS_OK(nt_status)) {
1387 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1388 talloc_free(mem_ctx);
1389 return;
1392 creds = cli_credentials_init(state->gensec_state);
1393 cli_credentials_set_conf(creds, lp_ctx);
1394 if (opt_username) {
1395 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1397 if (opt_domain) {
1398 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1400 if (use_cached_creds) {
1401 gensec_want_feature(state->gensec_state,
1402 GENSEC_FEATURE_NTLM_CCACHE);
1403 } else if (state->set_password) {
1404 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1405 } else {
1406 cli_credentials_set_password_callback(creds, get_password);
1408 if (opt_workstation) {
1409 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1412 gensec_set_credentials(state->gensec_state, creds);
1414 break;
1415 case GSS_SPNEGO_SERVER:
1416 case SQUID_2_5_NTLMSSP:
1418 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1419 &state->gensec_state);
1420 if (!NT_STATUS_IS_OK(nt_status)) {
1421 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1422 talloc_free(mem_ctx);
1423 return;
1425 break;
1427 default:
1428 talloc_free(mem_ctx);
1429 abort();
1432 gensec_want_feature_list(state->gensec_state, want_feature_list);
1434 switch (stdio_helper_mode) {
1435 case GSS_SPNEGO_CLIENT:
1436 case GSS_SPNEGO_SERVER:
1437 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1438 if (!in.length) {
1439 first = true;
1441 break;
1442 case NTLMSSP_CLIENT_1:
1443 if (!in.length) {
1444 first = true;
1446 /* fall through */
1447 case SQUID_2_5_NTLMSSP:
1448 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1449 break;
1450 default:
1451 talloc_free(mem_ctx);
1452 abort();
1455 if (!NT_STATUS_IS_OK(nt_status)) {
1456 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1457 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1458 talloc_free(mem_ctx);
1459 return;
1464 /* update */
1466 if (strncmp(buf, "PW ", 3) == 0) {
1467 state->set_password = talloc_strndup(state,
1468 (const char *)in.data,
1469 in.length);
1471 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1472 state->set_password,
1473 CRED_SPECIFIED);
1474 x_fprintf(x_stdout, "OK\n");
1475 data_blob_free(&in);
1476 talloc_free(mem_ctx);
1477 return;
1480 if (strncmp(buf, "GK", 2) == 0) {
1481 char *base64_key;
1482 DEBUG(10, ("Requested session key\n"));
1483 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1484 if(!NT_STATUS_IS_OK(nt_status)) {
1485 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1486 x_fprintf(x_stdout, "BH No session key\n");
1487 talloc_free(mem_ctx);
1488 return;
1489 } else {
1490 base64_key = base64_encode_data_blob(state, session_key);
1491 x_fprintf(x_stdout, "GK %s\n", base64_key);
1492 talloc_free(base64_key);
1494 talloc_free(mem_ctx);
1495 return;
1498 if (strncmp(buf, "GF", 2) == 0) {
1499 uint32_t neg_flags;
1501 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1503 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1504 if (neg_flags == 0) {
1505 x_fprintf(x_stdout, "BH\n");
1506 return;
1509 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1510 return;
1513 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1515 /* don't leak 'bad password'/'no such user' info to the network client */
1516 nt_status = nt_status_squash(nt_status);
1518 if (out.length) {
1519 out_base64 = base64_encode_data_blob(mem_ctx, out);
1520 } else {
1521 out_base64 = NULL;
1524 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1525 reply_arg = "*";
1526 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1527 reply_code = "YR";
1528 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1529 reply_code = "KK";
1530 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1531 reply_code = "TT";
1532 } else {
1533 abort();
1537 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1538 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1539 reply_arg = nt_errstr(nt_status);
1540 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1541 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1542 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1543 reply_arg = nt_errstr(nt_status);
1544 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1545 } else if (!NT_STATUS_IS_OK(nt_status)) {
1546 reply_code = "NA";
1547 reply_arg = nt_errstr(nt_status);
1548 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1549 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1550 struct auth_session_info *session_info;
1552 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1553 if (!NT_STATUS_IS_OK(nt_status)) {
1554 reply_code = "BH Failed to retrive session info";
1555 reply_arg = nt_errstr(nt_status);
1556 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1557 } else {
1559 reply_code = "AF";
1560 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1561 if (reply_arg == NULL) {
1562 reply_code = "BH out of memory";
1563 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1565 talloc_free(session_info);
1567 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1568 reply_code = "AF";
1569 reply_arg = out_base64;
1570 } else {
1571 abort();
1574 switch (stdio_helper_mode) {
1575 case GSS_SPNEGO_SERVER:
1576 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1577 out_base64 ? out_base64 : "*",
1578 reply_arg ? reply_arg : "*");
1579 break;
1580 default:
1581 if (out_base64) {
1582 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1583 } else if (reply_arg) {
1584 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1585 } else {
1586 x_fprintf(x_stdout, "%s\n", reply_code);
1590 talloc_free(mem_ctx);
1591 return;
1594 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1595 struct loadparm_context *lp_ctx,
1596 struct ntlm_auth_state *state,
1597 char *buf, int length, void **private2)
1599 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1600 return;
1603 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1604 struct loadparm_context *lp_ctx,
1605 struct ntlm_auth_state *state,
1606 char *buf, int length, void **private2)
1608 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1609 return;
1612 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1613 struct loadparm_context *lp_ctx,
1614 struct ntlm_auth_state *state,
1615 char *buf, int length, void **private2)
1617 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1618 return;
1621 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1622 struct loadparm_context *lp_ctx,
1623 struct ntlm_auth_state *state,
1624 char *buf, int length, void **private2)
1626 char *request, *parameter;
1627 static DATA_BLOB challenge;
1628 static DATA_BLOB lm_response;
1629 static DATA_BLOB nt_response;
1630 static char *full_username;
1631 static char *username;
1632 static char *domain;
1633 static char *plaintext_password;
1634 static bool ntlm_server_1_user_session_key;
1635 static bool ntlm_server_1_lm_session_key;
1637 if (strequal(buf, ".")) {
1638 if (!full_username && !username) {
1639 x_fprintf(x_stdout, "Error: No username supplied!\n");
1640 } else if (plaintext_password) {
1641 /* handle this request as plaintext */
1642 if (!full_username) {
1643 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1644 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1645 return;
1648 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1649 x_fprintf(x_stdout, "Authenticated: Yes\n");
1650 } else {
1651 x_fprintf(x_stdout, "Authenticated: No\n");
1653 } else if (!lm_response.data && !nt_response.data) {
1654 x_fprintf(x_stdout, "Error: No password supplied!\n");
1655 } else if (!challenge.data) {
1656 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
1657 } else {
1658 char *error_string = NULL;
1659 uchar lm_key[8];
1660 uchar user_session_key[16];
1661 uint32_t flags = 0;
1662 NTSTATUS nt_status;
1663 if (full_username && !username) {
1664 fstring fstr_user;
1665 fstring fstr_domain;
1667 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1668 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1669 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
1671 SAFE_FREE(username);
1672 SAFE_FREE(domain);
1673 username = smb_xstrdup(fstr_user);
1674 domain = smb_xstrdup(fstr_domain);
1677 if (opt_password) {
1678 DATA_BLOB nt_session_key, lm_session_key;
1679 struct samr_Password lm_pw, nt_pw;
1680 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1681 ZERO_STRUCT(user_session_key);
1682 ZERO_STRUCT(lm_key);
1684 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1685 nt_status = ntlm_password_check(mem_ctx,
1686 true, true, 0,
1687 &challenge,
1688 &lm_response,
1689 &nt_response,
1690 username,
1691 username,
1692 domain,
1693 &lm_pw, &nt_pw,
1694 &nt_session_key,
1695 &lm_session_key);
1696 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1697 if (ntlm_server_1_user_session_key) {
1698 if (nt_session_key.length == sizeof(user_session_key)) {
1699 memcpy(user_session_key,
1700 nt_session_key.data,
1701 sizeof(user_session_key));
1704 if (ntlm_server_1_lm_session_key) {
1705 if (lm_session_key.length == sizeof(lm_key)) {
1706 memcpy(lm_key,
1707 lm_session_key.data,
1708 sizeof(lm_key));
1711 TALLOC_FREE(mem_ctx);
1713 } else {
1714 if (!domain) {
1715 domain = smb_xstrdup(get_winbind_domain());
1718 if (ntlm_server_1_lm_session_key)
1719 flags |= WBFLAG_PAM_LMKEY;
1721 if (ntlm_server_1_user_session_key)
1722 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1724 nt_status = contact_winbind_auth_crap(username,
1725 domain,
1726 lp_netbios_name(),
1727 &challenge,
1728 &lm_response,
1729 &nt_response,
1730 flags, 0,
1731 lm_key,
1732 user_session_key,
1733 &error_string,
1734 NULL);
1737 if (!NT_STATUS_IS_OK(nt_status)) {
1738 x_fprintf(x_stdout, "Authenticated: No\n");
1739 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
1740 } else {
1741 static char zeros[16];
1742 char *hex_lm_key;
1743 char *hex_user_session_key;
1745 x_fprintf(x_stdout, "Authenticated: Yes\n");
1747 if (ntlm_server_1_lm_session_key
1748 && (memcmp(zeros, lm_key,
1749 sizeof(lm_key)) != 0)) {
1750 hex_lm_key = hex_encode_talloc(NULL,
1751 (const unsigned char *)lm_key,
1752 sizeof(lm_key));
1753 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
1754 TALLOC_FREE(hex_lm_key);
1757 if (ntlm_server_1_user_session_key
1758 && (memcmp(zeros, user_session_key,
1759 sizeof(user_session_key)) != 0)) {
1760 hex_user_session_key = hex_encode_talloc(NULL,
1761 (const unsigned char *)user_session_key,
1762 sizeof(user_session_key));
1763 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
1764 TALLOC_FREE(hex_user_session_key);
1767 SAFE_FREE(error_string);
1769 /* clear out the state */
1770 challenge = data_blob_null;
1771 nt_response = data_blob_null;
1772 lm_response = data_blob_null;
1773 SAFE_FREE(full_username);
1774 SAFE_FREE(username);
1775 SAFE_FREE(domain);
1776 SAFE_FREE(plaintext_password);
1777 ntlm_server_1_user_session_key = False;
1778 ntlm_server_1_lm_session_key = False;
1779 x_fprintf(x_stdout, ".\n");
1781 return;
1784 request = buf;
1786 /* Indicates a base64 encoded structure */
1787 parameter = strstr_m(request, ":: ");
1788 if (!parameter) {
1789 parameter = strstr_m(request, ": ");
1791 if (!parameter) {
1792 DEBUG(0, ("Parameter not found!\n"));
1793 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1794 return;
1797 parameter[0] ='\0';
1798 parameter++;
1799 parameter[0] ='\0';
1800 parameter++;
1802 } else {
1803 parameter[0] ='\0';
1804 parameter++;
1805 parameter[0] ='\0';
1806 parameter++;
1807 parameter[0] ='\0';
1808 parameter++;
1810 base64_decode_inplace(parameter);
1813 if (strequal(request, "LANMAN-Challenge")) {
1814 challenge = strhex_to_data_blob(NULL, parameter);
1815 if (challenge.length != 8) {
1816 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
1817 parameter,
1818 (int)challenge.length);
1819 challenge = data_blob_null;
1821 } else if (strequal(request, "NT-Response")) {
1822 nt_response = strhex_to_data_blob(NULL, parameter);
1823 if (nt_response.length < 24) {
1824 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
1825 parameter,
1826 (int)nt_response.length);
1827 nt_response = data_blob_null;
1829 } else if (strequal(request, "LANMAN-Response")) {
1830 lm_response = strhex_to_data_blob(NULL, parameter);
1831 if (lm_response.length != 24) {
1832 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
1833 parameter,
1834 (int)lm_response.length);
1835 lm_response = data_blob_null;
1837 } else if (strequal(request, "Password")) {
1838 plaintext_password = smb_xstrdup(parameter);
1839 } else if (strequal(request, "NT-Domain")) {
1840 domain = smb_xstrdup(parameter);
1841 } else if (strequal(request, "Username")) {
1842 username = smb_xstrdup(parameter);
1843 } else if (strequal(request, "Full-Username")) {
1844 full_username = smb_xstrdup(parameter);
1845 } else if (strequal(request, "Request-User-Session-Key")) {
1846 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1847 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1848 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1849 } else {
1850 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
1854 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
1855 struct loadparm_context *lp_ctx,
1856 struct ntlm_auth_state *state,
1857 char *buf, int length, void **private2)
1859 char *request, *parameter;
1860 static DATA_BLOB new_nt_pswd;
1861 static DATA_BLOB old_nt_hash_enc;
1862 static DATA_BLOB new_lm_pswd;
1863 static DATA_BLOB old_lm_hash_enc;
1864 static char *full_username = NULL;
1865 static char *username = NULL;
1866 static char *domain = NULL;
1867 static char *newpswd = NULL;
1868 static char *oldpswd = NULL;
1870 if (strequal(buf, ".")) {
1871 if(newpswd && oldpswd) {
1872 uchar old_nt_hash[16];
1873 uchar old_lm_hash[16];
1874 uchar new_nt_hash[16];
1875 uchar new_lm_hash[16];
1877 new_nt_pswd = data_blob(NULL, 516);
1878 old_nt_hash_enc = data_blob(NULL, 16);
1880 /* Calculate the MD4 hash (NT compatible) of the
1881 * password */
1882 E_md4hash(oldpswd, old_nt_hash);
1883 E_md4hash(newpswd, new_nt_hash);
1885 /* E_deshash returns false for 'long'
1886 passwords (> 14 DOS chars).
1888 Therefore, don't send a buffer
1889 encrypted with the truncated hash
1890 (it could allow an even easier
1891 attack on the password)
1893 Likewise, obey the admin's restriction
1896 if (lp_client_lanman_auth() &&
1897 E_deshash(newpswd, new_lm_hash) &&
1898 E_deshash(oldpswd, old_lm_hash)) {
1899 new_lm_pswd = data_blob(NULL, 516);
1900 old_lm_hash_enc = data_blob(NULL, 16);
1901 encode_pw_buffer(new_lm_pswd.data, newpswd,
1902 STR_UNICODE);
1904 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
1905 E_old_pw_hash(new_nt_hash, old_lm_hash,
1906 old_lm_hash_enc.data);
1907 } else {
1908 new_lm_pswd.data = NULL;
1909 new_lm_pswd.length = 0;
1910 old_lm_hash_enc.data = NULL;
1911 old_lm_hash_enc.length = 0;
1914 encode_pw_buffer(new_nt_pswd.data, newpswd,
1915 STR_UNICODE);
1917 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
1918 E_old_pw_hash(new_nt_hash, old_nt_hash,
1919 old_nt_hash_enc.data);
1922 if (!full_username && !username) {
1923 x_fprintf(x_stdout, "Error: No username supplied!\n");
1924 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1925 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1926 x_fprintf(x_stdout, "Error: No NT or LM password "
1927 "blobs supplied!\n");
1928 } else {
1929 char *error_string = NULL;
1931 if (full_username && !username) {
1932 fstring fstr_user;
1933 fstring fstr_domain;
1935 if (!parse_ntlm_auth_domain_user(full_username,
1936 fstr_user,
1937 fstr_domain)) {
1938 /* username might be 'tainted', don't
1939 * print into our new-line
1940 * deleimianted stream */
1941 x_fprintf(x_stdout, "Error: Could not "
1942 "parse into domain and "
1943 "username\n");
1944 SAFE_FREE(username);
1945 username = smb_xstrdup(full_username);
1946 } else {
1947 SAFE_FREE(username);
1948 SAFE_FREE(domain);
1949 username = smb_xstrdup(fstr_user);
1950 domain = smb_xstrdup(fstr_domain);
1955 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1956 username, domain,
1957 new_nt_pswd,
1958 old_nt_hash_enc,
1959 new_lm_pswd,
1960 old_lm_hash_enc,
1961 &error_string))) {
1962 x_fprintf(x_stdout, "Password-Change: No\n");
1963 x_fprintf(x_stdout, "Password-Change-Error: "
1964 "%s\n.\n", error_string);
1965 } else {
1966 x_fprintf(x_stdout, "Password-Change: Yes\n");
1969 SAFE_FREE(error_string);
1971 /* clear out the state */
1972 new_nt_pswd = data_blob_null;
1973 old_nt_hash_enc = data_blob_null;
1974 new_lm_pswd = data_blob_null;
1975 old_nt_hash_enc = data_blob_null;
1976 SAFE_FREE(full_username);
1977 SAFE_FREE(username);
1978 SAFE_FREE(domain);
1979 SAFE_FREE(newpswd);
1980 SAFE_FREE(oldpswd);
1981 x_fprintf(x_stdout, ".\n");
1983 return;
1986 request = buf;
1988 /* Indicates a base64 encoded structure */
1989 parameter = strstr_m(request, ":: ");
1990 if (!parameter) {
1991 parameter = strstr_m(request, ": ");
1993 if (!parameter) {
1994 DEBUG(0, ("Parameter not found!\n"));
1995 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1996 return;
1999 parameter[0] ='\0';
2000 parameter++;
2001 parameter[0] ='\0';
2002 parameter++;
2003 } else {
2004 parameter[0] ='\0';
2005 parameter++;
2006 parameter[0] ='\0';
2007 parameter++;
2008 parameter[0] ='\0';
2009 parameter++;
2011 base64_decode_inplace(parameter);
2014 if (strequal(request, "new-nt-password-blob")) {
2015 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2016 if (new_nt_pswd.length != 516) {
2017 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2018 "(got %d bytes, expected 516)\n.\n",
2019 parameter,
2020 (int)new_nt_pswd.length);
2021 new_nt_pswd = data_blob_null;
2023 } else if (strequal(request, "old-nt-hash-blob")) {
2024 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2025 if (old_nt_hash_enc.length != 16) {
2026 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2027 "(got %d bytes, expected 16)\n.\n",
2028 parameter,
2029 (int)old_nt_hash_enc.length);
2030 old_nt_hash_enc = data_blob_null;
2032 } else if (strequal(request, "new-lm-password-blob")) {
2033 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2034 if (new_lm_pswd.length != 516) {
2035 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2036 "(got %d bytes, expected 516)\n.\n",
2037 parameter,
2038 (int)new_lm_pswd.length);
2039 new_lm_pswd = data_blob_null;
2042 else if (strequal(request, "old-lm-hash-blob")) {
2043 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2044 if (old_lm_hash_enc.length != 16)
2046 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2047 "(got %d bytes, expected 16)\n.\n",
2048 parameter,
2049 (int)old_lm_hash_enc.length);
2050 old_lm_hash_enc = data_blob_null;
2052 } else if (strequal(request, "nt-domain")) {
2053 domain = smb_xstrdup(parameter);
2054 } else if(strequal(request, "username")) {
2055 username = smb_xstrdup(parameter);
2056 } else if(strequal(request, "full-username")) {
2057 username = smb_xstrdup(parameter);
2058 } else if(strequal(request, "new-password")) {
2059 newpswd = smb_xstrdup(parameter);
2060 } else if (strequal(request, "old-password")) {
2061 oldpswd = smb_xstrdup(parameter);
2062 } else {
2063 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2067 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2068 struct loadparm_context *lp_ctx,
2069 struct ntlm_auth_state *state,
2070 stdio_helper_function fn, void **private2)
2072 char *buf;
2073 char tmp[INITIAL_BUFFER_SIZE+1];
2074 int length, buf_size = 0;
2075 char *c;
2077 buf = talloc_strdup(state->mem_ctx, "");
2078 if (!buf) {
2079 DEBUG(0, ("Failed to allocate input buffer.\n"));
2080 x_fprintf(x_stderr, "ERR\n");
2081 exit(1);
2084 do {
2086 /* this is not a typo - x_fgets doesn't work too well under
2087 * squid */
2088 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2089 if (ferror(stdin)) {
2090 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2091 "(%s)\n", ferror(stdin),
2092 strerror(ferror(stdin))));
2094 exit(1);
2096 exit(0);
2099 buf = talloc_strdup_append_buffer(buf, tmp);
2100 buf_size += INITIAL_BUFFER_SIZE;
2102 if (buf_size > MAX_BUFFER_SIZE) {
2103 DEBUG(2, ("Oversized message\n"));
2104 x_fprintf(x_stderr, "ERR\n");
2105 talloc_free(buf);
2106 return;
2109 c = strchr(buf, '\n');
2110 } while (c == NULL);
2112 *c = '\0';
2113 length = c-buf;
2115 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2117 if (buf[0] == '\0') {
2118 DEBUG(2, ("Invalid Request\n"));
2119 x_fprintf(x_stderr, "ERR\n");
2120 talloc_free(buf);
2121 return;
2124 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2125 talloc_free(buf);
2129 static void squid_stream(enum stdio_helper_mode stdio_mode,
2130 struct loadparm_context *lp_ctx,
2131 stdio_helper_function fn) {
2132 TALLOC_CTX *mem_ctx;
2133 struct ntlm_auth_state *state;
2135 /* initialize FDescs */
2136 x_setbuf(x_stdout, NULL);
2137 x_setbuf(x_stderr, NULL);
2139 mem_ctx = talloc_init("ntlm_auth");
2140 if (!mem_ctx) {
2141 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2142 x_fprintf(x_stderr, "ERR\n");
2143 exit(1);
2146 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2147 if (!state) {
2148 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2149 x_fprintf(x_stderr, "ERR\n");
2150 exit(1);
2153 state->mem_ctx = mem_ctx;
2154 state->helper_mode = stdio_mode;
2156 while(1) {
2157 TALLOC_CTX *frame = talloc_stackframe();
2158 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2159 TALLOC_FREE(frame);
2164 /* Authenticate a user with a challenge/response */
2166 static bool check_auth_crap(void)
2168 NTSTATUS nt_status;
2169 uint32_t flags = 0;
2170 char lm_key[8];
2171 char user_session_key[16];
2172 char *hex_lm_key;
2173 char *hex_user_session_key;
2174 char *error_string;
2175 static uint8_t zeros[16];
2177 x_setbuf(x_stdout, NULL);
2179 if (request_lm_key)
2180 flags |= WBFLAG_PAM_LMKEY;
2182 if (request_user_session_key)
2183 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2185 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2187 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2188 opt_workstation,
2189 &opt_challenge,
2190 &opt_lm_response,
2191 &opt_nt_response,
2192 flags, 0,
2193 (unsigned char *)lm_key,
2194 (unsigned char *)user_session_key,
2195 &error_string, NULL);
2197 if (!NT_STATUS_IS_OK(nt_status)) {
2198 x_fprintf(x_stdout, "%s (0x%x)\n",
2199 error_string,
2200 NT_STATUS_V(nt_status));
2201 SAFE_FREE(error_string);
2202 return False;
2205 if (request_lm_key
2206 && (memcmp(zeros, lm_key,
2207 sizeof(lm_key)) != 0)) {
2208 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2209 sizeof(lm_key));
2210 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2211 TALLOC_FREE(hex_lm_key);
2213 if (request_user_session_key
2214 && (memcmp(zeros, user_session_key,
2215 sizeof(user_session_key)) != 0)) {
2216 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2217 sizeof(user_session_key));
2218 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2219 TALLOC_FREE(hex_user_session_key);
2222 return True;
2225 /* Main program */
2227 enum {
2228 OPT_USERNAME = 1000,
2229 OPT_DOMAIN,
2230 OPT_WORKSTATION,
2231 OPT_CHALLENGE,
2232 OPT_RESPONSE,
2233 OPT_LM,
2234 OPT_NT,
2235 OPT_PASSWORD,
2236 OPT_LM_KEY,
2237 OPT_USER_SESSION_KEY,
2238 OPT_DIAGNOSTICS,
2239 OPT_REQUIRE_MEMBERSHIP,
2240 OPT_USE_CACHED_CREDS,
2241 OPT_PAM_WINBIND_CONF,
2242 OPT_TARGET_SERVICE,
2243 OPT_TARGET_HOSTNAME,
2244 OPT_OFFLINE_LOGON
2247 int main(int argc, const char **argv)
2249 TALLOC_CTX *frame = talloc_stackframe();
2250 int opt;
2251 static const char *helper_protocol;
2252 static int diagnostics;
2254 static const char *hex_challenge;
2255 static const char *hex_lm_response;
2256 static const char *hex_nt_response;
2257 struct loadparm_context *lp_ctx;
2258 poptContext pc;
2260 /* NOTE: DO NOT change this interface without considering the implications!
2261 This is an external interface, which other programs will use to interact
2262 with this helper.
2265 /* We do not use single-letter command abbreviations, because they harm future
2266 interface stability. */
2268 struct poptOption long_options[] = {
2269 POPT_AUTOHELP
2270 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2271 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2272 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2273 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2274 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2275 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2276 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2277 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2278 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2279 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2280 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2281 { "offline-logon", 0, POPT_ARG_NONE, &offline_logon,
2282 OPT_OFFLINE_LOGON,
2283 "Use cached passwords when DC is offline"},
2284 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2285 OPT_DIAGNOSTICS,
2286 "Perform diagnostics on the authentication chain"},
2287 { "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" },
2288 { "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" },
2289 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2290 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2291 POPT_COMMON_CONFIGFILE
2292 POPT_COMMON_VERSION
2293 POPT_COMMON_OPTION
2294 POPT_TABLEEND
2297 /* Samba client initialisation */
2298 smb_init_locale();
2300 setup_logging("ntlm_auth", DEBUG_STDERR);
2302 /* Parse options */
2304 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2306 /* Parse command line options */
2308 if (argc == 1) {
2309 poptPrintHelp(pc, stderr, 0);
2310 return 1;
2313 while((opt = poptGetNextOpt(pc)) != -1) {
2314 /* Get generic config options like --configfile */
2317 poptFreeContext(pc);
2319 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2320 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2321 get_dyn_CONFIGFILE(), strerror(errno));
2322 exit(1);
2325 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2326 POPT_CONTEXT_KEEP_FIRST);
2328 while((opt = poptGetNextOpt(pc)) != -1) {
2329 switch (opt) {
2330 case OPT_CHALLENGE:
2331 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2332 if (opt_challenge.length != 8) {
2333 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2334 hex_challenge,
2335 (int)opt_challenge.length);
2336 exit(1);
2338 break;
2339 case OPT_LM:
2340 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2341 if (opt_lm_response.length != 24) {
2342 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2343 hex_lm_response,
2344 (int)opt_lm_response.length);
2345 exit(1);
2347 break;
2349 case OPT_NT:
2350 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2351 if (opt_nt_response.length < 24) {
2352 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2353 hex_nt_response,
2354 (int)opt_nt_response.length);
2355 exit(1);
2357 break;
2359 case OPT_REQUIRE_MEMBERSHIP:
2360 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2361 require_membership_of_sid = require_membership_of;
2363 break;
2367 if (opt_username) {
2368 char *domain = SMB_STRDUP(opt_username);
2369 char *p = strchr_m(domain, *lp_winbind_separator());
2370 if (p) {
2371 opt_username = p+1;
2372 *p = '\0';
2373 if (opt_domain && !strequal(opt_domain, domain)) {
2374 x_fprintf(x_stderr, "Domain specified in username (%s) "
2375 "doesn't match specified domain (%s)!\n\n",
2376 domain, opt_domain);
2377 poptPrintHelp(pc, stderr, 0);
2378 exit(1);
2380 opt_domain = domain;
2381 } else {
2382 SAFE_FREE(domain);
2386 /* Note: if opt_domain is "" then send no domain */
2387 if (opt_domain == NULL) {
2388 opt_domain = get_winbind_domain();
2391 if (opt_workstation == NULL) {
2392 opt_workstation = "";
2395 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2396 if (lp_ctx == NULL) {
2397 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2398 exit(1);
2401 if (helper_protocol) {
2402 int i;
2403 for (i=0; i<NUM_HELPER_MODES; i++) {
2404 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2405 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2406 exit(0);
2409 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2411 for (i=0; i<NUM_HELPER_MODES; i++) {
2412 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2415 exit(1);
2418 if (!opt_username || !*opt_username) {
2419 x_fprintf(x_stderr, "username must be specified!\n\n");
2420 poptPrintHelp(pc, stderr, 0);
2421 exit(1);
2424 if (opt_challenge.length) {
2425 if (!check_auth_crap()) {
2426 exit(1);
2428 exit(0);
2431 if (!opt_password) {
2432 char pwd[256] = {0};
2433 int rc;
2435 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2436 if (rc == 0) {
2437 opt_password = SMB_STRDUP(pwd);
2441 if (diagnostics) {
2442 if (!diagnose_ntlm_auth()) {
2443 return 1;
2445 } else {
2446 fstring user;
2448 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2449 if (!check_plaintext_auth(user, opt_password, True)) {
2450 return 1;
2454 /* Exit code */
2456 poptFreeContext(pc);
2457 TALLOC_FREE(frame);
2458 return 0;