ntlm_auth: Avoid some statics
[Samba.git] / source3 / utils / ntlm_auth.c
blob9888dbc17917810828e3da2c678c7c199085efc8
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"
49 #include "lib/util/base64.h"
50 #include "lib/util/xfile.h"
52 #if HAVE_KRB5
53 #include "auth/kerberos/pac_utils.h"
54 #endif
56 #ifndef PAM_WINBIND_CONFIG_FILE
57 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
58 #endif
60 #define WINBIND_KRB5_AUTH 0x00000080
62 #undef DBGC_CLASS
63 #define DBGC_CLASS DBGC_WINBIND
65 #define INITIAL_BUFFER_SIZE 300
66 #define MAX_BUFFER_SIZE 630000
68 enum stdio_helper_mode {
69 SQUID_2_4_BASIC,
70 SQUID_2_5_BASIC,
71 SQUID_2_5_NTLMSSP,
72 NTLMSSP_CLIENT_1,
73 GSS_SPNEGO_SERVER,
74 GSS_SPNEGO_CLIENT,
75 NTLM_SERVER_1,
76 NTLM_CHANGE_PASSWORD_1,
77 NUM_HELPER_MODES
80 enum ntlm_auth_cli_state {
81 CLIENT_INITIAL = 0,
82 CLIENT_RESPONSE,
83 CLIENT_FINISHED,
84 CLIENT_ERROR
87 struct ntlm_auth_state {
88 TALLOC_CTX *mem_ctx;
89 enum stdio_helper_mode helper_mode;
90 enum ntlm_auth_cli_state cli_state;
91 struct ntlmssp_state *ntlmssp_state;
92 uint32_t neg_flags;
93 char *want_feature_list;
94 bool have_session_key;
95 DATA_BLOB session_key;
96 DATA_BLOB initial_message;
97 void *gensec_private_1;
99 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
100 struct loadparm_context *lp_ctx,
101 struct ntlm_auth_state *state, char *buf,
102 int length, void **private2);
104 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
105 struct loadparm_context *lp_ctx,
106 char *buf, int length, void **private1);
108 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
109 struct loadparm_context *lp_ctx,
110 struct ntlm_auth_state *state,
111 stdio_helper_function fn, void **private2);
113 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
114 struct loadparm_context *lp_ctx,
115 struct ntlm_auth_state *state,
116 char *buf, int length, void **private2);
118 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
119 struct loadparm_context *lp_ctx,
120 struct ntlm_auth_state *state,
121 char *buf, int length, void **private2);
123 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
124 struct loadparm_context *lp_ctx,
125 struct ntlm_auth_state *state,
126 char *buf, int length, void **private2);
128 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
129 struct loadparm_context *lp_ctx,
130 struct ntlm_auth_state *state,
131 char *buf, int length, void **private2);
133 static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
134 struct loadparm_context *lp_ctx,
135 struct ntlm_auth_state *state,
136 char *buf, int length, void **private2);
138 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
139 struct loadparm_context *lp_ctx,
140 struct ntlm_auth_state *state,
141 char *buf, int length, void **private2);
143 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
144 struct loadparm_context *lp_ctx,
145 struct ntlm_auth_state *state,
146 char *buf, int length, void **private2);
148 static const struct {
149 enum stdio_helper_mode mode;
150 const char *name;
151 stdio_helper_function fn;
152 } stdio_helper_protocols[] = {
153 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
154 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
155 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
156 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
157 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
158 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
159 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
160 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
161 { NUM_HELPER_MODES, NULL, NULL}
164 const char *opt_username;
165 const char *opt_domain;
166 const char *opt_workstation;
167 const char *opt_password;
168 static DATA_BLOB opt_challenge;
169 static DATA_BLOB opt_lm_response;
170 static DATA_BLOB opt_nt_response;
171 static int request_lm_key;
172 static int request_user_session_key;
173 static int use_cached_creds;
174 static int offline_logon;
175 static int opt_allow_mschapv2;
177 static const char *require_membership_of;
178 static const char *require_membership_of_sid;
179 static const char *opt_pam_winbind_conf;
181 const char *opt_target_service;
182 const char *opt_target_hostname;
185 /* This is a bit hairy, but the basic idea is to do a password callback
186 to the calling application. The callback comes from within gensec */
188 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
189 struct loadparm_context *lp_ctx,
190 struct ntlm_auth_state *state, char *buf, int length,
191 void **password)
193 DATA_BLOB in;
194 if (strlen(buf) < 2) {
195 DEBUG(1, ("query [%s] invalid", buf));
196 x_fprintf(x_stdout, "BH Query invalid\n");
197 return;
200 if (strlen(buf) > 3) {
201 in = base64_decode_data_blob(buf + 3);
202 } else {
203 in = data_blob(NULL, 0);
206 if (strncmp(buf, "PW ", 3) == 0) {
208 *password = talloc_strndup(NULL,
209 (const char *)in.data, in.length);
211 if (*password == NULL) {
212 DEBUG(1, ("Out of memory\n"));
213 x_fprintf(x_stdout, "BH Out of memory\n");
214 data_blob_free(&in);
215 return;
218 x_fprintf(x_stdout, "OK\n");
219 data_blob_free(&in);
220 return;
222 DEBUG(1, ("Asked for (and expected) a password\n"));
223 x_fprintf(x_stdout, "BH Expected a password\n");
224 data_blob_free(&in);
228 * Callback for password credentials. This is not async, and when
229 * GENSEC and the credentials code is made async, it will look rather
230 * different.
233 static const char *get_password(struct cli_credentials *credentials)
235 TALLOC_CTX *frame = talloc_stackframe();
236 char *password = NULL;
237 struct ntlm_auth_state *state;
239 state = talloc_zero(frame, struct ntlm_auth_state);
240 if (state == NULL) {
241 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
242 x_fprintf(x_stderr, "ERR\n");
243 exit(1);
246 state->mem_ctx = state;
248 /* Ask for a password */
249 x_fprintf(x_stdout, "PW\n");
251 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, state, manage_gensec_get_pw_request, (void **)&password);
252 talloc_steal(credentials, password);
253 TALLOC_FREE(frame);
254 return password;
258 * A limited set of features are defined with text strings as needed
259 * by ntlm_auth
262 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
264 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
265 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
266 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
268 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
269 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
270 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
272 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
273 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
274 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
276 if (in_list("NTLMSSP_FEATURE_CCACHE", feature_list, true)) {
277 DEBUG(10, ("want GENSEC_FEATURE_NTLM_CCACHE\n"));
278 gensec_want_feature(state, GENSEC_FEATURE_NTLM_CCACHE);
282 static char winbind_separator(void)
284 struct winbindd_response response;
285 static bool got_sep;
286 static char sep;
288 if (got_sep)
289 return sep;
291 ZERO_STRUCT(response);
293 /* Send off request */
295 if (winbindd_request_response(NULL, WINBINDD_INFO, NULL, &response) !=
296 NSS_STATUS_SUCCESS) {
297 d_printf("could not obtain winbind separator!\n");
298 return *lp_winbind_separator();
301 sep = response.data.info.winbind_separator;
302 got_sep = True;
304 if (!sep) {
305 d_printf("winbind separator was NULL!\n");
306 return *lp_winbind_separator();
309 return sep;
312 const char *get_winbind_domain(void)
314 struct winbindd_response response;
316 static fstring winbind_domain;
317 if (*winbind_domain) {
318 return winbind_domain;
321 ZERO_STRUCT(response);
323 /* Send off request */
325 if (winbindd_request_response(NULL, WINBINDD_DOMAIN_NAME, NULL, &response) !=
326 NSS_STATUS_SUCCESS) {
327 DEBUG(1, ("could not obtain winbind domain name!\n"));
328 return lp_workgroup();
331 fstrcpy(winbind_domain, response.data.domain_name);
333 return winbind_domain;
337 const char *get_winbind_netbios_name(void)
339 struct winbindd_response response;
341 static fstring winbind_netbios_name;
343 if (*winbind_netbios_name) {
344 return winbind_netbios_name;
347 ZERO_STRUCT(response);
349 /* Send off request */
351 if (winbindd_request_response(NULL, WINBINDD_NETBIOS_NAME, NULL, &response) !=
352 NSS_STATUS_SUCCESS) {
353 DEBUG(1, ("could not obtain winbind netbios name!\n"));
354 return lp_netbios_name();
357 fstrcpy(winbind_netbios_name, response.data.netbios_name);
359 return winbind_netbios_name;
363 DATA_BLOB get_challenge(void)
365 static DATA_BLOB chal;
366 if (opt_challenge.length)
367 return opt_challenge;
369 chal = data_blob(NULL, 8);
371 generate_random_buffer(chal.data, chal.length);
372 return chal;
375 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
376 form DOMAIN/user into a domain and a user */
378 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
379 fstring user)
382 char *p = strchr(domuser,winbind_separator());
384 if (!p) {
385 return False;
388 fstrcpy(user, p+1);
389 fstrcpy(domain, domuser);
390 domain[PTR_DIFF(p, domuser)] = 0;
391 return strupper_m(domain);
394 static bool get_require_membership_sid(void) {
395 struct winbindd_request request;
396 struct winbindd_response response;
398 if (!require_membership_of) {
399 return True;
402 if (require_membership_of_sid) {
403 return True;
406 /* Otherwise, ask winbindd for the name->sid request */
408 ZERO_STRUCT(request);
409 ZERO_STRUCT(response);
411 if (!parse_ntlm_auth_domain_user(require_membership_of,
412 request.data.name.dom_name,
413 request.data.name.name)) {
414 DEBUG(0, ("Could not parse %s into separate domain/name parts!\n",
415 require_membership_of));
416 return False;
419 if (winbindd_request_response(NULL, WINBINDD_LOOKUPNAME, &request, &response) !=
420 NSS_STATUS_SUCCESS) {
421 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
422 require_membership_of));
423 return False;
426 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
428 if (require_membership_of_sid)
429 return True;
431 return False;
435 * Get some configuration from pam_winbind.conf to see if we
436 * need to contact trusted domain
439 int get_pam_winbind_config()
441 int ctrl = 0;
442 struct tiniparser_dictionary *d = NULL;
444 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
445 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
448 d = tiniparser_load(opt_pam_winbind_conf);
450 if (!d) {
451 return 0;
454 if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
455 ctrl |= WINBIND_KRB5_AUTH;
458 tiniparser_freedict(d);
460 return ctrl;
463 /* Authenticate a user with a plaintext password */
465 static bool check_plaintext_auth(const char *user, const char *pass,
466 bool stdout_diagnostics)
468 struct winbindd_request request;
469 struct winbindd_response response;
470 NSS_STATUS result;
472 if (!get_require_membership_sid()) {
473 return False;
476 /* Send off request */
478 ZERO_STRUCT(request);
479 ZERO_STRUCT(response);
481 fstrcpy(request.data.auth.user, user);
482 fstrcpy(request.data.auth.pass, pass);
483 if (require_membership_of_sid) {
484 strlcpy(request.data.auth.require_membership_of_sid,
485 require_membership_of_sid,
486 sizeof(request.data.auth.require_membership_of_sid));
489 if (offline_logon) {
490 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
493 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH, &request, &response);
495 /* Display response */
497 if (stdout_diagnostics) {
498 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
499 d_printf("Reading winbind reply failed! (0x01)\n");
502 d_printf("%s: %s (0x%x)\n",
503 response.data.auth.nt_status_string,
504 response.data.auth.error_string,
505 response.data.auth.nt_status);
506 } else {
507 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
508 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
511 DEBUG(3, ("%s: %s (0x%x)\n",
512 response.data.auth.nt_status_string,
513 response.data.auth.error_string,
514 response.data.auth.nt_status));
517 return (result == NSS_STATUS_SUCCESS);
520 /* authenticate a user with an encrypted username/password */
522 NTSTATUS contact_winbind_auth_crap(const char *username,
523 const char *domain,
524 const char *workstation,
525 const DATA_BLOB *challenge,
526 const DATA_BLOB *lm_response,
527 const DATA_BLOB *nt_response,
528 uint32_t flags,
529 uint32_t extra_logon_parameters,
530 uint8_t lm_key[8],
531 uint8_t user_session_key[16],
532 char **error_string,
533 char **unix_name)
535 NTSTATUS nt_status;
536 NSS_STATUS result;
537 struct winbindd_request request;
538 struct winbindd_response response;
540 if (!get_require_membership_sid()) {
541 return NT_STATUS_INVALID_PARAMETER;
544 ZERO_STRUCT(request);
545 ZERO_STRUCT(response);
547 request.flags = flags;
549 request.data.auth_crap.logon_parameters = extra_logon_parameters
550 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
552 if (opt_allow_mschapv2) {
553 request.data.auth_crap.logon_parameters |= MSV1_0_ALLOW_MSVCHAPV2;
556 if (require_membership_of_sid)
557 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
559 fstrcpy(request.data.auth_crap.user, username);
560 fstrcpy(request.data.auth_crap.domain, domain);
562 fstrcpy(request.data.auth_crap.workstation,
563 workstation);
565 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
567 if (lm_response && lm_response->length) {
568 memcpy(request.data.auth_crap.lm_resp,
569 lm_response->data,
570 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
571 request.data.auth_crap.lm_resp_len = lm_response->length;
574 if (nt_response && nt_response->length) {
575 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
576 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
577 request.extra_len = nt_response->length;
578 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
579 if (request.extra_data.data == NULL) {
580 return NT_STATUS_NO_MEMORY;
582 memcpy(request.extra_data.data, nt_response->data,
583 nt_response->length);
585 } else {
586 memcpy(request.data.auth_crap.nt_resp,
587 nt_response->data, nt_response->length);
589 request.data.auth_crap.nt_resp_len = nt_response->length;
592 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH_CRAP, &request, &response);
593 SAFE_FREE(request.extra_data.data);
595 /* Display response */
597 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
598 nt_status = NT_STATUS_UNSUCCESSFUL;
599 if (error_string)
600 *error_string = smb_xstrdup("Reading winbind reply failed!");
601 winbindd_free_response(&response);
602 return nt_status;
605 nt_status = (NT_STATUS(response.data.auth.nt_status));
606 if (!NT_STATUS_IS_OK(nt_status)) {
607 if (error_string)
608 *error_string = smb_xstrdup(response.data.auth.error_string);
609 winbindd_free_response(&response);
610 return nt_status;
613 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
614 memcpy(lm_key, response.data.auth.first_8_lm_hash,
615 sizeof(response.data.auth.first_8_lm_hash));
617 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
618 memcpy(user_session_key, response.data.auth.user_session_key,
619 sizeof(response.data.auth.user_session_key));
622 if (flags & WBFLAG_PAM_UNIX_NAME) {
623 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
624 if (!*unix_name) {
625 winbindd_free_response(&response);
626 return NT_STATUS_NO_MEMORY;
630 winbindd_free_response(&response);
631 return nt_status;
634 /* contact server to change user password using auth crap */
635 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
636 const char *domain,
637 const DATA_BLOB new_nt_pswd,
638 const DATA_BLOB old_nt_hash_enc,
639 const DATA_BLOB new_lm_pswd,
640 const DATA_BLOB old_lm_hash_enc,
641 char **error_string)
643 NTSTATUS nt_status;
644 NSS_STATUS result;
645 struct winbindd_request request;
646 struct winbindd_response response;
648 if (!get_require_membership_sid())
650 if(error_string)
651 *error_string = smb_xstrdup("Can't get membership sid.");
652 return NT_STATUS_INVALID_PARAMETER;
655 ZERO_STRUCT(request);
656 ZERO_STRUCT(response);
658 if(username != NULL)
659 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
660 if(domain != NULL)
661 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
663 if(new_nt_pswd.length)
665 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
666 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
669 if(old_nt_hash_enc.length)
671 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));
672 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
675 if(new_lm_pswd.length)
677 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
678 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
681 if(old_lm_hash_enc.length)
683 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));
684 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
687 result = winbindd_request_response(NULL, WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
689 /* Display response */
691 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
693 nt_status = NT_STATUS_UNSUCCESSFUL;
694 if (error_string)
695 *error_string = smb_xstrdup("Reading winbind reply failed!");
696 winbindd_free_response(&response);
697 return nt_status;
700 nt_status = (NT_STATUS(response.data.auth.nt_status));
701 if (!NT_STATUS_IS_OK(nt_status))
703 if (error_string)
704 *error_string = smb_xstrdup(response.data.auth.error_string);
705 winbindd_free_response(&response);
706 return nt_status;
709 winbindd_free_response(&response);
711 return nt_status;
714 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
715 TALLOC_CTX *mem_ctx,
716 void *server_returned_info,
717 const char *original_user_name,
718 uint32_t session_info_flags,
719 struct auth_session_info **session_info_out)
721 const char *unix_username = (const char *)server_returned_info;
722 bool ok;
723 struct dom_sid *sids = NULL;
724 struct auth_session_info *session_info = NULL;
726 session_info = talloc_zero(mem_ctx, struct auth_session_info);
727 if (session_info == NULL) {
728 return NT_STATUS_NO_MEMORY;
731 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
732 if (session_info->unix_info == NULL) {
733 TALLOC_FREE(session_info);
734 return NT_STATUS_NO_MEMORY;
736 session_info->unix_info->unix_name = talloc_strdup(session_info->unix_info,
737 unix_username);
738 if (session_info->unix_info->unix_name == NULL) {
739 TALLOC_FREE(session_info);
740 return NT_STATUS_NO_MEMORY;
743 session_info->security_token = talloc_zero(session_info, struct security_token);
744 if (session_info->security_token == NULL) {
745 TALLOC_FREE(session_info);
746 return NT_STATUS_NO_MEMORY;
749 sids = talloc_zero_array(session_info->security_token,
750 struct dom_sid, 3);
751 if (sids == NULL) {
752 TALLOC_FREE(session_info);
753 return NT_STATUS_NO_MEMORY;
755 ok = dom_sid_parse(SID_WORLD, &sids[0]);
756 if (!ok) {
757 TALLOC_FREE(session_info);
758 return NT_STATUS_INTERNAL_ERROR;
760 ok = dom_sid_parse(SID_NT_NETWORK, &sids[1]);
761 if (!ok) {
762 TALLOC_FREE(session_info);
763 return NT_STATUS_INTERNAL_ERROR;
765 ok = dom_sid_parse(SID_NT_AUTHENTICATED_USERS, &sids[2]);
766 if (!ok) {
767 TALLOC_FREE(session_info);
768 return NT_STATUS_INTERNAL_ERROR;
771 session_info->security_token->num_sids = talloc_array_length(sids);
772 session_info->security_token->sids = sids;
774 *session_info_out = session_info;
776 return NT_STATUS_OK;
779 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
780 TALLOC_CTX *mem_ctx,
781 struct smb_krb5_context *smb_krb5_context,
782 DATA_BLOB *pac_blob,
783 const char *princ_name,
784 const struct tsocket_address *remote_address,
785 uint32_t session_info_flags,
786 struct auth_session_info **session_info)
788 TALLOC_CTX *tmp_ctx;
789 struct PAC_LOGON_INFO *logon_info = NULL;
790 char *unixuser;
791 NTSTATUS status;
792 char *domain = NULL;
793 char *realm = NULL;
794 char *user = NULL;
795 char *p;
797 tmp_ctx = talloc_new(mem_ctx);
798 if (!tmp_ctx) {
799 return NT_STATUS_NO_MEMORY;
802 if (pac_blob) {
803 #ifdef HAVE_KRB5
804 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
805 NULL, NULL, 0, &logon_info);
806 #else
807 status = NT_STATUS_ACCESS_DENIED;
808 #endif
809 if (!NT_STATUS_IS_OK(status)) {
810 goto done;
814 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
816 p = strchr_m(princ_name, '@');
817 if (!p) {
818 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
819 princ_name));
820 return NT_STATUS_LOGON_FAILURE;
823 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
824 if (!user) {
825 return NT_STATUS_NO_MEMORY;
828 realm = talloc_strdup(talloc_tos(), p + 1);
829 if (!realm) {
830 return NT_STATUS_NO_MEMORY;
833 if (!strequal(realm, lp_realm())) {
834 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
835 if (!lp_allow_trusted_domains()) {
836 return NT_STATUS_LOGON_FAILURE;
840 if (logon_info && logon_info->info3.base.logon_domain.string) {
841 domain = talloc_strdup(mem_ctx,
842 logon_info->info3.base.logon_domain.string);
843 if (!domain) {
844 return NT_STATUS_NO_MEMORY;
846 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
847 } else {
849 /* If we have winbind running, we can (and must) shorten the
850 username by using the short netbios name. Otherwise we will
851 have inconsistent user names. With Kerberos, we get the
852 fully qualified realm, with ntlmssp we get the short
853 name. And even w2k3 does use ntlmssp if you for example
854 connect to an ip address. */
856 wbcErr wbc_status;
857 struct wbcDomainInfo *info = NULL;
859 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
860 realm));
862 wbc_status = wbcDomainInfo(realm, &info);
864 if (WBC_ERROR_IS_OK(wbc_status)) {
865 domain = talloc_strdup(mem_ctx,
866 info->short_name);
867 wbcFreeMemory(info);
868 } else {
869 DEBUG(3, ("Could not find short name: %s\n",
870 wbcErrorString(wbc_status)));
871 domain = talloc_strdup(mem_ctx, realm);
873 if (!domain) {
874 return NT_STATUS_NO_MEMORY;
876 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
879 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
880 if (!unixuser) {
881 status = NT_STATUS_NO_MEMORY;
882 goto done;
885 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
887 done:
888 TALLOC_FREE(tmp_ctx);
889 return status;
895 * Return the challenge as determined by the authentication subsystem
896 * @return an 8 byte random challenge
899 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
900 uint8_t chal[8])
902 if (auth_ctx->challenge.data.length == 8) {
903 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
904 auth_ctx->challenge.set_by));
905 memcpy(chal, auth_ctx->challenge.data.data, 8);
906 return NT_STATUS_OK;
909 if (!auth_ctx->challenge.set_by) {
910 generate_random_buffer(chal, 8);
912 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
913 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
914 auth_ctx->challenge.set_by = "random";
917 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
918 auth_ctx->challenge.set_by));
920 return NT_STATUS_OK;
924 * NTLM2 authentication modifies the effective challenge,
925 * @param challenge The new challenge value
927 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
929 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
930 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
932 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
933 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
935 return NT_STATUS_OK;
939 * Check the password on an NTLMSSP login.
941 * Return the session keys used on the connection.
944 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
945 TALLOC_CTX *mem_ctx,
946 const struct auth_usersupplied_info *user_info,
947 void **server_returned_info,
948 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
950 static const char zeros[16] = { 0, };
951 NTSTATUS nt_status;
952 char *error_string = NULL;
953 uint8_t lm_key[8];
954 uint8_t user_sess_key[16];
955 char *unix_name = NULL;
957 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
958 user_info->workstation_name,
959 &auth4_context->challenge.data,
960 &user_info->password.response.lanman,
961 &user_info->password.response.nt,
962 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
964 lm_key, user_sess_key,
965 &error_string, &unix_name);
967 if (NT_STATUS_IS_OK(nt_status)) {
968 if (memcmp(lm_key, zeros, 8) != 0) {
969 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
970 memcpy(lm_session_key->data, lm_key, 8);
971 memset(lm_session_key->data+8, '\0', 8);
974 if (memcmp(user_sess_key, zeros, 16) != 0) {
975 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
977 *server_returned_info = talloc_strdup(mem_ctx,
978 unix_name);
979 } else {
980 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
981 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
982 user_info->client.domain_name, user_info->client.account_name,
983 user_info->workstation_name,
984 error_string ? error_string : "unknown error (NULL)"));
987 SAFE_FREE(error_string);
988 SAFE_FREE(unix_name);
989 return nt_status;
992 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
993 TALLOC_CTX *mem_ctx,
994 const struct auth_usersupplied_info *user_info,
995 void **server_returned_info,
996 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
998 NTSTATUS nt_status;
999 struct samr_Password lm_pw, nt_pw;
1001 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1003 nt_status = ntlm_password_check(mem_ctx,
1004 true, true, 0,
1005 &auth4_context->challenge.data,
1006 &user_info->password.response.lanman,
1007 &user_info->password.response.nt,
1008 user_info->client.account_name,
1009 user_info->client.account_name,
1010 user_info->client.domain_name,
1011 &lm_pw, &nt_pw, session_key, lm_session_key);
1013 if (NT_STATUS_IS_OK(nt_status)) {
1014 *server_returned_info = talloc_asprintf(mem_ctx,
1015 "%s%c%s", user_info->client.domain_name,
1016 *lp_winbind_separator(),
1017 user_info->client.account_name);
1018 } else {
1019 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
1020 user_info->client.domain_name, user_info->client.account_name,
1021 user_info->workstation_name,
1022 nt_errstr(nt_status)));
1024 return nt_status;
1027 static NTSTATUS ntlm_auth_prepare_gensec_client(TALLOC_CTX *mem_ctx,
1028 struct loadparm_context *lp_ctx,
1029 struct gensec_security **gensec_security_out)
1031 struct gensec_security *gensec_security = NULL;
1032 NTSTATUS nt_status;
1033 TALLOC_CTX *tmp_ctx;
1034 const struct gensec_security_ops **backends = NULL;
1035 struct gensec_settings *gensec_settings = NULL;
1036 size_t idx = 0;
1038 tmp_ctx = talloc_new(mem_ctx);
1039 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1041 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1042 if (gensec_settings == NULL) {
1043 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1044 TALLOC_FREE(tmp_ctx);
1045 return NT_STATUS_NO_MEMORY;
1048 backends = talloc_zero_array(gensec_settings,
1049 const struct gensec_security_ops *, 4);
1050 if (backends == NULL) {
1051 TALLOC_FREE(tmp_ctx);
1052 return NT_STATUS_NO_MEMORY;
1054 gensec_settings->backends = backends;
1056 gensec_init();
1058 /* These need to be in priority order, krb5 before NTLMSSP */
1059 #if defined(HAVE_KRB5)
1060 backends[idx++] = &gensec_gse_krb5_security_ops;
1061 #endif
1063 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1065 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1067 nt_status = gensec_client_start(NULL, &gensec_security,
1068 gensec_settings);
1069 if (!NT_STATUS_IS_OK(nt_status)) {
1070 TALLOC_FREE(tmp_ctx);
1071 return nt_status;
1074 talloc_unlink(tmp_ctx, gensec_settings);
1076 if (opt_target_service != NULL) {
1077 nt_status = gensec_set_target_service(gensec_security,
1078 opt_target_service);
1079 if (!NT_STATUS_IS_OK(nt_status)) {
1080 TALLOC_FREE(tmp_ctx);
1081 return nt_status;
1085 if (opt_target_hostname != NULL) {
1086 nt_status = gensec_set_target_hostname(gensec_security,
1087 opt_target_hostname);
1088 if (!NT_STATUS_IS_OK(nt_status)) {
1089 TALLOC_FREE(tmp_ctx);
1090 return nt_status;
1094 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1095 TALLOC_FREE(tmp_ctx);
1096 return NT_STATUS_OK;
1099 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1101 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1102 if (auth4_context == NULL) {
1103 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1104 return NULL;
1106 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1107 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1108 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1109 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1110 if (local_pw) {
1111 auth4_context->check_ntlm_password = local_pw_check;
1112 } else {
1113 auth4_context->check_ntlm_password = winbind_pw_check;
1115 auth4_context->private_data = NULL;
1116 return auth4_context;
1119 static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx,
1120 struct loadparm_context *lp_ctx,
1121 struct gensec_security **gensec_security_out)
1123 struct gensec_security *gensec_security;
1124 NTSTATUS nt_status;
1126 TALLOC_CTX *tmp_ctx;
1127 const struct gensec_security_ops **backends;
1128 struct gensec_settings *gensec_settings;
1129 size_t idx = 0;
1130 struct cli_credentials *server_credentials;
1132 struct auth4_context *auth4_context;
1134 tmp_ctx = talloc_new(mem_ctx);
1135 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1137 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1138 if (auth4_context == NULL) {
1139 TALLOC_FREE(tmp_ctx);
1140 return NT_STATUS_NO_MEMORY;
1143 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1144 if (lp_ctx == NULL) {
1145 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1146 TALLOC_FREE(tmp_ctx);
1147 return NT_STATUS_NO_MEMORY;
1151 * This should be a 'netbios domain -> DNS domain'
1152 * mapping, and can currently validly return NULL on
1153 * poorly configured systems.
1155 * This is used for the NTLMSSP server
1158 if (opt_password) {
1159 gensec_settings->server_netbios_name = lp_netbios_name();
1160 gensec_settings->server_netbios_domain = lp_workgroup();
1161 } else {
1162 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1163 gensec_settings->server_netbios_domain = get_winbind_domain();
1166 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1167 get_mydnsdomname(talloc_tos()));
1168 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1169 get_mydnsfullname());
1171 backends = talloc_zero_array(gensec_settings,
1172 const struct gensec_security_ops *, 4);
1174 if (backends == NULL) {
1175 TALLOC_FREE(tmp_ctx);
1176 return NT_STATUS_NO_MEMORY;
1178 gensec_settings->backends = backends;
1180 gensec_init();
1182 /* These need to be in priority order, krb5 before NTLMSSP */
1183 #if defined(HAVE_KRB5)
1184 backends[idx++] = &gensec_gse_krb5_security_ops;
1185 #endif
1187 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1189 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1192 * This is anonymous for now, because we just use it
1193 * to set the kerberos state at the moment
1195 server_credentials = cli_credentials_init_anon(tmp_ctx);
1196 if (!server_credentials) {
1197 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1198 return NT_STATUS_NO_MEMORY;
1201 cli_credentials_set_conf(server_credentials, lp_ctx);
1203 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1204 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1205 } else {
1206 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1209 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1210 auth4_context, &gensec_security);
1212 if (!NT_STATUS_IS_OK(nt_status)) {
1213 TALLOC_FREE(tmp_ctx);
1214 return nt_status;
1217 gensec_set_credentials(gensec_security, server_credentials);
1219 talloc_unlink(tmp_ctx, lp_ctx);
1220 talloc_unlink(tmp_ctx, server_credentials);
1221 talloc_unlink(tmp_ctx, gensec_settings);
1222 talloc_unlink(tmp_ctx, auth4_context);
1224 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1225 TALLOC_FREE(tmp_ctx);
1226 return NT_STATUS_OK;
1229 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1230 struct loadparm_context *lp_ctx,
1231 struct ntlm_auth_state *state,
1232 char *buf, int length, void **private2)
1234 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1235 return;
1238 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1239 struct loadparm_context *lp_ctx,
1240 struct ntlm_auth_state *state,
1241 char *buf, int length, void **private2)
1243 char *user, *pass;
1244 user=buf;
1246 pass=(char *)memchr(buf,' ',length);
1247 if (!pass) {
1248 DEBUG(2, ("Password not found. Denying access\n"));
1249 x_fprintf(x_stdout, "ERR\n");
1250 return;
1252 *pass='\0';
1253 pass++;
1255 if (state->helper_mode == SQUID_2_5_BASIC) {
1256 rfc1738_unescape(user);
1257 rfc1738_unescape(pass);
1260 if (check_plaintext_auth(user, pass, False)) {
1261 x_fprintf(x_stdout, "OK\n");
1262 } else {
1263 x_fprintf(x_stdout, "ERR\n");
1267 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1268 struct loadparm_context *lp_ctx,
1269 char *buf, int length, void **private1)
1271 DATA_BLOB in;
1272 DATA_BLOB out = data_blob(NULL, 0);
1273 char *out_base64 = NULL;
1274 const char *reply_arg = NULL;
1275 struct gensec_ntlm_state {
1276 struct gensec_security *gensec_state;
1277 const char *set_password;
1279 struct gensec_ntlm_state *state;
1281 NTSTATUS nt_status;
1282 bool first = false;
1283 const char *reply_code;
1284 struct cli_credentials *creds;
1286 static char *want_feature_list = NULL;
1287 static DATA_BLOB session_key;
1289 TALLOC_CTX *mem_ctx;
1291 if (*private1) {
1292 state = (struct gensec_ntlm_state *)*private1;
1293 } else {
1294 state = talloc_zero(NULL, struct gensec_ntlm_state);
1295 if (!state) {
1296 x_fprintf(x_stdout, "BH No Memory\n");
1297 exit(1);
1299 *private1 = state;
1300 if (opt_password) {
1301 state->set_password = opt_password;
1305 if (strlen(buf) < 2) {
1306 DEBUG(1, ("query [%s] invalid", buf));
1307 x_fprintf(x_stdout, "BH Query invalid\n");
1308 return;
1311 if (strlen(buf) > 3) {
1312 if(strncmp(buf, "SF ", 3) == 0) {
1313 DEBUG(10, ("Setting flags to negotiate\n"));
1314 talloc_free(want_feature_list);
1315 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1316 x_fprintf(x_stdout, "OK\n");
1317 return;
1319 in = base64_decode_data_blob(buf + 3);
1320 } else {
1321 in = data_blob(NULL, 0);
1324 if (strncmp(buf, "YR", 2) == 0) {
1325 if (state->gensec_state) {
1326 talloc_free(state->gensec_state);
1327 state->gensec_state = NULL;
1329 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1330 /* Just return BH, like ntlm_auth from Samba 3 does. */
1331 x_fprintf(x_stdout, "BH Command expected\n");
1332 data_blob_free(&in);
1333 return;
1334 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1335 (strncmp(buf, "KK ", 3) != 0) &&
1336 (strncmp(buf, "AF ", 3) != 0) &&
1337 (strncmp(buf, "NA ", 3) != 0) &&
1338 (strncmp(buf, "UG", 2) != 0) &&
1339 (strncmp(buf, "PW ", 3) != 0) &&
1340 (strncmp(buf, "GK", 2) != 0) &&
1341 (strncmp(buf, "GF", 2) != 0)) {
1342 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1343 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1344 data_blob_free(&in);
1345 return;
1348 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1350 /* setup gensec */
1351 if (!(state->gensec_state)) {
1352 switch (stdio_helper_mode) {
1353 case GSS_SPNEGO_CLIENT:
1355 * cached credentials are only supported by
1356 * NTLMSSP_CLIENT_1 for now.
1358 use_cached_creds = false;
1359 /* fall through */
1360 case NTLMSSP_CLIENT_1:
1361 /* setup the client side */
1363 if (state->set_password != NULL) {
1364 use_cached_creds = false;
1367 if (use_cached_creds) {
1368 struct wbcCredentialCacheParams params;
1369 struct wbcCredentialCacheInfo *info = NULL;
1370 struct wbcAuthErrorInfo *error = NULL;
1371 wbcErr wbc_status;
1373 params.account_name = opt_username;
1374 params.domain_name = opt_domain;
1375 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1376 params.num_blobs = 0;
1377 params.blobs = NULL;
1379 wbc_status = wbcCredentialCache(&params, &info,
1380 &error);
1381 wbcFreeMemory(error);
1382 if (!WBC_ERROR_IS_OK(wbc_status)) {
1383 use_cached_creds = false;
1385 wbcFreeMemory(info);
1388 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1389 &state->gensec_state);
1390 if (!NT_STATUS_IS_OK(nt_status)) {
1391 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1392 talloc_free(mem_ctx);
1393 return;
1396 creds = cli_credentials_init(state->gensec_state);
1397 cli_credentials_set_conf(creds, lp_ctx);
1398 if (opt_username) {
1399 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1401 if (opt_domain) {
1402 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1404 if (use_cached_creds) {
1405 gensec_want_feature(state->gensec_state,
1406 GENSEC_FEATURE_NTLM_CCACHE);
1407 } else if (state->set_password) {
1408 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1409 } else {
1410 cli_credentials_set_password_callback(creds, get_password);
1412 if (opt_workstation) {
1413 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1416 gensec_set_credentials(state->gensec_state, creds);
1418 break;
1419 case GSS_SPNEGO_SERVER:
1420 case SQUID_2_5_NTLMSSP:
1422 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1423 &state->gensec_state);
1424 if (!NT_STATUS_IS_OK(nt_status)) {
1425 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1426 talloc_free(mem_ctx);
1427 return;
1429 break;
1431 default:
1432 talloc_free(mem_ctx);
1433 abort();
1436 gensec_want_feature_list(state->gensec_state, want_feature_list);
1438 switch (stdio_helper_mode) {
1439 case GSS_SPNEGO_CLIENT:
1440 case GSS_SPNEGO_SERVER:
1441 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1442 if (!in.length) {
1443 first = true;
1445 break;
1446 case NTLMSSP_CLIENT_1:
1447 if (!in.length) {
1448 first = true;
1450 /* fall through */
1451 case SQUID_2_5_NTLMSSP:
1452 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1453 break;
1454 default:
1455 talloc_free(mem_ctx);
1456 abort();
1459 if (!NT_STATUS_IS_OK(nt_status)) {
1460 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1461 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1462 talloc_free(mem_ctx);
1463 return;
1468 /* update */
1470 if (strncmp(buf, "PW ", 3) == 0) {
1471 state->set_password = talloc_strndup(state,
1472 (const char *)in.data,
1473 in.length);
1475 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1476 state->set_password,
1477 CRED_SPECIFIED);
1478 x_fprintf(x_stdout, "OK\n");
1479 data_blob_free(&in);
1480 talloc_free(mem_ctx);
1481 return;
1484 if (strncmp(buf, "GK", 2) == 0) {
1485 char *base64_key;
1486 DEBUG(10, ("Requested session key\n"));
1487 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1488 if(!NT_STATUS_IS_OK(nt_status)) {
1489 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1490 x_fprintf(x_stdout, "BH No session key\n");
1491 talloc_free(mem_ctx);
1492 return;
1493 } else {
1494 base64_key = base64_encode_data_blob(state, session_key);
1495 SMB_ASSERT(base64_key != NULL);
1496 x_fprintf(x_stdout, "GK %s\n", base64_key);
1497 talloc_free(base64_key);
1499 talloc_free(mem_ctx);
1500 return;
1503 if (strncmp(buf, "GF", 2) == 0) {
1504 uint32_t neg_flags;
1506 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1508 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1509 if (neg_flags == 0) {
1510 x_fprintf(x_stdout, "BH\n");
1511 return;
1514 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1515 return;
1518 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1520 /* don't leak 'bad password'/'no such user' info to the network client */
1521 nt_status = nt_status_squash(nt_status);
1523 if (out.length) {
1524 out_base64 = base64_encode_data_blob(mem_ctx, out);
1525 SMB_ASSERT(out_base64 != NULL);
1526 } else {
1527 out_base64 = NULL;
1530 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1531 reply_arg = "*";
1532 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1533 reply_code = "YR";
1534 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1535 reply_code = "KK";
1536 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1537 reply_code = "TT";
1538 } else {
1539 abort();
1543 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1544 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1545 reply_arg = nt_errstr(nt_status);
1546 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1547 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1548 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1549 reply_arg = nt_errstr(nt_status);
1550 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1551 } else if (!NT_STATUS_IS_OK(nt_status)) {
1552 reply_code = "NA";
1553 reply_arg = nt_errstr(nt_status);
1554 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1555 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1556 struct auth_session_info *session_info;
1558 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1559 if (!NT_STATUS_IS_OK(nt_status)) {
1560 reply_code = "BH Failed to retrive session info";
1561 reply_arg = nt_errstr(nt_status);
1562 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1563 } else {
1565 reply_code = "AF";
1566 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1567 if (reply_arg == NULL) {
1568 reply_code = "BH out of memory";
1569 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1571 talloc_free(session_info);
1573 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1574 reply_code = "AF";
1575 reply_arg = out_base64;
1576 } else {
1577 abort();
1580 switch (stdio_helper_mode) {
1581 case GSS_SPNEGO_SERVER:
1582 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1583 out_base64 ? out_base64 : "*",
1584 reply_arg ? reply_arg : "*");
1585 break;
1586 default:
1587 if (out_base64) {
1588 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1589 } else if (reply_arg) {
1590 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1591 } else {
1592 x_fprintf(x_stdout, "%s\n", reply_code);
1596 talloc_free(mem_ctx);
1597 return;
1600 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1601 struct loadparm_context *lp_ctx,
1602 struct ntlm_auth_state *state,
1603 char *buf, int length, void **private2)
1605 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1606 return;
1609 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1610 struct loadparm_context *lp_ctx,
1611 struct ntlm_auth_state *state,
1612 char *buf, int length, void **private2)
1614 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1615 return;
1618 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1619 struct loadparm_context *lp_ctx,
1620 struct ntlm_auth_state *state,
1621 char *buf, int length, void **private2)
1623 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1624 return;
1627 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1628 struct loadparm_context *lp_ctx,
1629 struct ntlm_auth_state *state,
1630 char *buf, int length, void **private2)
1632 char *request, *parameter;
1633 static DATA_BLOB challenge;
1634 static DATA_BLOB lm_response;
1635 static DATA_BLOB nt_response;
1636 static char *full_username;
1637 static char *username;
1638 static char *domain;
1639 static char *plaintext_password;
1640 static bool ntlm_server_1_user_session_key;
1641 static bool ntlm_server_1_lm_session_key;
1643 if (strequal(buf, ".")) {
1644 if (!full_username && !username) {
1645 x_fprintf(x_stdout, "Error: No username supplied!\n");
1646 } else if (plaintext_password) {
1647 /* handle this request as plaintext */
1648 if (!full_username) {
1649 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1650 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1651 return;
1654 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1655 x_fprintf(x_stdout, "Authenticated: Yes\n");
1656 } else {
1657 x_fprintf(x_stdout, "Authenticated: No\n");
1659 } else if (!lm_response.data && !nt_response.data) {
1660 x_fprintf(x_stdout, "Error: No password supplied!\n");
1661 } else if (!challenge.data) {
1662 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
1663 } else {
1664 char *error_string = NULL;
1665 uchar lm_key[8];
1666 uchar user_session_key[16];
1667 uint32_t flags = 0;
1668 NTSTATUS nt_status;
1669 if (full_username && !username) {
1670 fstring fstr_user;
1671 fstring fstr_domain;
1673 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1674 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1675 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
1677 SAFE_FREE(username);
1678 SAFE_FREE(domain);
1679 username = smb_xstrdup(fstr_user);
1680 domain = smb_xstrdup(fstr_domain);
1683 if (opt_password) {
1684 DATA_BLOB nt_session_key, lm_session_key;
1685 struct samr_Password lm_pw, nt_pw;
1686 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1687 ZERO_STRUCT(user_session_key);
1688 ZERO_STRUCT(lm_key);
1690 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1691 nt_status = ntlm_password_check(mem_ctx,
1692 true, true, 0,
1693 &challenge,
1694 &lm_response,
1695 &nt_response,
1696 username,
1697 username,
1698 domain,
1699 &lm_pw, &nt_pw,
1700 &nt_session_key,
1701 &lm_session_key);
1702 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1703 if (ntlm_server_1_user_session_key) {
1704 if (nt_session_key.length == sizeof(user_session_key)) {
1705 memcpy(user_session_key,
1706 nt_session_key.data,
1707 sizeof(user_session_key));
1710 if (ntlm_server_1_lm_session_key) {
1711 if (lm_session_key.length == sizeof(lm_key)) {
1712 memcpy(lm_key,
1713 lm_session_key.data,
1714 sizeof(lm_key));
1717 TALLOC_FREE(mem_ctx);
1719 } else {
1720 if (!domain) {
1721 domain = smb_xstrdup(get_winbind_domain());
1724 if (ntlm_server_1_lm_session_key)
1725 flags |= WBFLAG_PAM_LMKEY;
1727 if (ntlm_server_1_user_session_key)
1728 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1730 nt_status = contact_winbind_auth_crap(username,
1731 domain,
1732 lp_netbios_name(),
1733 &challenge,
1734 &lm_response,
1735 &nt_response,
1736 flags, 0,
1737 lm_key,
1738 user_session_key,
1739 &error_string,
1740 NULL);
1743 if (!NT_STATUS_IS_OK(nt_status)) {
1744 x_fprintf(x_stdout, "Authenticated: No\n");
1745 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
1746 } else {
1747 static char zeros[16];
1748 char *hex_lm_key;
1749 char *hex_user_session_key;
1751 x_fprintf(x_stdout, "Authenticated: Yes\n");
1753 if (ntlm_server_1_lm_session_key
1754 && (memcmp(zeros, lm_key,
1755 sizeof(lm_key)) != 0)) {
1756 hex_lm_key = hex_encode_talloc(NULL,
1757 (const unsigned char *)lm_key,
1758 sizeof(lm_key));
1759 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
1760 TALLOC_FREE(hex_lm_key);
1763 if (ntlm_server_1_user_session_key
1764 && (memcmp(zeros, user_session_key,
1765 sizeof(user_session_key)) != 0)) {
1766 hex_user_session_key = hex_encode_talloc(NULL,
1767 (const unsigned char *)user_session_key,
1768 sizeof(user_session_key));
1769 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
1770 TALLOC_FREE(hex_user_session_key);
1773 SAFE_FREE(error_string);
1775 /* clear out the state */
1776 challenge = data_blob_null;
1777 nt_response = data_blob_null;
1778 lm_response = data_blob_null;
1779 SAFE_FREE(full_username);
1780 SAFE_FREE(username);
1781 SAFE_FREE(domain);
1782 SAFE_FREE(plaintext_password);
1783 ntlm_server_1_user_session_key = False;
1784 ntlm_server_1_lm_session_key = False;
1785 x_fprintf(x_stdout, ".\n");
1787 return;
1790 request = buf;
1792 /* Indicates a base64 encoded structure */
1793 parameter = strstr_m(request, ":: ");
1794 if (!parameter) {
1795 parameter = strstr_m(request, ": ");
1797 if (!parameter) {
1798 DEBUG(0, ("Parameter not found!\n"));
1799 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1800 return;
1803 parameter[0] ='\0';
1804 parameter++;
1805 parameter[0] ='\0';
1806 parameter++;
1808 } else {
1809 parameter[0] ='\0';
1810 parameter++;
1811 parameter[0] ='\0';
1812 parameter++;
1813 parameter[0] ='\0';
1814 parameter++;
1816 base64_decode_inplace(parameter);
1819 if (strequal(request, "LANMAN-Challenge")) {
1820 challenge = strhex_to_data_blob(NULL, parameter);
1821 if (challenge.length != 8) {
1822 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
1823 parameter,
1824 (int)challenge.length);
1825 challenge = data_blob_null;
1827 } else if (strequal(request, "NT-Response")) {
1828 nt_response = strhex_to_data_blob(NULL, parameter);
1829 if (nt_response.length < 24) {
1830 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
1831 parameter,
1832 (int)nt_response.length);
1833 nt_response = data_blob_null;
1835 } else if (strequal(request, "LANMAN-Response")) {
1836 lm_response = strhex_to_data_blob(NULL, parameter);
1837 if (lm_response.length != 24) {
1838 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
1839 parameter,
1840 (int)lm_response.length);
1841 lm_response = data_blob_null;
1843 } else if (strequal(request, "Password")) {
1844 plaintext_password = smb_xstrdup(parameter);
1845 } else if (strequal(request, "NT-Domain")) {
1846 domain = smb_xstrdup(parameter);
1847 } else if (strequal(request, "Username")) {
1848 username = smb_xstrdup(parameter);
1849 } else if (strequal(request, "Full-Username")) {
1850 full_username = smb_xstrdup(parameter);
1851 } else if (strequal(request, "Request-User-Session-Key")) {
1852 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1853 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1854 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1855 } else {
1856 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
1860 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
1861 struct loadparm_context *lp_ctx,
1862 struct ntlm_auth_state *state,
1863 char *buf, int length, void **private2)
1865 char *request, *parameter;
1866 static DATA_BLOB new_nt_pswd;
1867 static DATA_BLOB old_nt_hash_enc;
1868 static DATA_BLOB new_lm_pswd;
1869 static DATA_BLOB old_lm_hash_enc;
1870 static char *full_username = NULL;
1871 static char *username = NULL;
1872 static char *domain = NULL;
1873 static char *newpswd = NULL;
1874 static char *oldpswd = NULL;
1876 if (strequal(buf, ".")) {
1877 if(newpswd && oldpswd) {
1878 uchar old_nt_hash[16];
1879 uchar old_lm_hash[16];
1880 uchar new_nt_hash[16];
1881 uchar new_lm_hash[16];
1883 new_nt_pswd = data_blob(NULL, 516);
1884 old_nt_hash_enc = data_blob(NULL, 16);
1886 /* Calculate the MD4 hash (NT compatible) of the
1887 * password */
1888 E_md4hash(oldpswd, old_nt_hash);
1889 E_md4hash(newpswd, new_nt_hash);
1891 /* E_deshash returns false for 'long'
1892 passwords (> 14 DOS chars).
1894 Therefore, don't send a buffer
1895 encrypted with the truncated hash
1896 (it could allow an even easier
1897 attack on the password)
1899 Likewise, obey the admin's restriction
1902 if (lp_client_lanman_auth() &&
1903 E_deshash(newpswd, new_lm_hash) &&
1904 E_deshash(oldpswd, old_lm_hash)) {
1905 new_lm_pswd = data_blob(NULL, 516);
1906 old_lm_hash_enc = data_blob(NULL, 16);
1907 encode_pw_buffer(new_lm_pswd.data, newpswd,
1908 STR_UNICODE);
1910 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
1911 E_old_pw_hash(new_nt_hash, old_lm_hash,
1912 old_lm_hash_enc.data);
1913 } else {
1914 new_lm_pswd.data = NULL;
1915 new_lm_pswd.length = 0;
1916 old_lm_hash_enc.data = NULL;
1917 old_lm_hash_enc.length = 0;
1920 encode_pw_buffer(new_nt_pswd.data, newpswd,
1921 STR_UNICODE);
1923 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
1924 E_old_pw_hash(new_nt_hash, old_nt_hash,
1925 old_nt_hash_enc.data);
1928 if (!full_username && !username) {
1929 x_fprintf(x_stdout, "Error: No username supplied!\n");
1930 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1931 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1932 x_fprintf(x_stdout, "Error: No NT or LM password "
1933 "blobs supplied!\n");
1934 } else {
1935 char *error_string = NULL;
1937 if (full_username && !username) {
1938 fstring fstr_user;
1939 fstring fstr_domain;
1941 if (!parse_ntlm_auth_domain_user(full_username,
1942 fstr_user,
1943 fstr_domain)) {
1944 /* username might be 'tainted', don't
1945 * print into our new-line
1946 * deleimianted stream */
1947 x_fprintf(x_stdout, "Error: Could not "
1948 "parse into domain and "
1949 "username\n");
1950 SAFE_FREE(username);
1951 username = smb_xstrdup(full_username);
1952 } else {
1953 SAFE_FREE(username);
1954 SAFE_FREE(domain);
1955 username = smb_xstrdup(fstr_user);
1956 domain = smb_xstrdup(fstr_domain);
1961 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1962 username, domain,
1963 new_nt_pswd,
1964 old_nt_hash_enc,
1965 new_lm_pswd,
1966 old_lm_hash_enc,
1967 &error_string))) {
1968 x_fprintf(x_stdout, "Password-Change: No\n");
1969 x_fprintf(x_stdout, "Password-Change-Error: "
1970 "%s\n.\n", error_string);
1971 } else {
1972 x_fprintf(x_stdout, "Password-Change: Yes\n");
1975 SAFE_FREE(error_string);
1977 /* clear out the state */
1978 new_nt_pswd = data_blob_null;
1979 old_nt_hash_enc = data_blob_null;
1980 new_lm_pswd = data_blob_null;
1981 old_nt_hash_enc = data_blob_null;
1982 SAFE_FREE(full_username);
1983 SAFE_FREE(username);
1984 SAFE_FREE(domain);
1985 SAFE_FREE(newpswd);
1986 SAFE_FREE(oldpswd);
1987 x_fprintf(x_stdout, ".\n");
1989 return;
1992 request = buf;
1994 /* Indicates a base64 encoded structure */
1995 parameter = strstr_m(request, ":: ");
1996 if (!parameter) {
1997 parameter = strstr_m(request, ": ");
1999 if (!parameter) {
2000 DEBUG(0, ("Parameter not found!\n"));
2001 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2002 return;
2005 parameter[0] ='\0';
2006 parameter++;
2007 parameter[0] ='\0';
2008 parameter++;
2009 } else {
2010 parameter[0] ='\0';
2011 parameter++;
2012 parameter[0] ='\0';
2013 parameter++;
2014 parameter[0] ='\0';
2015 parameter++;
2017 base64_decode_inplace(parameter);
2020 if (strequal(request, "new-nt-password-blob")) {
2021 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2022 if (new_nt_pswd.length != 516) {
2023 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2024 "(got %d bytes, expected 516)\n.\n",
2025 parameter,
2026 (int)new_nt_pswd.length);
2027 new_nt_pswd = data_blob_null;
2029 } else if (strequal(request, "old-nt-hash-blob")) {
2030 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2031 if (old_nt_hash_enc.length != 16) {
2032 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2033 "(got %d bytes, expected 16)\n.\n",
2034 parameter,
2035 (int)old_nt_hash_enc.length);
2036 old_nt_hash_enc = data_blob_null;
2038 } else if (strequal(request, "new-lm-password-blob")) {
2039 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2040 if (new_lm_pswd.length != 516) {
2041 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2042 "(got %d bytes, expected 516)\n.\n",
2043 parameter,
2044 (int)new_lm_pswd.length);
2045 new_lm_pswd = data_blob_null;
2048 else if (strequal(request, "old-lm-hash-blob")) {
2049 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2050 if (old_lm_hash_enc.length != 16)
2052 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2053 "(got %d bytes, expected 16)\n.\n",
2054 parameter,
2055 (int)old_lm_hash_enc.length);
2056 old_lm_hash_enc = data_blob_null;
2058 } else if (strequal(request, "nt-domain")) {
2059 domain = smb_xstrdup(parameter);
2060 } else if(strequal(request, "username")) {
2061 username = smb_xstrdup(parameter);
2062 } else if(strequal(request, "full-username")) {
2063 username = smb_xstrdup(parameter);
2064 } else if(strequal(request, "new-password")) {
2065 newpswd = smb_xstrdup(parameter);
2066 } else if (strequal(request, "old-password")) {
2067 oldpswd = smb_xstrdup(parameter);
2068 } else {
2069 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2073 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2074 struct loadparm_context *lp_ctx,
2075 struct ntlm_auth_state *state,
2076 stdio_helper_function fn, void **private2)
2078 char *buf;
2079 char tmp[INITIAL_BUFFER_SIZE+1];
2080 int length, buf_size = 0;
2081 char *c;
2083 buf = talloc_strdup(state->mem_ctx, "");
2084 if (!buf) {
2085 DEBUG(0, ("Failed to allocate input buffer.\n"));
2086 x_fprintf(x_stderr, "ERR\n");
2087 exit(1);
2090 do {
2092 /* this is not a typo - x_fgets doesn't work too well under
2093 * squid */
2094 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2095 if (ferror(stdin)) {
2096 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2097 "(%s)\n", ferror(stdin),
2098 strerror(ferror(stdin))));
2100 exit(1);
2102 exit(0);
2105 buf = talloc_strdup_append_buffer(buf, tmp);
2106 buf_size += INITIAL_BUFFER_SIZE;
2108 if (buf_size > MAX_BUFFER_SIZE) {
2109 DEBUG(2, ("Oversized message\n"));
2110 x_fprintf(x_stderr, "ERR\n");
2111 talloc_free(buf);
2112 return;
2115 c = strchr(buf, '\n');
2116 } while (c == NULL);
2118 *c = '\0';
2119 length = c-buf;
2121 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2123 if (buf[0] == '\0') {
2124 DEBUG(2, ("Invalid Request\n"));
2125 x_fprintf(x_stderr, "ERR\n");
2126 talloc_free(buf);
2127 return;
2130 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2131 talloc_free(buf);
2135 static void squid_stream(enum stdio_helper_mode stdio_mode,
2136 struct loadparm_context *lp_ctx,
2137 stdio_helper_function fn) {
2138 TALLOC_CTX *mem_ctx;
2139 struct ntlm_auth_state *state;
2141 /* initialize FDescs */
2142 x_setbuf(x_stdout, NULL);
2143 x_setbuf(x_stderr, NULL);
2145 mem_ctx = talloc_init("ntlm_auth");
2146 if (!mem_ctx) {
2147 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2148 x_fprintf(x_stderr, "ERR\n");
2149 exit(1);
2152 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2153 if (!state) {
2154 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2155 x_fprintf(x_stderr, "ERR\n");
2156 exit(1);
2159 state->mem_ctx = mem_ctx;
2160 state->helper_mode = stdio_mode;
2162 while(1) {
2163 TALLOC_CTX *frame = talloc_stackframe();
2164 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2165 TALLOC_FREE(frame);
2170 /* Authenticate a user with a challenge/response */
2172 static bool check_auth_crap(void)
2174 NTSTATUS nt_status;
2175 uint32_t flags = 0;
2176 char lm_key[8];
2177 char user_session_key[16];
2178 char *hex_lm_key;
2179 char *hex_user_session_key;
2180 char *error_string;
2181 static uint8_t zeros[16];
2183 x_setbuf(x_stdout, NULL);
2185 if (request_lm_key)
2186 flags |= WBFLAG_PAM_LMKEY;
2188 if (request_user_session_key)
2189 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2191 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2193 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2194 opt_workstation,
2195 &opt_challenge,
2196 &opt_lm_response,
2197 &opt_nt_response,
2198 flags, 0,
2199 (unsigned char *)lm_key,
2200 (unsigned char *)user_session_key,
2201 &error_string, NULL);
2203 if (!NT_STATUS_IS_OK(nt_status)) {
2204 x_fprintf(x_stdout, "%s (0x%x)\n",
2205 error_string,
2206 NT_STATUS_V(nt_status));
2207 SAFE_FREE(error_string);
2208 return False;
2211 if (request_lm_key
2212 && (memcmp(zeros, lm_key,
2213 sizeof(lm_key)) != 0)) {
2214 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2215 sizeof(lm_key));
2216 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2217 TALLOC_FREE(hex_lm_key);
2219 if (request_user_session_key
2220 && (memcmp(zeros, user_session_key,
2221 sizeof(user_session_key)) != 0)) {
2222 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2223 sizeof(user_session_key));
2224 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2225 TALLOC_FREE(hex_user_session_key);
2228 return True;
2231 /* Main program */
2233 enum {
2234 OPT_USERNAME = 1000,
2235 OPT_DOMAIN,
2236 OPT_WORKSTATION,
2237 OPT_CHALLENGE,
2238 OPT_RESPONSE,
2239 OPT_LM,
2240 OPT_NT,
2241 OPT_PASSWORD,
2242 OPT_LM_KEY,
2243 OPT_USER_SESSION_KEY,
2244 OPT_DIAGNOSTICS,
2245 OPT_REQUIRE_MEMBERSHIP,
2246 OPT_USE_CACHED_CREDS,
2247 OPT_ALLOW_MSCHAPV2,
2248 OPT_PAM_WINBIND_CONF,
2249 OPT_TARGET_SERVICE,
2250 OPT_TARGET_HOSTNAME,
2251 OPT_OFFLINE_LOGON
2254 int main(int argc, const char **argv)
2256 TALLOC_CTX *frame = talloc_stackframe();
2257 int opt;
2258 const char *helper_protocol = NULL;
2259 int diagnostics = 0;
2261 const char *hex_challenge = NULL;
2262 const char *hex_lm_response = NULL;
2263 const char *hex_nt_response = NULL;
2264 struct loadparm_context *lp_ctx;
2265 poptContext pc;
2267 /* NOTE: DO NOT change this interface without considering the implications!
2268 This is an external interface, which other programs will use to interact
2269 with this helper.
2272 /* We do not use single-letter command abbreviations, because they harm future
2273 interface stability. */
2275 struct poptOption long_options[] = {
2276 POPT_AUTOHELP
2277 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2278 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2279 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2280 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2281 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2282 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2283 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2284 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2285 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2286 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2287 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2288 { "allow-mschapv2", 0, POPT_ARG_NONE, &opt_allow_mschapv2, OPT_ALLOW_MSCHAPV2, "Explicitly allow MSCHAPv2" },
2289 { "offline-logon", 0, POPT_ARG_NONE, &offline_logon,
2290 OPT_OFFLINE_LOGON,
2291 "Use cached passwords when DC is offline"},
2292 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2293 OPT_DIAGNOSTICS,
2294 "Perform diagnostics on the authentication chain"},
2295 { "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" },
2296 { "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" },
2297 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2298 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2299 POPT_COMMON_CONFIGFILE
2300 POPT_COMMON_VERSION
2301 POPT_COMMON_OPTION
2302 POPT_TABLEEND
2305 /* Samba client initialisation */
2306 smb_init_locale();
2308 setup_logging("ntlm_auth", DEBUG_STDERR);
2309 fault_setup();
2311 /* Parse options */
2313 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2315 /* Parse command line options */
2317 if (argc == 1) {
2318 poptPrintHelp(pc, stderr, 0);
2319 return 1;
2322 while((opt = poptGetNextOpt(pc)) != -1) {
2323 /* Get generic config options like --configfile */
2326 poptFreeContext(pc);
2328 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2329 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2330 get_dyn_CONFIGFILE(), strerror(errno));
2331 exit(1);
2334 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2335 POPT_CONTEXT_KEEP_FIRST);
2337 while((opt = poptGetNextOpt(pc)) != -1) {
2338 switch (opt) {
2339 case OPT_CHALLENGE:
2340 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2341 if (opt_challenge.length != 8) {
2342 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2343 hex_challenge,
2344 (int)opt_challenge.length);
2345 exit(1);
2347 break;
2348 case OPT_LM:
2349 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2350 if (opt_lm_response.length != 24) {
2351 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2352 hex_lm_response,
2353 (int)opt_lm_response.length);
2354 exit(1);
2356 break;
2358 case OPT_NT:
2359 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2360 if (opt_nt_response.length < 24) {
2361 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2362 hex_nt_response,
2363 (int)opt_nt_response.length);
2364 exit(1);
2366 break;
2368 case OPT_REQUIRE_MEMBERSHIP:
2369 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2370 require_membership_of_sid = require_membership_of;
2372 break;
2376 if (opt_username) {
2377 char *domain = SMB_STRDUP(opt_username);
2378 char *p = strchr_m(domain, *lp_winbind_separator());
2379 if (p) {
2380 opt_username = p+1;
2381 *p = '\0';
2382 if (opt_domain && !strequal(opt_domain, domain)) {
2383 x_fprintf(x_stderr, "Domain specified in username (%s) "
2384 "doesn't match specified domain (%s)!\n\n",
2385 domain, opt_domain);
2386 poptPrintHelp(pc, stderr, 0);
2387 exit(1);
2389 opt_domain = domain;
2390 } else {
2391 SAFE_FREE(domain);
2395 /* Note: if opt_domain is "" then send no domain */
2396 if (opt_domain == NULL) {
2397 opt_domain = get_winbind_domain();
2400 if (opt_workstation == NULL) {
2401 opt_workstation = "";
2404 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2405 if (lp_ctx == NULL) {
2406 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2407 exit(1);
2410 if (helper_protocol) {
2411 int i;
2412 for (i=0; i<NUM_HELPER_MODES; i++) {
2413 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2414 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2415 exit(0);
2418 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2420 for (i=0; i<NUM_HELPER_MODES; i++) {
2421 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2424 exit(1);
2427 if (!opt_username || !*opt_username) {
2428 x_fprintf(x_stderr, "username must be specified!\n\n");
2429 poptPrintHelp(pc, stderr, 0);
2430 exit(1);
2433 if (opt_challenge.length) {
2434 if (!check_auth_crap()) {
2435 exit(1);
2437 exit(0);
2440 if (!opt_password) {
2441 char pwd[256] = {0};
2442 int rc;
2444 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2445 if (rc == 0) {
2446 opt_password = SMB_STRDUP(pwd);
2450 if (diagnostics) {
2451 if (!diagnose_ntlm_auth()) {
2452 return 1;
2454 } else {
2455 fstring user;
2457 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2458 if (!check_plaintext_auth(user, opt_password, True)) {
2459 return 1;
2463 /* Exit code */
2465 poptFreeContext(pc);
2466 TALLOC_FREE(frame);
2467 return 0;