s3:ntlm_auth: call fault_setup() in order to get usefull backtraces
[Samba.git] / source3 / utils / ntlm_auth.c
blob05916d6aa08e3ec8134302d7ccbfb6907f8f54a7
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"
51 #if HAVE_KRB5
52 #include "auth/kerberos/pac_utils.h"
53 #endif
55 #ifndef PAM_WINBIND_CONFIG_FILE
56 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
57 #endif
59 #define WINBIND_KRB5_AUTH 0x00000080
61 #undef DBGC_CLASS
62 #define DBGC_CLASS DBGC_WINBIND
64 #define INITIAL_BUFFER_SIZE 300
65 #define MAX_BUFFER_SIZE 630000
67 enum stdio_helper_mode {
68 SQUID_2_4_BASIC,
69 SQUID_2_5_BASIC,
70 SQUID_2_5_NTLMSSP,
71 NTLMSSP_CLIENT_1,
72 GSS_SPNEGO_SERVER,
73 GSS_SPNEGO_CLIENT,
74 NTLM_SERVER_1,
75 NTLM_CHANGE_PASSWORD_1,
76 NUM_HELPER_MODES
79 enum ntlm_auth_cli_state {
80 CLIENT_INITIAL = 0,
81 CLIENT_RESPONSE,
82 CLIENT_FINISHED,
83 CLIENT_ERROR
86 struct ntlm_auth_state {
87 TALLOC_CTX *mem_ctx;
88 enum stdio_helper_mode helper_mode;
89 enum ntlm_auth_cli_state cli_state;
90 struct ntlmssp_state *ntlmssp_state;
91 uint32_t neg_flags;
92 char *want_feature_list;
93 bool have_session_key;
94 DATA_BLOB session_key;
95 DATA_BLOB initial_message;
96 void *gensec_private_1;
98 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
99 struct loadparm_context *lp_ctx,
100 struct ntlm_auth_state *state, char *buf,
101 int length, void **private2);
103 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
104 struct loadparm_context *lp_ctx,
105 char *buf, int length, void **private1);
107 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
108 struct loadparm_context *lp_ctx,
109 struct ntlm_auth_state *state,
110 stdio_helper_function fn, void **private2);
112 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
113 struct loadparm_context *lp_ctx,
114 struct ntlm_auth_state *state,
115 char *buf, int length, void **private2);
117 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
118 struct loadparm_context *lp_ctx,
119 struct ntlm_auth_state *state,
120 char *buf, int length, void **private2);
122 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
123 struct loadparm_context *lp_ctx,
124 struct ntlm_auth_state *state,
125 char *buf, int length, void **private2);
127 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
128 struct loadparm_context *lp_ctx,
129 struct ntlm_auth_state *state,
130 char *buf, int length, void **private2);
132 static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
133 struct loadparm_context *lp_ctx,
134 struct ntlm_auth_state *state,
135 char *buf, int length, void **private2);
137 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
138 struct loadparm_context *lp_ctx,
139 struct ntlm_auth_state *state,
140 char *buf, int length, void **private2);
142 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
143 struct loadparm_context *lp_ctx,
144 struct ntlm_auth_state *state,
145 char *buf, int length, void **private2);
147 static const struct {
148 enum stdio_helper_mode mode;
149 const char *name;
150 stdio_helper_function fn;
151 } stdio_helper_protocols[] = {
152 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
153 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
154 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
155 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
156 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
157 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
158 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
159 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
160 { NUM_HELPER_MODES, NULL, NULL}
163 const char *opt_username;
164 const char *opt_domain;
165 const char *opt_workstation;
166 const char *opt_password;
167 static DATA_BLOB opt_challenge;
168 static DATA_BLOB opt_lm_response;
169 static DATA_BLOB opt_nt_response;
170 static int request_lm_key;
171 static int request_user_session_key;
172 static int use_cached_creds;
173 static int offline_logon;
174 static int opt_allow_mschapv2;
176 static const char *require_membership_of;
177 static const char *require_membership_of_sid;
178 static const char *opt_pam_winbind_conf;
180 const char *opt_target_service;
181 const char *opt_target_hostname;
184 /* This is a bit hairy, but the basic idea is to do a password callback
185 to the calling application. The callback comes from within gensec */
187 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
188 struct loadparm_context *lp_ctx,
189 struct ntlm_auth_state *state, char *buf, int length,
190 void **password)
192 DATA_BLOB in;
193 if (strlen(buf) < 2) {
194 DEBUG(1, ("query [%s] invalid", buf));
195 x_fprintf(x_stdout, "BH Query invalid\n");
196 return;
199 if (strlen(buf) > 3) {
200 in = base64_decode_data_blob(buf + 3);
201 } else {
202 in = data_blob(NULL, 0);
205 if (strncmp(buf, "PW ", 3) == 0) {
207 *password = talloc_strndup(NULL,
208 (const char *)in.data, in.length);
210 if (*password == NULL) {
211 DEBUG(1, ("Out of memory\n"));
212 x_fprintf(x_stdout, "BH Out of memory\n");
213 data_blob_free(&in);
214 return;
217 x_fprintf(x_stdout, "OK\n");
218 data_blob_free(&in);
219 return;
221 DEBUG(1, ("Asked for (and expected) a password\n"));
222 x_fprintf(x_stdout, "BH Expected a password\n");
223 data_blob_free(&in);
227 * Callback for password credentials. This is not async, and when
228 * GENSEC and the credentials code is made async, it will look rather
229 * different.
232 static const char *get_password(struct cli_credentials *credentials)
234 TALLOC_CTX *frame = talloc_stackframe();
235 char *password = NULL;
236 struct ntlm_auth_state *state;
238 state = talloc_zero(frame, struct ntlm_auth_state);
239 if (state == NULL) {
240 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
241 x_fprintf(x_stderr, "ERR\n");
242 exit(1);
245 state->mem_ctx = state;
247 /* Ask for a password */
248 x_fprintf(x_stdout, "PW\n");
250 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, state, manage_gensec_get_pw_request, (void **)&password);
251 talloc_steal(credentials, password);
252 TALLOC_FREE(frame);
253 return password;
257 * A limited set of features are defined with text strings as needed
258 * by ntlm_auth
261 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
263 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
264 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
265 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
267 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
268 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
269 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
271 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
272 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
273 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
275 if (in_list("NTLMSSP_FEATURE_CCACHE", feature_list, true)) {
276 DEBUG(10, ("want GENSEC_FEATURE_NTLM_CCACHE\n"));
277 gensec_want_feature(state, GENSEC_FEATURE_NTLM_CCACHE);
281 static char winbind_separator(void)
283 struct winbindd_response response;
284 static bool got_sep;
285 static char sep;
287 if (got_sep)
288 return sep;
290 ZERO_STRUCT(response);
292 /* Send off request */
294 if (winbindd_request_response(NULL, WINBINDD_INFO, NULL, &response) !=
295 NSS_STATUS_SUCCESS) {
296 d_printf("could not obtain winbind separator!\n");
297 return *lp_winbind_separator();
300 sep = response.data.info.winbind_separator;
301 got_sep = True;
303 if (!sep) {
304 d_printf("winbind separator was NULL!\n");
305 return *lp_winbind_separator();
308 return sep;
311 const char *get_winbind_domain(void)
313 struct winbindd_response response;
315 static fstring winbind_domain;
316 if (*winbind_domain) {
317 return winbind_domain;
320 ZERO_STRUCT(response);
322 /* Send off request */
324 if (winbindd_request_response(NULL, WINBINDD_DOMAIN_NAME, NULL, &response) !=
325 NSS_STATUS_SUCCESS) {
326 DEBUG(1, ("could not obtain winbind domain name!\n"));
327 return lp_workgroup();
330 fstrcpy(winbind_domain, response.data.domain_name);
332 return winbind_domain;
336 const char *get_winbind_netbios_name(void)
338 struct winbindd_response response;
340 static fstring winbind_netbios_name;
342 if (*winbind_netbios_name) {
343 return winbind_netbios_name;
346 ZERO_STRUCT(response);
348 /* Send off request */
350 if (winbindd_request_response(NULL, WINBINDD_NETBIOS_NAME, NULL, &response) !=
351 NSS_STATUS_SUCCESS) {
352 DEBUG(1, ("could not obtain winbind netbios name!\n"));
353 return lp_netbios_name();
356 fstrcpy(winbind_netbios_name, response.data.netbios_name);
358 return winbind_netbios_name;
362 DATA_BLOB get_challenge(void)
364 static DATA_BLOB chal;
365 if (opt_challenge.length)
366 return opt_challenge;
368 chal = data_blob(NULL, 8);
370 generate_random_buffer(chal.data, chal.length);
371 return chal;
374 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
375 form DOMAIN/user into a domain and a user */
377 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
378 fstring user)
381 char *p = strchr(domuser,winbind_separator());
383 if (!p) {
384 return False;
387 fstrcpy(user, p+1);
388 fstrcpy(domain, domuser);
389 domain[PTR_DIFF(p, domuser)] = 0;
390 return strupper_m(domain);
393 static bool get_require_membership_sid(void) {
394 struct winbindd_request request;
395 struct winbindd_response response;
397 if (!require_membership_of) {
398 return True;
401 if (require_membership_of_sid) {
402 return True;
405 /* Otherwise, ask winbindd for the name->sid request */
407 ZERO_STRUCT(request);
408 ZERO_STRUCT(response);
410 if (!parse_ntlm_auth_domain_user(require_membership_of,
411 request.data.name.dom_name,
412 request.data.name.name)) {
413 DEBUG(0, ("Could not parse %s into separate domain/name parts!\n",
414 require_membership_of));
415 return False;
418 if (winbindd_request_response(NULL, WINBINDD_LOOKUPNAME, &request, &response) !=
419 NSS_STATUS_SUCCESS) {
420 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
421 require_membership_of));
422 return False;
425 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
427 if (require_membership_of_sid)
428 return True;
430 return False;
434 * Get some configuration from pam_winbind.conf to see if we
435 * need to contact trusted domain
438 int get_pam_winbind_config()
440 int ctrl = 0;
441 struct tiniparser_dictionary *d = NULL;
443 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
444 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
447 d = tiniparser_load(opt_pam_winbind_conf);
449 if (!d) {
450 return 0;
453 if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
454 ctrl |= WINBIND_KRB5_AUTH;
457 tiniparser_freedict(d);
459 return ctrl;
462 /* Authenticate a user with a plaintext password */
464 static bool check_plaintext_auth(const char *user, const char *pass,
465 bool stdout_diagnostics)
467 struct winbindd_request request;
468 struct winbindd_response response;
469 NSS_STATUS result;
471 if (!get_require_membership_sid()) {
472 return False;
475 /* Send off request */
477 ZERO_STRUCT(request);
478 ZERO_STRUCT(response);
480 fstrcpy(request.data.auth.user, user);
481 fstrcpy(request.data.auth.pass, pass);
482 if (require_membership_of_sid) {
483 strlcpy(request.data.auth.require_membership_of_sid,
484 require_membership_of_sid,
485 sizeof(request.data.auth.require_membership_of_sid));
488 if (offline_logon) {
489 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
492 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH, &request, &response);
494 /* Display response */
496 if (stdout_diagnostics) {
497 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
498 d_printf("Reading winbind reply failed! (0x01)\n");
501 d_printf("%s: %s (0x%x)\n",
502 response.data.auth.nt_status_string,
503 response.data.auth.error_string,
504 response.data.auth.nt_status);
505 } else {
506 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
507 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
510 DEBUG(3, ("%s: %s (0x%x)\n",
511 response.data.auth.nt_status_string,
512 response.data.auth.error_string,
513 response.data.auth.nt_status));
516 return (result == NSS_STATUS_SUCCESS);
519 /* authenticate a user with an encrypted username/password */
521 NTSTATUS contact_winbind_auth_crap(const char *username,
522 const char *domain,
523 const char *workstation,
524 const DATA_BLOB *challenge,
525 const DATA_BLOB *lm_response,
526 const DATA_BLOB *nt_response,
527 uint32_t flags,
528 uint32_t extra_logon_parameters,
529 uint8_t lm_key[8],
530 uint8_t user_session_key[16],
531 char **error_string,
532 char **unix_name)
534 NTSTATUS nt_status;
535 NSS_STATUS result;
536 struct winbindd_request request;
537 struct winbindd_response response;
539 if (!get_require_membership_sid()) {
540 return NT_STATUS_INVALID_PARAMETER;
543 ZERO_STRUCT(request);
544 ZERO_STRUCT(response);
546 request.flags = flags;
548 request.data.auth_crap.logon_parameters = extra_logon_parameters
549 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
551 if (opt_allow_mschapv2) {
552 request.data.auth_crap.logon_parameters |= MSV1_0_ALLOW_MSVCHAPV2;
555 if (require_membership_of_sid)
556 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
558 fstrcpy(request.data.auth_crap.user, username);
559 fstrcpy(request.data.auth_crap.domain, domain);
561 fstrcpy(request.data.auth_crap.workstation,
562 workstation);
564 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
566 if (lm_response && lm_response->length) {
567 memcpy(request.data.auth_crap.lm_resp,
568 lm_response->data,
569 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
570 request.data.auth_crap.lm_resp_len = lm_response->length;
573 if (nt_response && nt_response->length) {
574 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
575 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
576 request.extra_len = nt_response->length;
577 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
578 if (request.extra_data.data == NULL) {
579 return NT_STATUS_NO_MEMORY;
581 memcpy(request.extra_data.data, nt_response->data,
582 nt_response->length);
584 } else {
585 memcpy(request.data.auth_crap.nt_resp,
586 nt_response->data, nt_response->length);
588 request.data.auth_crap.nt_resp_len = nt_response->length;
591 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH_CRAP, &request, &response);
592 SAFE_FREE(request.extra_data.data);
594 /* Display response */
596 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
597 nt_status = NT_STATUS_UNSUCCESSFUL;
598 if (error_string)
599 *error_string = smb_xstrdup("Reading winbind reply failed!");
600 winbindd_free_response(&response);
601 return nt_status;
604 nt_status = (NT_STATUS(response.data.auth.nt_status));
605 if (!NT_STATUS_IS_OK(nt_status)) {
606 if (error_string)
607 *error_string = smb_xstrdup(response.data.auth.error_string);
608 winbindd_free_response(&response);
609 return nt_status;
612 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
613 memcpy(lm_key, response.data.auth.first_8_lm_hash,
614 sizeof(response.data.auth.first_8_lm_hash));
616 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
617 memcpy(user_session_key, response.data.auth.user_session_key,
618 sizeof(response.data.auth.user_session_key));
621 if (flags & WBFLAG_PAM_UNIX_NAME) {
622 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
623 if (!*unix_name) {
624 winbindd_free_response(&response);
625 return NT_STATUS_NO_MEMORY;
629 winbindd_free_response(&response);
630 return nt_status;
633 /* contact server to change user password using auth crap */
634 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
635 const char *domain,
636 const DATA_BLOB new_nt_pswd,
637 const DATA_BLOB old_nt_hash_enc,
638 const DATA_BLOB new_lm_pswd,
639 const DATA_BLOB old_lm_hash_enc,
640 char **error_string)
642 NTSTATUS nt_status;
643 NSS_STATUS result;
644 struct winbindd_request request;
645 struct winbindd_response response;
647 if (!get_require_membership_sid())
649 if(error_string)
650 *error_string = smb_xstrdup("Can't get membership sid.");
651 return NT_STATUS_INVALID_PARAMETER;
654 ZERO_STRUCT(request);
655 ZERO_STRUCT(response);
657 if(username != NULL)
658 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
659 if(domain != NULL)
660 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
662 if(new_nt_pswd.length)
664 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
665 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
668 if(old_nt_hash_enc.length)
670 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));
671 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
674 if(new_lm_pswd.length)
676 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
677 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
680 if(old_lm_hash_enc.length)
682 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));
683 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
686 result = winbindd_request_response(NULL, WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
688 /* Display response */
690 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
692 nt_status = NT_STATUS_UNSUCCESSFUL;
693 if (error_string)
694 *error_string = smb_xstrdup("Reading winbind reply failed!");
695 winbindd_free_response(&response);
696 return nt_status;
699 nt_status = (NT_STATUS(response.data.auth.nt_status));
700 if (!NT_STATUS_IS_OK(nt_status))
702 if (error_string)
703 *error_string = smb_xstrdup(response.data.auth.error_string);
704 winbindd_free_response(&response);
705 return nt_status;
708 winbindd_free_response(&response);
710 return nt_status;
713 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
714 TALLOC_CTX *mem_ctx,
715 void *server_returned_info,
716 const char *original_user_name,
717 uint32_t session_info_flags,
718 struct auth_session_info **session_info_out)
720 const char *unix_username = (const char *)server_returned_info;
721 bool ok;
722 struct dom_sid *sids = NULL;
723 struct auth_session_info *session_info = NULL;
725 session_info = talloc_zero(mem_ctx, struct auth_session_info);
726 if (session_info == NULL) {
727 return NT_STATUS_NO_MEMORY;
730 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
731 if (session_info->unix_info == NULL) {
732 TALLOC_FREE(session_info);
733 return NT_STATUS_NO_MEMORY;
735 session_info->unix_info->unix_name = talloc_strdup(session_info->unix_info,
736 unix_username);
737 if (session_info->unix_info->unix_name == NULL) {
738 TALLOC_FREE(session_info);
739 return NT_STATUS_NO_MEMORY;
742 session_info->security_token = talloc_zero(session_info, struct security_token);
743 if (session_info->security_token == NULL) {
744 TALLOC_FREE(session_info);
745 return NT_STATUS_NO_MEMORY;
748 sids = talloc_zero_array(session_info->security_token,
749 struct dom_sid, 3);
750 if (sids == NULL) {
751 TALLOC_FREE(session_info);
752 return NT_STATUS_NO_MEMORY;
754 ok = dom_sid_parse(SID_WORLD, &sids[0]);
755 if (!ok) {
756 TALLOC_FREE(session_info);
757 return NT_STATUS_INTERNAL_ERROR;
759 ok = dom_sid_parse(SID_NT_NETWORK, &sids[1]);
760 if (!ok) {
761 TALLOC_FREE(session_info);
762 return NT_STATUS_INTERNAL_ERROR;
764 ok = dom_sid_parse(SID_NT_AUTHENTICATED_USERS, &sids[2]);
765 if (!ok) {
766 TALLOC_FREE(session_info);
767 return NT_STATUS_INTERNAL_ERROR;
770 session_info->security_token->num_sids = talloc_array_length(sids);
771 session_info->security_token->sids = sids;
773 *session_info_out = session_info;
775 return NT_STATUS_OK;
778 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
779 TALLOC_CTX *mem_ctx,
780 struct smb_krb5_context *smb_krb5_context,
781 DATA_BLOB *pac_blob,
782 const char *princ_name,
783 const struct tsocket_address *remote_address,
784 uint32_t session_info_flags,
785 struct auth_session_info **session_info)
787 TALLOC_CTX *tmp_ctx;
788 struct PAC_LOGON_INFO *logon_info = NULL;
789 char *unixuser;
790 NTSTATUS status;
791 char *domain = NULL;
792 char *realm = NULL;
793 char *user = NULL;
794 char *p;
796 tmp_ctx = talloc_new(mem_ctx);
797 if (!tmp_ctx) {
798 return NT_STATUS_NO_MEMORY;
801 if (pac_blob) {
802 #ifdef HAVE_KRB5
803 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
804 NULL, NULL, 0, &logon_info);
805 #else
806 status = NT_STATUS_ACCESS_DENIED;
807 #endif
808 if (!NT_STATUS_IS_OK(status)) {
809 goto done;
813 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
815 p = strchr_m(princ_name, '@');
816 if (!p) {
817 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
818 princ_name));
819 return NT_STATUS_LOGON_FAILURE;
822 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
823 if (!user) {
824 return NT_STATUS_NO_MEMORY;
827 realm = talloc_strdup(talloc_tos(), p + 1);
828 if (!realm) {
829 return NT_STATUS_NO_MEMORY;
832 if (!strequal(realm, lp_realm())) {
833 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
834 if (!lp_allow_trusted_domains()) {
835 return NT_STATUS_LOGON_FAILURE;
839 if (logon_info && logon_info->info3.base.logon_domain.string) {
840 domain = talloc_strdup(mem_ctx,
841 logon_info->info3.base.logon_domain.string);
842 if (!domain) {
843 return NT_STATUS_NO_MEMORY;
845 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
846 } else {
848 /* If we have winbind running, we can (and must) shorten the
849 username by using the short netbios name. Otherwise we will
850 have inconsistent user names. With Kerberos, we get the
851 fully qualified realm, with ntlmssp we get the short
852 name. And even w2k3 does use ntlmssp if you for example
853 connect to an ip address. */
855 wbcErr wbc_status;
856 struct wbcDomainInfo *info = NULL;
858 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
859 realm));
861 wbc_status = wbcDomainInfo(realm, &info);
863 if (WBC_ERROR_IS_OK(wbc_status)) {
864 domain = talloc_strdup(mem_ctx,
865 info->short_name);
866 wbcFreeMemory(info);
867 } else {
868 DEBUG(3, ("Could not find short name: %s\n",
869 wbcErrorString(wbc_status)));
870 domain = talloc_strdup(mem_ctx, realm);
872 if (!domain) {
873 return NT_STATUS_NO_MEMORY;
875 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
878 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
879 if (!unixuser) {
880 status = NT_STATUS_NO_MEMORY;
881 goto done;
884 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
886 done:
887 TALLOC_FREE(tmp_ctx);
888 return status;
894 * Return the challenge as determined by the authentication subsystem
895 * @return an 8 byte random challenge
898 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
899 uint8_t chal[8])
901 if (auth_ctx->challenge.data.length == 8) {
902 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
903 auth_ctx->challenge.set_by));
904 memcpy(chal, auth_ctx->challenge.data.data, 8);
905 return NT_STATUS_OK;
908 if (!auth_ctx->challenge.set_by) {
909 generate_random_buffer(chal, 8);
911 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
912 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
913 auth_ctx->challenge.set_by = "random";
916 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
917 auth_ctx->challenge.set_by));
919 return NT_STATUS_OK;
923 * NTLM2 authentication modifies the effective challenge,
924 * @param challenge The new challenge value
926 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
928 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
929 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
931 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
932 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
934 return NT_STATUS_OK;
938 * Check the password on an NTLMSSP login.
940 * Return the session keys used on the connection.
943 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
944 TALLOC_CTX *mem_ctx,
945 const struct auth_usersupplied_info *user_info,
946 void **server_returned_info,
947 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
949 static const char zeros[16] = { 0, };
950 NTSTATUS nt_status;
951 char *error_string = NULL;
952 uint8_t lm_key[8];
953 uint8_t user_sess_key[16];
954 char *unix_name = NULL;
956 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
957 user_info->workstation_name,
958 &auth4_context->challenge.data,
959 &user_info->password.response.lanman,
960 &user_info->password.response.nt,
961 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
963 lm_key, user_sess_key,
964 &error_string, &unix_name);
966 if (NT_STATUS_IS_OK(nt_status)) {
967 if (memcmp(lm_key, zeros, 8) != 0) {
968 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
969 memcpy(lm_session_key->data, lm_key, 8);
970 memset(lm_session_key->data+8, '\0', 8);
973 if (memcmp(user_sess_key, zeros, 16) != 0) {
974 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
976 *server_returned_info = talloc_strdup(mem_ctx,
977 unix_name);
978 } else {
979 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
980 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
981 user_info->client.domain_name, user_info->client.account_name,
982 user_info->workstation_name,
983 error_string ? error_string : "unknown error (NULL)"));
986 SAFE_FREE(error_string);
987 SAFE_FREE(unix_name);
988 return nt_status;
991 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
992 TALLOC_CTX *mem_ctx,
993 const struct auth_usersupplied_info *user_info,
994 void **server_returned_info,
995 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
997 NTSTATUS nt_status;
998 struct samr_Password lm_pw, nt_pw;
1000 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1002 nt_status = ntlm_password_check(mem_ctx,
1003 true, true, 0,
1004 &auth4_context->challenge.data,
1005 &user_info->password.response.lanman,
1006 &user_info->password.response.nt,
1007 user_info->client.account_name,
1008 user_info->client.account_name,
1009 user_info->client.domain_name,
1010 &lm_pw, &nt_pw, session_key, lm_session_key);
1012 if (NT_STATUS_IS_OK(nt_status)) {
1013 *server_returned_info = talloc_asprintf(mem_ctx,
1014 "%s%c%s", user_info->client.domain_name,
1015 *lp_winbind_separator(),
1016 user_info->client.account_name);
1017 } else {
1018 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
1019 user_info->client.domain_name, user_info->client.account_name,
1020 user_info->workstation_name,
1021 nt_errstr(nt_status)));
1023 return nt_status;
1026 static NTSTATUS ntlm_auth_prepare_gensec_client(TALLOC_CTX *mem_ctx,
1027 struct loadparm_context *lp_ctx,
1028 struct gensec_security **gensec_security_out)
1030 struct gensec_security *gensec_security = NULL;
1031 NTSTATUS nt_status;
1032 TALLOC_CTX *tmp_ctx;
1033 const struct gensec_security_ops **backends = NULL;
1034 struct gensec_settings *gensec_settings = NULL;
1035 size_t idx = 0;
1037 tmp_ctx = talloc_new(mem_ctx);
1038 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1040 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1041 if (gensec_settings == NULL) {
1042 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1043 TALLOC_FREE(tmp_ctx);
1044 return NT_STATUS_NO_MEMORY;
1047 backends = talloc_zero_array(gensec_settings,
1048 const struct gensec_security_ops *, 4);
1049 if (backends == NULL) {
1050 TALLOC_FREE(tmp_ctx);
1051 return NT_STATUS_NO_MEMORY;
1053 gensec_settings->backends = backends;
1055 gensec_init();
1057 /* These need to be in priority order, krb5 before NTLMSSP */
1058 #if defined(HAVE_KRB5)
1059 backends[idx++] = &gensec_gse_krb5_security_ops;
1060 #endif
1062 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1064 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1066 nt_status = gensec_client_start(NULL, &gensec_security,
1067 gensec_settings);
1068 if (!NT_STATUS_IS_OK(nt_status)) {
1069 TALLOC_FREE(tmp_ctx);
1070 return nt_status;
1073 talloc_unlink(tmp_ctx, gensec_settings);
1075 if (opt_target_service != NULL) {
1076 nt_status = gensec_set_target_service(gensec_security,
1077 opt_target_service);
1078 if (!NT_STATUS_IS_OK(nt_status)) {
1079 TALLOC_FREE(tmp_ctx);
1080 return nt_status;
1084 if (opt_target_hostname != NULL) {
1085 nt_status = gensec_set_target_hostname(gensec_security,
1086 opt_target_hostname);
1087 if (!NT_STATUS_IS_OK(nt_status)) {
1088 TALLOC_FREE(tmp_ctx);
1089 return nt_status;
1093 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1094 TALLOC_FREE(tmp_ctx);
1095 return NT_STATUS_OK;
1098 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1100 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1101 if (auth4_context == NULL) {
1102 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1103 return NULL;
1105 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1106 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1107 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1108 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1109 if (local_pw) {
1110 auth4_context->check_ntlm_password = local_pw_check;
1111 } else {
1112 auth4_context->check_ntlm_password = winbind_pw_check;
1114 auth4_context->private_data = NULL;
1115 return auth4_context;
1118 static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx,
1119 struct loadparm_context *lp_ctx,
1120 struct gensec_security **gensec_security_out)
1122 struct gensec_security *gensec_security;
1123 NTSTATUS nt_status;
1125 TALLOC_CTX *tmp_ctx;
1126 const struct gensec_security_ops **backends;
1127 struct gensec_settings *gensec_settings;
1128 size_t idx = 0;
1129 struct cli_credentials *server_credentials;
1131 struct auth4_context *auth4_context;
1133 tmp_ctx = talloc_new(mem_ctx);
1134 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1136 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1137 if (auth4_context == NULL) {
1138 TALLOC_FREE(tmp_ctx);
1139 return NT_STATUS_NO_MEMORY;
1142 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1143 if (lp_ctx == NULL) {
1144 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1145 TALLOC_FREE(tmp_ctx);
1146 return NT_STATUS_NO_MEMORY;
1150 * This should be a 'netbios domain -> DNS domain'
1151 * mapping, and can currently validly return NULL on
1152 * poorly configured systems.
1154 * This is used for the NTLMSSP server
1157 if (opt_password) {
1158 gensec_settings->server_netbios_name = lp_netbios_name();
1159 gensec_settings->server_netbios_domain = lp_workgroup();
1160 } else {
1161 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1162 gensec_settings->server_netbios_domain = get_winbind_domain();
1165 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1166 get_mydnsdomname(talloc_tos()));
1167 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1168 get_mydnsfullname());
1170 backends = talloc_zero_array(gensec_settings,
1171 const struct gensec_security_ops *, 4);
1173 if (backends == NULL) {
1174 TALLOC_FREE(tmp_ctx);
1175 return NT_STATUS_NO_MEMORY;
1177 gensec_settings->backends = backends;
1179 gensec_init();
1181 /* These need to be in priority order, krb5 before NTLMSSP */
1182 #if defined(HAVE_KRB5)
1183 backends[idx++] = &gensec_gse_krb5_security_ops;
1184 #endif
1186 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1188 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1191 * This is anonymous for now, because we just use it
1192 * to set the kerberos state at the moment
1194 server_credentials = cli_credentials_init_anon(tmp_ctx);
1195 if (!server_credentials) {
1196 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1197 return NT_STATUS_NO_MEMORY;
1200 cli_credentials_set_conf(server_credentials, lp_ctx);
1202 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1203 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1204 } else {
1205 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1208 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1209 auth4_context, &gensec_security);
1211 if (!NT_STATUS_IS_OK(nt_status)) {
1212 TALLOC_FREE(tmp_ctx);
1213 return nt_status;
1216 gensec_set_credentials(gensec_security, server_credentials);
1218 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
1219 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
1221 talloc_unlink(tmp_ctx, lp_ctx);
1222 talloc_unlink(tmp_ctx, server_credentials);
1223 talloc_unlink(tmp_ctx, gensec_settings);
1224 talloc_unlink(tmp_ctx, auth4_context);
1226 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1227 TALLOC_FREE(tmp_ctx);
1228 return NT_STATUS_OK;
1231 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1232 struct loadparm_context *lp_ctx,
1233 struct ntlm_auth_state *state,
1234 char *buf, int length, void **private2)
1236 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1237 return;
1240 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1241 struct loadparm_context *lp_ctx,
1242 struct ntlm_auth_state *state,
1243 char *buf, int length, void **private2)
1245 char *user, *pass;
1246 user=buf;
1248 pass=(char *)memchr(buf,' ',length);
1249 if (!pass) {
1250 DEBUG(2, ("Password not found. Denying access\n"));
1251 x_fprintf(x_stdout, "ERR\n");
1252 return;
1254 *pass='\0';
1255 pass++;
1257 if (state->helper_mode == SQUID_2_5_BASIC) {
1258 rfc1738_unescape(user);
1259 rfc1738_unescape(pass);
1262 if (check_plaintext_auth(user, pass, False)) {
1263 x_fprintf(x_stdout, "OK\n");
1264 } else {
1265 x_fprintf(x_stdout, "ERR\n");
1269 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1270 struct loadparm_context *lp_ctx,
1271 char *buf, int length, void **private1)
1273 DATA_BLOB in;
1274 DATA_BLOB out = data_blob(NULL, 0);
1275 char *out_base64 = NULL;
1276 const char *reply_arg = NULL;
1277 struct gensec_ntlm_state {
1278 struct gensec_security *gensec_state;
1279 const char *set_password;
1281 struct gensec_ntlm_state *state;
1283 NTSTATUS nt_status;
1284 bool first = false;
1285 const char *reply_code;
1286 struct cli_credentials *creds;
1288 static char *want_feature_list = NULL;
1289 static DATA_BLOB session_key;
1291 TALLOC_CTX *mem_ctx;
1293 if (*private1) {
1294 state = (struct gensec_ntlm_state *)*private1;
1295 } else {
1296 state = talloc_zero(NULL, struct gensec_ntlm_state);
1297 if (!state) {
1298 x_fprintf(x_stdout, "BH No Memory\n");
1299 exit(1);
1301 *private1 = state;
1302 if (opt_password) {
1303 state->set_password = opt_password;
1307 if (strlen(buf) < 2) {
1308 DEBUG(1, ("query [%s] invalid", buf));
1309 x_fprintf(x_stdout, "BH Query invalid\n");
1310 return;
1313 if (strlen(buf) > 3) {
1314 if(strncmp(buf, "SF ", 3) == 0) {
1315 DEBUG(10, ("Setting flags to negotiate\n"));
1316 talloc_free(want_feature_list);
1317 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1318 x_fprintf(x_stdout, "OK\n");
1319 return;
1321 in = base64_decode_data_blob(buf + 3);
1322 } else {
1323 in = data_blob(NULL, 0);
1326 if (strncmp(buf, "YR", 2) == 0) {
1327 if (state->gensec_state) {
1328 talloc_free(state->gensec_state);
1329 state->gensec_state = NULL;
1331 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1332 /* Just return BH, like ntlm_auth from Samba 3 does. */
1333 x_fprintf(x_stdout, "BH Command expected\n");
1334 data_blob_free(&in);
1335 return;
1336 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1337 (strncmp(buf, "KK ", 3) != 0) &&
1338 (strncmp(buf, "AF ", 3) != 0) &&
1339 (strncmp(buf, "NA ", 3) != 0) &&
1340 (strncmp(buf, "UG", 2) != 0) &&
1341 (strncmp(buf, "PW ", 3) != 0) &&
1342 (strncmp(buf, "GK", 2) != 0) &&
1343 (strncmp(buf, "GF", 2) != 0)) {
1344 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1345 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1346 data_blob_free(&in);
1347 return;
1350 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1352 /* setup gensec */
1353 if (!(state->gensec_state)) {
1354 switch (stdio_helper_mode) {
1355 case GSS_SPNEGO_CLIENT:
1357 * cached credentials are only supported by
1358 * NTLMSSP_CLIENT_1 for now.
1360 use_cached_creds = false;
1361 /* fall through */
1362 case NTLMSSP_CLIENT_1:
1363 /* setup the client side */
1365 if (state->set_password != NULL) {
1366 use_cached_creds = false;
1369 if (use_cached_creds) {
1370 struct wbcCredentialCacheParams params;
1371 struct wbcCredentialCacheInfo *info = NULL;
1372 struct wbcAuthErrorInfo *error = NULL;
1373 wbcErr wbc_status;
1375 params.account_name = opt_username;
1376 params.domain_name = opt_domain;
1377 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1378 params.num_blobs = 0;
1379 params.blobs = NULL;
1381 wbc_status = wbcCredentialCache(&params, &info,
1382 &error);
1383 wbcFreeMemory(error);
1384 if (!WBC_ERROR_IS_OK(wbc_status)) {
1385 use_cached_creds = false;
1387 wbcFreeMemory(info);
1390 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1391 &state->gensec_state);
1392 if (!NT_STATUS_IS_OK(nt_status)) {
1393 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1394 talloc_free(mem_ctx);
1395 return;
1398 creds = cli_credentials_init(state->gensec_state);
1399 cli_credentials_set_conf(creds, lp_ctx);
1400 if (opt_username) {
1401 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1403 if (opt_domain) {
1404 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1406 if (use_cached_creds) {
1407 gensec_want_feature(state->gensec_state,
1408 GENSEC_FEATURE_NTLM_CCACHE);
1409 } else if (state->set_password) {
1410 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1411 } else {
1412 cli_credentials_set_password_callback(creds, get_password);
1414 if (opt_workstation) {
1415 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1418 gensec_set_credentials(state->gensec_state, creds);
1420 break;
1421 case GSS_SPNEGO_SERVER:
1422 case SQUID_2_5_NTLMSSP:
1424 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1425 &state->gensec_state);
1426 if (!NT_STATUS_IS_OK(nt_status)) {
1427 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1428 talloc_free(mem_ctx);
1429 return;
1431 break;
1433 default:
1434 talloc_free(mem_ctx);
1435 abort();
1438 gensec_want_feature_list(state->gensec_state, want_feature_list);
1440 switch (stdio_helper_mode) {
1441 case GSS_SPNEGO_CLIENT:
1442 case GSS_SPNEGO_SERVER:
1443 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1444 if (!in.length) {
1445 first = true;
1447 break;
1448 case NTLMSSP_CLIENT_1:
1449 if (!in.length) {
1450 first = true;
1452 /* fall through */
1453 case SQUID_2_5_NTLMSSP:
1454 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1455 break;
1456 default:
1457 talloc_free(mem_ctx);
1458 abort();
1461 if (!NT_STATUS_IS_OK(nt_status)) {
1462 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1463 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1464 talloc_free(mem_ctx);
1465 return;
1470 /* update */
1472 if (strncmp(buf, "PW ", 3) == 0) {
1473 state->set_password = talloc_strndup(state,
1474 (const char *)in.data,
1475 in.length);
1477 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1478 state->set_password,
1479 CRED_SPECIFIED);
1480 x_fprintf(x_stdout, "OK\n");
1481 data_blob_free(&in);
1482 talloc_free(mem_ctx);
1483 return;
1486 if (strncmp(buf, "GK", 2) == 0) {
1487 char *base64_key;
1488 DEBUG(10, ("Requested session key\n"));
1489 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1490 if(!NT_STATUS_IS_OK(nt_status)) {
1491 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1492 x_fprintf(x_stdout, "BH No session key\n");
1493 talloc_free(mem_ctx);
1494 return;
1495 } else {
1496 base64_key = base64_encode_data_blob(state, session_key);
1497 SMB_ASSERT(base64_key != NULL);
1498 x_fprintf(x_stdout, "GK %s\n", base64_key);
1499 talloc_free(base64_key);
1501 talloc_free(mem_ctx);
1502 return;
1505 if (strncmp(buf, "GF", 2) == 0) {
1506 uint32_t neg_flags;
1508 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1510 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1511 if (neg_flags == 0) {
1512 x_fprintf(x_stdout, "BH\n");
1513 return;
1516 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1517 return;
1520 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1522 /* don't leak 'bad password'/'no such user' info to the network client */
1523 nt_status = nt_status_squash(nt_status);
1525 if (out.length) {
1526 out_base64 = base64_encode_data_blob(mem_ctx, out);
1527 SMB_ASSERT(out_base64 != NULL);
1528 } else {
1529 out_base64 = NULL;
1532 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1533 reply_arg = "*";
1534 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1535 reply_code = "YR";
1536 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1537 reply_code = "KK";
1538 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1539 reply_code = "TT";
1540 } else {
1541 abort();
1545 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1546 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1547 reply_arg = nt_errstr(nt_status);
1548 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1549 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1550 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1551 reply_arg = nt_errstr(nt_status);
1552 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1553 } else if (!NT_STATUS_IS_OK(nt_status)) {
1554 reply_code = "NA";
1555 reply_arg = nt_errstr(nt_status);
1556 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1557 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1558 struct auth_session_info *session_info;
1560 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1561 if (!NT_STATUS_IS_OK(nt_status)) {
1562 reply_code = "BH Failed to retrive session info";
1563 reply_arg = nt_errstr(nt_status);
1564 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1565 } else {
1567 reply_code = "AF";
1568 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1569 if (reply_arg == NULL) {
1570 reply_code = "BH out of memory";
1571 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1573 talloc_free(session_info);
1575 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1576 reply_code = "AF";
1577 reply_arg = out_base64;
1578 } else {
1579 abort();
1582 switch (stdio_helper_mode) {
1583 case GSS_SPNEGO_SERVER:
1584 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1585 out_base64 ? out_base64 : "*",
1586 reply_arg ? reply_arg : "*");
1587 break;
1588 default:
1589 if (out_base64) {
1590 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1591 } else if (reply_arg) {
1592 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1593 } else {
1594 x_fprintf(x_stdout, "%s\n", reply_code);
1598 talloc_free(mem_ctx);
1599 return;
1602 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1603 struct loadparm_context *lp_ctx,
1604 struct ntlm_auth_state *state,
1605 char *buf, int length, void **private2)
1607 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1608 return;
1611 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1612 struct loadparm_context *lp_ctx,
1613 struct ntlm_auth_state *state,
1614 char *buf, int length, void **private2)
1616 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1617 return;
1620 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1621 struct loadparm_context *lp_ctx,
1622 struct ntlm_auth_state *state,
1623 char *buf, int length, void **private2)
1625 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1626 return;
1629 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1630 struct loadparm_context *lp_ctx,
1631 struct ntlm_auth_state *state,
1632 char *buf, int length, void **private2)
1634 char *request, *parameter;
1635 static DATA_BLOB challenge;
1636 static DATA_BLOB lm_response;
1637 static DATA_BLOB nt_response;
1638 static char *full_username;
1639 static char *username;
1640 static char *domain;
1641 static char *plaintext_password;
1642 static bool ntlm_server_1_user_session_key;
1643 static bool ntlm_server_1_lm_session_key;
1645 if (strequal(buf, ".")) {
1646 if (!full_username && !username) {
1647 x_fprintf(x_stdout, "Error: No username supplied!\n");
1648 } else if (plaintext_password) {
1649 /* handle this request as plaintext */
1650 if (!full_username) {
1651 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1652 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1653 return;
1656 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1657 x_fprintf(x_stdout, "Authenticated: Yes\n");
1658 } else {
1659 x_fprintf(x_stdout, "Authenticated: No\n");
1661 } else if (!lm_response.data && !nt_response.data) {
1662 x_fprintf(x_stdout, "Error: No password supplied!\n");
1663 } else if (!challenge.data) {
1664 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
1665 } else {
1666 char *error_string = NULL;
1667 uchar lm_key[8];
1668 uchar user_session_key[16];
1669 uint32_t flags = 0;
1670 NTSTATUS nt_status;
1671 if (full_username && !username) {
1672 fstring fstr_user;
1673 fstring fstr_domain;
1675 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1676 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1677 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
1679 SAFE_FREE(username);
1680 SAFE_FREE(domain);
1681 username = smb_xstrdup(fstr_user);
1682 domain = smb_xstrdup(fstr_domain);
1685 if (opt_password) {
1686 DATA_BLOB nt_session_key, lm_session_key;
1687 struct samr_Password lm_pw, nt_pw;
1688 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1689 ZERO_STRUCT(user_session_key);
1690 ZERO_STRUCT(lm_key);
1692 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1693 nt_status = ntlm_password_check(mem_ctx,
1694 true, true, 0,
1695 &challenge,
1696 &lm_response,
1697 &nt_response,
1698 username,
1699 username,
1700 domain,
1701 &lm_pw, &nt_pw,
1702 &nt_session_key,
1703 &lm_session_key);
1704 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1705 if (ntlm_server_1_user_session_key) {
1706 if (nt_session_key.length == sizeof(user_session_key)) {
1707 memcpy(user_session_key,
1708 nt_session_key.data,
1709 sizeof(user_session_key));
1712 if (ntlm_server_1_lm_session_key) {
1713 if (lm_session_key.length == sizeof(lm_key)) {
1714 memcpy(lm_key,
1715 lm_session_key.data,
1716 sizeof(lm_key));
1719 TALLOC_FREE(mem_ctx);
1721 } else {
1722 if (!domain) {
1723 domain = smb_xstrdup(get_winbind_domain());
1726 if (ntlm_server_1_lm_session_key)
1727 flags |= WBFLAG_PAM_LMKEY;
1729 if (ntlm_server_1_user_session_key)
1730 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1732 nt_status = contact_winbind_auth_crap(username,
1733 domain,
1734 lp_netbios_name(),
1735 &challenge,
1736 &lm_response,
1737 &nt_response,
1738 flags, 0,
1739 lm_key,
1740 user_session_key,
1741 &error_string,
1742 NULL);
1745 if (!NT_STATUS_IS_OK(nt_status)) {
1746 x_fprintf(x_stdout, "Authenticated: No\n");
1747 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
1748 } else {
1749 static char zeros[16];
1750 char *hex_lm_key;
1751 char *hex_user_session_key;
1753 x_fprintf(x_stdout, "Authenticated: Yes\n");
1755 if (ntlm_server_1_lm_session_key
1756 && (memcmp(zeros, lm_key,
1757 sizeof(lm_key)) != 0)) {
1758 hex_lm_key = hex_encode_talloc(NULL,
1759 (const unsigned char *)lm_key,
1760 sizeof(lm_key));
1761 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
1762 TALLOC_FREE(hex_lm_key);
1765 if (ntlm_server_1_user_session_key
1766 && (memcmp(zeros, user_session_key,
1767 sizeof(user_session_key)) != 0)) {
1768 hex_user_session_key = hex_encode_talloc(NULL,
1769 (const unsigned char *)user_session_key,
1770 sizeof(user_session_key));
1771 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
1772 TALLOC_FREE(hex_user_session_key);
1775 SAFE_FREE(error_string);
1777 /* clear out the state */
1778 challenge = data_blob_null;
1779 nt_response = data_blob_null;
1780 lm_response = data_blob_null;
1781 SAFE_FREE(full_username);
1782 SAFE_FREE(username);
1783 SAFE_FREE(domain);
1784 SAFE_FREE(plaintext_password);
1785 ntlm_server_1_user_session_key = False;
1786 ntlm_server_1_lm_session_key = False;
1787 x_fprintf(x_stdout, ".\n");
1789 return;
1792 request = buf;
1794 /* Indicates a base64 encoded structure */
1795 parameter = strstr_m(request, ":: ");
1796 if (!parameter) {
1797 parameter = strstr_m(request, ": ");
1799 if (!parameter) {
1800 DEBUG(0, ("Parameter not found!\n"));
1801 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1802 return;
1805 parameter[0] ='\0';
1806 parameter++;
1807 parameter[0] ='\0';
1808 parameter++;
1810 } else {
1811 parameter[0] ='\0';
1812 parameter++;
1813 parameter[0] ='\0';
1814 parameter++;
1815 parameter[0] ='\0';
1816 parameter++;
1818 base64_decode_inplace(parameter);
1821 if (strequal(request, "LANMAN-Challenge")) {
1822 challenge = strhex_to_data_blob(NULL, parameter);
1823 if (challenge.length != 8) {
1824 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
1825 parameter,
1826 (int)challenge.length);
1827 challenge = data_blob_null;
1829 } else if (strequal(request, "NT-Response")) {
1830 nt_response = strhex_to_data_blob(NULL, parameter);
1831 if (nt_response.length < 24) {
1832 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
1833 parameter,
1834 (int)nt_response.length);
1835 nt_response = data_blob_null;
1837 } else if (strequal(request, "LANMAN-Response")) {
1838 lm_response = strhex_to_data_blob(NULL, parameter);
1839 if (lm_response.length != 24) {
1840 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
1841 parameter,
1842 (int)lm_response.length);
1843 lm_response = data_blob_null;
1845 } else if (strequal(request, "Password")) {
1846 plaintext_password = smb_xstrdup(parameter);
1847 } else if (strequal(request, "NT-Domain")) {
1848 domain = smb_xstrdup(parameter);
1849 } else if (strequal(request, "Username")) {
1850 username = smb_xstrdup(parameter);
1851 } else if (strequal(request, "Full-Username")) {
1852 full_username = smb_xstrdup(parameter);
1853 } else if (strequal(request, "Request-User-Session-Key")) {
1854 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1855 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1856 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1857 } else {
1858 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
1862 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
1863 struct loadparm_context *lp_ctx,
1864 struct ntlm_auth_state *state,
1865 char *buf, int length, void **private2)
1867 char *request, *parameter;
1868 static DATA_BLOB new_nt_pswd;
1869 static DATA_BLOB old_nt_hash_enc;
1870 static DATA_BLOB new_lm_pswd;
1871 static DATA_BLOB old_lm_hash_enc;
1872 static char *full_username = NULL;
1873 static char *username = NULL;
1874 static char *domain = NULL;
1875 static char *newpswd = NULL;
1876 static char *oldpswd = NULL;
1878 if (strequal(buf, ".")) {
1879 if(newpswd && oldpswd) {
1880 uchar old_nt_hash[16];
1881 uchar old_lm_hash[16];
1882 uchar new_nt_hash[16];
1883 uchar new_lm_hash[16];
1885 new_nt_pswd = data_blob(NULL, 516);
1886 old_nt_hash_enc = data_blob(NULL, 16);
1888 /* Calculate the MD4 hash (NT compatible) of the
1889 * password */
1890 E_md4hash(oldpswd, old_nt_hash);
1891 E_md4hash(newpswd, new_nt_hash);
1893 /* E_deshash returns false for 'long'
1894 passwords (> 14 DOS chars).
1896 Therefore, don't send a buffer
1897 encrypted with the truncated hash
1898 (it could allow an even easier
1899 attack on the password)
1901 Likewise, obey the admin's restriction
1904 if (lp_client_lanman_auth() &&
1905 E_deshash(newpswd, new_lm_hash) &&
1906 E_deshash(oldpswd, old_lm_hash)) {
1907 new_lm_pswd = data_blob(NULL, 516);
1908 old_lm_hash_enc = data_blob(NULL, 16);
1909 encode_pw_buffer(new_lm_pswd.data, newpswd,
1910 STR_UNICODE);
1912 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
1913 E_old_pw_hash(new_nt_hash, old_lm_hash,
1914 old_lm_hash_enc.data);
1915 } else {
1916 new_lm_pswd.data = NULL;
1917 new_lm_pswd.length = 0;
1918 old_lm_hash_enc.data = NULL;
1919 old_lm_hash_enc.length = 0;
1922 encode_pw_buffer(new_nt_pswd.data, newpswd,
1923 STR_UNICODE);
1925 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
1926 E_old_pw_hash(new_nt_hash, old_nt_hash,
1927 old_nt_hash_enc.data);
1930 if (!full_username && !username) {
1931 x_fprintf(x_stdout, "Error: No username supplied!\n");
1932 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1933 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1934 x_fprintf(x_stdout, "Error: No NT or LM password "
1935 "blobs supplied!\n");
1936 } else {
1937 char *error_string = NULL;
1939 if (full_username && !username) {
1940 fstring fstr_user;
1941 fstring fstr_domain;
1943 if (!parse_ntlm_auth_domain_user(full_username,
1944 fstr_user,
1945 fstr_domain)) {
1946 /* username might be 'tainted', don't
1947 * print into our new-line
1948 * deleimianted stream */
1949 x_fprintf(x_stdout, "Error: Could not "
1950 "parse into domain and "
1951 "username\n");
1952 SAFE_FREE(username);
1953 username = smb_xstrdup(full_username);
1954 } else {
1955 SAFE_FREE(username);
1956 SAFE_FREE(domain);
1957 username = smb_xstrdup(fstr_user);
1958 domain = smb_xstrdup(fstr_domain);
1963 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1964 username, domain,
1965 new_nt_pswd,
1966 old_nt_hash_enc,
1967 new_lm_pswd,
1968 old_lm_hash_enc,
1969 &error_string))) {
1970 x_fprintf(x_stdout, "Password-Change: No\n");
1971 x_fprintf(x_stdout, "Password-Change-Error: "
1972 "%s\n.\n", error_string);
1973 } else {
1974 x_fprintf(x_stdout, "Password-Change: Yes\n");
1977 SAFE_FREE(error_string);
1979 /* clear out the state */
1980 new_nt_pswd = data_blob_null;
1981 old_nt_hash_enc = data_blob_null;
1982 new_lm_pswd = data_blob_null;
1983 old_nt_hash_enc = data_blob_null;
1984 SAFE_FREE(full_username);
1985 SAFE_FREE(username);
1986 SAFE_FREE(domain);
1987 SAFE_FREE(newpswd);
1988 SAFE_FREE(oldpswd);
1989 x_fprintf(x_stdout, ".\n");
1991 return;
1994 request = buf;
1996 /* Indicates a base64 encoded structure */
1997 parameter = strstr_m(request, ":: ");
1998 if (!parameter) {
1999 parameter = strstr_m(request, ": ");
2001 if (!parameter) {
2002 DEBUG(0, ("Parameter not found!\n"));
2003 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2004 return;
2007 parameter[0] ='\0';
2008 parameter++;
2009 parameter[0] ='\0';
2010 parameter++;
2011 } else {
2012 parameter[0] ='\0';
2013 parameter++;
2014 parameter[0] ='\0';
2015 parameter++;
2016 parameter[0] ='\0';
2017 parameter++;
2019 base64_decode_inplace(parameter);
2022 if (strequal(request, "new-nt-password-blob")) {
2023 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2024 if (new_nt_pswd.length != 516) {
2025 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2026 "(got %d bytes, expected 516)\n.\n",
2027 parameter,
2028 (int)new_nt_pswd.length);
2029 new_nt_pswd = data_blob_null;
2031 } else if (strequal(request, "old-nt-hash-blob")) {
2032 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2033 if (old_nt_hash_enc.length != 16) {
2034 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2035 "(got %d bytes, expected 16)\n.\n",
2036 parameter,
2037 (int)old_nt_hash_enc.length);
2038 old_nt_hash_enc = data_blob_null;
2040 } else if (strequal(request, "new-lm-password-blob")) {
2041 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2042 if (new_lm_pswd.length != 516) {
2043 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2044 "(got %d bytes, expected 516)\n.\n",
2045 parameter,
2046 (int)new_lm_pswd.length);
2047 new_lm_pswd = data_blob_null;
2050 else if (strequal(request, "old-lm-hash-blob")) {
2051 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2052 if (old_lm_hash_enc.length != 16)
2054 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2055 "(got %d bytes, expected 16)\n.\n",
2056 parameter,
2057 (int)old_lm_hash_enc.length);
2058 old_lm_hash_enc = data_blob_null;
2060 } else if (strequal(request, "nt-domain")) {
2061 domain = smb_xstrdup(parameter);
2062 } else if(strequal(request, "username")) {
2063 username = smb_xstrdup(parameter);
2064 } else if(strequal(request, "full-username")) {
2065 username = smb_xstrdup(parameter);
2066 } else if(strequal(request, "new-password")) {
2067 newpswd = smb_xstrdup(parameter);
2068 } else if (strequal(request, "old-password")) {
2069 oldpswd = smb_xstrdup(parameter);
2070 } else {
2071 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2075 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2076 struct loadparm_context *lp_ctx,
2077 struct ntlm_auth_state *state,
2078 stdio_helper_function fn, void **private2)
2080 char *buf;
2081 char tmp[INITIAL_BUFFER_SIZE+1];
2082 int length, buf_size = 0;
2083 char *c;
2085 buf = talloc_strdup(state->mem_ctx, "");
2086 if (!buf) {
2087 DEBUG(0, ("Failed to allocate input buffer.\n"));
2088 x_fprintf(x_stderr, "ERR\n");
2089 exit(1);
2092 do {
2094 /* this is not a typo - x_fgets doesn't work too well under
2095 * squid */
2096 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2097 if (ferror(stdin)) {
2098 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2099 "(%s)\n", ferror(stdin),
2100 strerror(ferror(stdin))));
2102 exit(1);
2104 exit(0);
2107 buf = talloc_strdup_append_buffer(buf, tmp);
2108 buf_size += INITIAL_BUFFER_SIZE;
2110 if (buf_size > MAX_BUFFER_SIZE) {
2111 DEBUG(2, ("Oversized message\n"));
2112 x_fprintf(x_stderr, "ERR\n");
2113 talloc_free(buf);
2114 return;
2117 c = strchr(buf, '\n');
2118 } while (c == NULL);
2120 *c = '\0';
2121 length = c-buf;
2123 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2125 if (buf[0] == '\0') {
2126 DEBUG(2, ("Invalid Request\n"));
2127 x_fprintf(x_stderr, "ERR\n");
2128 talloc_free(buf);
2129 return;
2132 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2133 talloc_free(buf);
2137 static void squid_stream(enum stdio_helper_mode stdio_mode,
2138 struct loadparm_context *lp_ctx,
2139 stdio_helper_function fn) {
2140 TALLOC_CTX *mem_ctx;
2141 struct ntlm_auth_state *state;
2143 /* initialize FDescs */
2144 x_setbuf(x_stdout, NULL);
2145 x_setbuf(x_stderr, NULL);
2147 mem_ctx = talloc_init("ntlm_auth");
2148 if (!mem_ctx) {
2149 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2150 x_fprintf(x_stderr, "ERR\n");
2151 exit(1);
2154 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2155 if (!state) {
2156 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2157 x_fprintf(x_stderr, "ERR\n");
2158 exit(1);
2161 state->mem_ctx = mem_ctx;
2162 state->helper_mode = stdio_mode;
2164 while(1) {
2165 TALLOC_CTX *frame = talloc_stackframe();
2166 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2167 TALLOC_FREE(frame);
2172 /* Authenticate a user with a challenge/response */
2174 static bool check_auth_crap(void)
2176 NTSTATUS nt_status;
2177 uint32_t flags = 0;
2178 char lm_key[8];
2179 char user_session_key[16];
2180 char *hex_lm_key;
2181 char *hex_user_session_key;
2182 char *error_string;
2183 static uint8_t zeros[16];
2185 x_setbuf(x_stdout, NULL);
2187 if (request_lm_key)
2188 flags |= WBFLAG_PAM_LMKEY;
2190 if (request_user_session_key)
2191 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2193 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2195 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2196 opt_workstation,
2197 &opt_challenge,
2198 &opt_lm_response,
2199 &opt_nt_response,
2200 flags, 0,
2201 (unsigned char *)lm_key,
2202 (unsigned char *)user_session_key,
2203 &error_string, NULL);
2205 if (!NT_STATUS_IS_OK(nt_status)) {
2206 x_fprintf(x_stdout, "%s (0x%x)\n",
2207 error_string,
2208 NT_STATUS_V(nt_status));
2209 SAFE_FREE(error_string);
2210 return False;
2213 if (request_lm_key
2214 && (memcmp(zeros, lm_key,
2215 sizeof(lm_key)) != 0)) {
2216 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2217 sizeof(lm_key));
2218 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2219 TALLOC_FREE(hex_lm_key);
2221 if (request_user_session_key
2222 && (memcmp(zeros, user_session_key,
2223 sizeof(user_session_key)) != 0)) {
2224 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2225 sizeof(user_session_key));
2226 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2227 TALLOC_FREE(hex_user_session_key);
2230 return True;
2233 /* Main program */
2235 enum {
2236 OPT_USERNAME = 1000,
2237 OPT_DOMAIN,
2238 OPT_WORKSTATION,
2239 OPT_CHALLENGE,
2240 OPT_RESPONSE,
2241 OPT_LM,
2242 OPT_NT,
2243 OPT_PASSWORD,
2244 OPT_LM_KEY,
2245 OPT_USER_SESSION_KEY,
2246 OPT_DIAGNOSTICS,
2247 OPT_REQUIRE_MEMBERSHIP,
2248 OPT_USE_CACHED_CREDS,
2249 OPT_ALLOW_MSCHAPV2,
2250 OPT_PAM_WINBIND_CONF,
2251 OPT_TARGET_SERVICE,
2252 OPT_TARGET_HOSTNAME,
2253 OPT_OFFLINE_LOGON
2256 int main(int argc, const char **argv)
2258 TALLOC_CTX *frame = talloc_stackframe();
2259 int opt;
2260 static const char *helper_protocol;
2261 static int diagnostics;
2263 static const char *hex_challenge;
2264 static const char *hex_lm_response;
2265 static const char *hex_nt_response;
2266 struct loadparm_context *lp_ctx;
2267 poptContext pc;
2269 /* NOTE: DO NOT change this interface without considering the implications!
2270 This is an external interface, which other programs will use to interact
2271 with this helper.
2274 /* We do not use single-letter command abbreviations, because they harm future
2275 interface stability. */
2277 struct poptOption long_options[] = {
2278 POPT_AUTOHELP
2279 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2280 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2281 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2282 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2283 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2284 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2285 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2286 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2287 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2288 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2289 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2290 { "allow-mschapv2", 0, POPT_ARG_NONE, &opt_allow_mschapv2, OPT_ALLOW_MSCHAPV2, "Explicitly allow MSCHAPv2" },
2291 { "offline-logon", 0, POPT_ARG_NONE, &offline_logon,
2292 OPT_OFFLINE_LOGON,
2293 "Use cached passwords when DC is offline"},
2294 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2295 OPT_DIAGNOSTICS,
2296 "Perform diagnostics on the authentication chain"},
2297 { "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" },
2298 { "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" },
2299 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2300 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2301 POPT_COMMON_CONFIGFILE
2302 POPT_COMMON_VERSION
2303 POPT_COMMON_OPTION
2304 POPT_TABLEEND
2307 /* Samba client initialisation */
2308 smb_init_locale();
2310 setup_logging("ntlm_auth", DEBUG_STDERR);
2311 fault_setup();
2313 /* Parse options */
2315 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2317 /* Parse command line options */
2319 if (argc == 1) {
2320 poptPrintHelp(pc, stderr, 0);
2321 return 1;
2324 while((opt = poptGetNextOpt(pc)) != -1) {
2325 /* Get generic config options like --configfile */
2328 poptFreeContext(pc);
2330 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2331 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2332 get_dyn_CONFIGFILE(), strerror(errno));
2333 exit(1);
2336 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2337 POPT_CONTEXT_KEEP_FIRST);
2339 while((opt = poptGetNextOpt(pc)) != -1) {
2340 switch (opt) {
2341 case OPT_CHALLENGE:
2342 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2343 if (opt_challenge.length != 8) {
2344 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2345 hex_challenge,
2346 (int)opt_challenge.length);
2347 exit(1);
2349 break;
2350 case OPT_LM:
2351 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2352 if (opt_lm_response.length != 24) {
2353 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2354 hex_lm_response,
2355 (int)opt_lm_response.length);
2356 exit(1);
2358 break;
2360 case OPT_NT:
2361 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2362 if (opt_nt_response.length < 24) {
2363 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2364 hex_nt_response,
2365 (int)opt_nt_response.length);
2366 exit(1);
2368 break;
2370 case OPT_REQUIRE_MEMBERSHIP:
2371 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2372 require_membership_of_sid = require_membership_of;
2374 break;
2378 if (opt_username) {
2379 char *domain = SMB_STRDUP(opt_username);
2380 char *p = strchr_m(domain, *lp_winbind_separator());
2381 if (p) {
2382 opt_username = p+1;
2383 *p = '\0';
2384 if (opt_domain && !strequal(opt_domain, domain)) {
2385 x_fprintf(x_stderr, "Domain specified in username (%s) "
2386 "doesn't match specified domain (%s)!\n\n",
2387 domain, opt_domain);
2388 poptPrintHelp(pc, stderr, 0);
2389 exit(1);
2391 opt_domain = domain;
2392 } else {
2393 SAFE_FREE(domain);
2397 /* Note: if opt_domain is "" then send no domain */
2398 if (opt_domain == NULL) {
2399 opt_domain = get_winbind_domain();
2402 if (opt_workstation == NULL) {
2403 opt_workstation = "";
2406 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2407 if (lp_ctx == NULL) {
2408 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2409 exit(1);
2412 if (helper_protocol) {
2413 int i;
2414 for (i=0; i<NUM_HELPER_MODES; i++) {
2415 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2416 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2417 exit(0);
2420 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2422 for (i=0; i<NUM_HELPER_MODES; i++) {
2423 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2426 exit(1);
2429 if (!opt_username || !*opt_username) {
2430 x_fprintf(x_stderr, "username must be specified!\n\n");
2431 poptPrintHelp(pc, stderr, 0);
2432 exit(1);
2435 if (opt_challenge.length) {
2436 if (!check_auth_crap()) {
2437 exit(1);
2439 exit(0);
2442 if (!opt_password) {
2443 char pwd[256] = {0};
2444 int rc;
2446 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2447 if (rc == 0) {
2448 opt_password = SMB_STRDUP(pwd);
2452 if (diagnostics) {
2453 if (!diagnose_ntlm_auth()) {
2454 return 1;
2456 } else {
2457 fstring user;
2459 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2460 if (!check_plaintext_auth(user, opt_password, True)) {
2461 return 1;
2465 /* Exit code */
2467 poptFreeContext(pc);
2468 TALLOC_FREE(frame);
2469 return 0;