s3:ntlm_auth: don't use gensec_want_feature(gensec_security, GENSEC_FEATURE_{SIGN...
[Samba.git] / source3 / utils / ntlm_auth.c
bloba5c1844cecdc0ce593f6471a58ad7c24f6a8407e
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 talloc_unlink(tmp_ctx, lp_ctx);
1219 talloc_unlink(tmp_ctx, server_credentials);
1220 talloc_unlink(tmp_ctx, gensec_settings);
1221 talloc_unlink(tmp_ctx, auth4_context);
1223 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1224 TALLOC_FREE(tmp_ctx);
1225 return NT_STATUS_OK;
1228 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1229 struct loadparm_context *lp_ctx,
1230 struct ntlm_auth_state *state,
1231 char *buf, int length, void **private2)
1233 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1234 return;
1237 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1238 struct loadparm_context *lp_ctx,
1239 struct ntlm_auth_state *state,
1240 char *buf, int length, void **private2)
1242 char *user, *pass;
1243 user=buf;
1245 pass=(char *)memchr(buf,' ',length);
1246 if (!pass) {
1247 DEBUG(2, ("Password not found. Denying access\n"));
1248 x_fprintf(x_stdout, "ERR\n");
1249 return;
1251 *pass='\0';
1252 pass++;
1254 if (state->helper_mode == SQUID_2_5_BASIC) {
1255 rfc1738_unescape(user);
1256 rfc1738_unescape(pass);
1259 if (check_plaintext_auth(user, pass, False)) {
1260 x_fprintf(x_stdout, "OK\n");
1261 } else {
1262 x_fprintf(x_stdout, "ERR\n");
1266 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1267 struct loadparm_context *lp_ctx,
1268 char *buf, int length, void **private1)
1270 DATA_BLOB in;
1271 DATA_BLOB out = data_blob(NULL, 0);
1272 char *out_base64 = NULL;
1273 const char *reply_arg = NULL;
1274 struct gensec_ntlm_state {
1275 struct gensec_security *gensec_state;
1276 const char *set_password;
1278 struct gensec_ntlm_state *state;
1280 NTSTATUS nt_status;
1281 bool first = false;
1282 const char *reply_code;
1283 struct cli_credentials *creds;
1285 static char *want_feature_list = NULL;
1286 static DATA_BLOB session_key;
1288 TALLOC_CTX *mem_ctx;
1290 if (*private1) {
1291 state = (struct gensec_ntlm_state *)*private1;
1292 } else {
1293 state = talloc_zero(NULL, struct gensec_ntlm_state);
1294 if (!state) {
1295 x_fprintf(x_stdout, "BH No Memory\n");
1296 exit(1);
1298 *private1 = state;
1299 if (opt_password) {
1300 state->set_password = opt_password;
1304 if (strlen(buf) < 2) {
1305 DEBUG(1, ("query [%s] invalid", buf));
1306 x_fprintf(x_stdout, "BH Query invalid\n");
1307 return;
1310 if (strlen(buf) > 3) {
1311 if(strncmp(buf, "SF ", 3) == 0) {
1312 DEBUG(10, ("Setting flags to negotiate\n"));
1313 talloc_free(want_feature_list);
1314 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1315 x_fprintf(x_stdout, "OK\n");
1316 return;
1318 in = base64_decode_data_blob(buf + 3);
1319 } else {
1320 in = data_blob(NULL, 0);
1323 if (strncmp(buf, "YR", 2) == 0) {
1324 if (state->gensec_state) {
1325 talloc_free(state->gensec_state);
1326 state->gensec_state = NULL;
1328 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1329 /* Just return BH, like ntlm_auth from Samba 3 does. */
1330 x_fprintf(x_stdout, "BH Command expected\n");
1331 data_blob_free(&in);
1332 return;
1333 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1334 (strncmp(buf, "KK ", 3) != 0) &&
1335 (strncmp(buf, "AF ", 3) != 0) &&
1336 (strncmp(buf, "NA ", 3) != 0) &&
1337 (strncmp(buf, "UG", 2) != 0) &&
1338 (strncmp(buf, "PW ", 3) != 0) &&
1339 (strncmp(buf, "GK", 2) != 0) &&
1340 (strncmp(buf, "GF", 2) != 0)) {
1341 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1342 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1343 data_blob_free(&in);
1344 return;
1347 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1349 /* setup gensec */
1350 if (!(state->gensec_state)) {
1351 switch (stdio_helper_mode) {
1352 case GSS_SPNEGO_CLIENT:
1354 * cached credentials are only supported by
1355 * NTLMSSP_CLIENT_1 for now.
1357 use_cached_creds = false;
1358 /* fall through */
1359 case NTLMSSP_CLIENT_1:
1360 /* setup the client side */
1362 if (state->set_password != NULL) {
1363 use_cached_creds = false;
1366 if (use_cached_creds) {
1367 struct wbcCredentialCacheParams params;
1368 struct wbcCredentialCacheInfo *info = NULL;
1369 struct wbcAuthErrorInfo *error = NULL;
1370 wbcErr wbc_status;
1372 params.account_name = opt_username;
1373 params.domain_name = opt_domain;
1374 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1375 params.num_blobs = 0;
1376 params.blobs = NULL;
1378 wbc_status = wbcCredentialCache(&params, &info,
1379 &error);
1380 wbcFreeMemory(error);
1381 if (!WBC_ERROR_IS_OK(wbc_status)) {
1382 use_cached_creds = false;
1384 wbcFreeMemory(info);
1387 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1388 &state->gensec_state);
1389 if (!NT_STATUS_IS_OK(nt_status)) {
1390 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1391 talloc_free(mem_ctx);
1392 return;
1395 creds = cli_credentials_init(state->gensec_state);
1396 cli_credentials_set_conf(creds, lp_ctx);
1397 if (opt_username) {
1398 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1400 if (opt_domain) {
1401 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1403 if (use_cached_creds) {
1404 gensec_want_feature(state->gensec_state,
1405 GENSEC_FEATURE_NTLM_CCACHE);
1406 } else if (state->set_password) {
1407 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1408 } else {
1409 cli_credentials_set_password_callback(creds, get_password);
1411 if (opt_workstation) {
1412 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1415 gensec_set_credentials(state->gensec_state, creds);
1417 break;
1418 case GSS_SPNEGO_SERVER:
1419 case SQUID_2_5_NTLMSSP:
1421 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1422 &state->gensec_state);
1423 if (!NT_STATUS_IS_OK(nt_status)) {
1424 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1425 talloc_free(mem_ctx);
1426 return;
1428 break;
1430 default:
1431 talloc_free(mem_ctx);
1432 abort();
1435 gensec_want_feature_list(state->gensec_state, want_feature_list);
1437 switch (stdio_helper_mode) {
1438 case GSS_SPNEGO_CLIENT:
1439 case GSS_SPNEGO_SERVER:
1440 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1441 if (!in.length) {
1442 first = true;
1444 break;
1445 case NTLMSSP_CLIENT_1:
1446 if (!in.length) {
1447 first = true;
1449 /* fall through */
1450 case SQUID_2_5_NTLMSSP:
1451 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1452 break;
1453 default:
1454 talloc_free(mem_ctx);
1455 abort();
1458 if (!NT_STATUS_IS_OK(nt_status)) {
1459 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1460 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1461 talloc_free(mem_ctx);
1462 return;
1467 /* update */
1469 if (strncmp(buf, "PW ", 3) == 0) {
1470 state->set_password = talloc_strndup(state,
1471 (const char *)in.data,
1472 in.length);
1474 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1475 state->set_password,
1476 CRED_SPECIFIED);
1477 x_fprintf(x_stdout, "OK\n");
1478 data_blob_free(&in);
1479 talloc_free(mem_ctx);
1480 return;
1483 if (strncmp(buf, "GK", 2) == 0) {
1484 char *base64_key;
1485 DEBUG(10, ("Requested session key\n"));
1486 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1487 if(!NT_STATUS_IS_OK(nt_status)) {
1488 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1489 x_fprintf(x_stdout, "BH No session key\n");
1490 talloc_free(mem_ctx);
1491 return;
1492 } else {
1493 base64_key = base64_encode_data_blob(state, session_key);
1494 SMB_ASSERT(base64_key != NULL);
1495 x_fprintf(x_stdout, "GK %s\n", base64_key);
1496 talloc_free(base64_key);
1498 talloc_free(mem_ctx);
1499 return;
1502 if (strncmp(buf, "GF", 2) == 0) {
1503 uint32_t neg_flags;
1505 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1507 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1508 if (neg_flags == 0) {
1509 x_fprintf(x_stdout, "BH\n");
1510 return;
1513 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1514 return;
1517 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1519 /* don't leak 'bad password'/'no such user' info to the network client */
1520 nt_status = nt_status_squash(nt_status);
1522 if (out.length) {
1523 out_base64 = base64_encode_data_blob(mem_ctx, out);
1524 SMB_ASSERT(out_base64 != NULL);
1525 } else {
1526 out_base64 = NULL;
1529 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1530 reply_arg = "*";
1531 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1532 reply_code = "YR";
1533 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1534 reply_code = "KK";
1535 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1536 reply_code = "TT";
1537 } else {
1538 abort();
1542 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1543 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1544 reply_arg = nt_errstr(nt_status);
1545 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1546 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1547 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1548 reply_arg = nt_errstr(nt_status);
1549 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1550 } else if (!NT_STATUS_IS_OK(nt_status)) {
1551 reply_code = "NA";
1552 reply_arg = nt_errstr(nt_status);
1553 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1554 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1555 struct auth_session_info *session_info;
1557 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1558 if (!NT_STATUS_IS_OK(nt_status)) {
1559 reply_code = "BH Failed to retrive session info";
1560 reply_arg = nt_errstr(nt_status);
1561 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1562 } else {
1564 reply_code = "AF";
1565 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1566 if (reply_arg == NULL) {
1567 reply_code = "BH out of memory";
1568 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1570 talloc_free(session_info);
1572 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1573 reply_code = "AF";
1574 reply_arg = out_base64;
1575 } else {
1576 abort();
1579 switch (stdio_helper_mode) {
1580 case GSS_SPNEGO_SERVER:
1581 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1582 out_base64 ? out_base64 : "*",
1583 reply_arg ? reply_arg : "*");
1584 break;
1585 default:
1586 if (out_base64) {
1587 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1588 } else if (reply_arg) {
1589 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1590 } else {
1591 x_fprintf(x_stdout, "%s\n", reply_code);
1595 talloc_free(mem_ctx);
1596 return;
1599 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1600 struct loadparm_context *lp_ctx,
1601 struct ntlm_auth_state *state,
1602 char *buf, int length, void **private2)
1604 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1605 return;
1608 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1609 struct loadparm_context *lp_ctx,
1610 struct ntlm_auth_state *state,
1611 char *buf, int length, void **private2)
1613 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1614 return;
1617 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1618 struct loadparm_context *lp_ctx,
1619 struct ntlm_auth_state *state,
1620 char *buf, int length, void **private2)
1622 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1623 return;
1626 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1627 struct loadparm_context *lp_ctx,
1628 struct ntlm_auth_state *state,
1629 char *buf, int length, void **private2)
1631 char *request, *parameter;
1632 static DATA_BLOB challenge;
1633 static DATA_BLOB lm_response;
1634 static DATA_BLOB nt_response;
1635 static char *full_username;
1636 static char *username;
1637 static char *domain;
1638 static char *plaintext_password;
1639 static bool ntlm_server_1_user_session_key;
1640 static bool ntlm_server_1_lm_session_key;
1642 if (strequal(buf, ".")) {
1643 if (!full_username && !username) {
1644 x_fprintf(x_stdout, "Error: No username supplied!\n");
1645 } else if (plaintext_password) {
1646 /* handle this request as plaintext */
1647 if (!full_username) {
1648 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1649 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1650 return;
1653 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1654 x_fprintf(x_stdout, "Authenticated: Yes\n");
1655 } else {
1656 x_fprintf(x_stdout, "Authenticated: No\n");
1658 } else if (!lm_response.data && !nt_response.data) {
1659 x_fprintf(x_stdout, "Error: No password supplied!\n");
1660 } else if (!challenge.data) {
1661 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
1662 } else {
1663 char *error_string = NULL;
1664 uchar lm_key[8];
1665 uchar user_session_key[16];
1666 uint32_t flags = 0;
1667 NTSTATUS nt_status;
1668 if (full_username && !username) {
1669 fstring fstr_user;
1670 fstring fstr_domain;
1672 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1673 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1674 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
1676 SAFE_FREE(username);
1677 SAFE_FREE(domain);
1678 username = smb_xstrdup(fstr_user);
1679 domain = smb_xstrdup(fstr_domain);
1682 if (opt_password) {
1683 DATA_BLOB nt_session_key, lm_session_key;
1684 struct samr_Password lm_pw, nt_pw;
1685 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1686 ZERO_STRUCT(user_session_key);
1687 ZERO_STRUCT(lm_key);
1689 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1690 nt_status = ntlm_password_check(mem_ctx,
1691 true, true, 0,
1692 &challenge,
1693 &lm_response,
1694 &nt_response,
1695 username,
1696 username,
1697 domain,
1698 &lm_pw, &nt_pw,
1699 &nt_session_key,
1700 &lm_session_key);
1701 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1702 if (ntlm_server_1_user_session_key) {
1703 if (nt_session_key.length == sizeof(user_session_key)) {
1704 memcpy(user_session_key,
1705 nt_session_key.data,
1706 sizeof(user_session_key));
1709 if (ntlm_server_1_lm_session_key) {
1710 if (lm_session_key.length == sizeof(lm_key)) {
1711 memcpy(lm_key,
1712 lm_session_key.data,
1713 sizeof(lm_key));
1716 TALLOC_FREE(mem_ctx);
1718 } else {
1719 if (!domain) {
1720 domain = smb_xstrdup(get_winbind_domain());
1723 if (ntlm_server_1_lm_session_key)
1724 flags |= WBFLAG_PAM_LMKEY;
1726 if (ntlm_server_1_user_session_key)
1727 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1729 nt_status = contact_winbind_auth_crap(username,
1730 domain,
1731 lp_netbios_name(),
1732 &challenge,
1733 &lm_response,
1734 &nt_response,
1735 flags, 0,
1736 lm_key,
1737 user_session_key,
1738 &error_string,
1739 NULL);
1742 if (!NT_STATUS_IS_OK(nt_status)) {
1743 x_fprintf(x_stdout, "Authenticated: No\n");
1744 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
1745 } else {
1746 static char zeros[16];
1747 char *hex_lm_key;
1748 char *hex_user_session_key;
1750 x_fprintf(x_stdout, "Authenticated: Yes\n");
1752 if (ntlm_server_1_lm_session_key
1753 && (memcmp(zeros, lm_key,
1754 sizeof(lm_key)) != 0)) {
1755 hex_lm_key = hex_encode_talloc(NULL,
1756 (const unsigned char *)lm_key,
1757 sizeof(lm_key));
1758 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
1759 TALLOC_FREE(hex_lm_key);
1762 if (ntlm_server_1_user_session_key
1763 && (memcmp(zeros, user_session_key,
1764 sizeof(user_session_key)) != 0)) {
1765 hex_user_session_key = hex_encode_talloc(NULL,
1766 (const unsigned char *)user_session_key,
1767 sizeof(user_session_key));
1768 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
1769 TALLOC_FREE(hex_user_session_key);
1772 SAFE_FREE(error_string);
1774 /* clear out the state */
1775 challenge = data_blob_null;
1776 nt_response = data_blob_null;
1777 lm_response = data_blob_null;
1778 SAFE_FREE(full_username);
1779 SAFE_FREE(username);
1780 SAFE_FREE(domain);
1781 SAFE_FREE(plaintext_password);
1782 ntlm_server_1_user_session_key = False;
1783 ntlm_server_1_lm_session_key = False;
1784 x_fprintf(x_stdout, ".\n");
1786 return;
1789 request = buf;
1791 /* Indicates a base64 encoded structure */
1792 parameter = strstr_m(request, ":: ");
1793 if (!parameter) {
1794 parameter = strstr_m(request, ": ");
1796 if (!parameter) {
1797 DEBUG(0, ("Parameter not found!\n"));
1798 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1799 return;
1802 parameter[0] ='\0';
1803 parameter++;
1804 parameter[0] ='\0';
1805 parameter++;
1807 } else {
1808 parameter[0] ='\0';
1809 parameter++;
1810 parameter[0] ='\0';
1811 parameter++;
1812 parameter[0] ='\0';
1813 parameter++;
1815 base64_decode_inplace(parameter);
1818 if (strequal(request, "LANMAN-Challenge")) {
1819 challenge = strhex_to_data_blob(NULL, parameter);
1820 if (challenge.length != 8) {
1821 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
1822 parameter,
1823 (int)challenge.length);
1824 challenge = data_blob_null;
1826 } else if (strequal(request, "NT-Response")) {
1827 nt_response = strhex_to_data_blob(NULL, parameter);
1828 if (nt_response.length < 24) {
1829 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
1830 parameter,
1831 (int)nt_response.length);
1832 nt_response = data_blob_null;
1834 } else if (strequal(request, "LANMAN-Response")) {
1835 lm_response = strhex_to_data_blob(NULL, parameter);
1836 if (lm_response.length != 24) {
1837 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
1838 parameter,
1839 (int)lm_response.length);
1840 lm_response = data_blob_null;
1842 } else if (strequal(request, "Password")) {
1843 plaintext_password = smb_xstrdup(parameter);
1844 } else if (strequal(request, "NT-Domain")) {
1845 domain = smb_xstrdup(parameter);
1846 } else if (strequal(request, "Username")) {
1847 username = smb_xstrdup(parameter);
1848 } else if (strequal(request, "Full-Username")) {
1849 full_username = smb_xstrdup(parameter);
1850 } else if (strequal(request, "Request-User-Session-Key")) {
1851 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1852 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1853 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1854 } else {
1855 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
1859 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
1860 struct loadparm_context *lp_ctx,
1861 struct ntlm_auth_state *state,
1862 char *buf, int length, void **private2)
1864 char *request, *parameter;
1865 static DATA_BLOB new_nt_pswd;
1866 static DATA_BLOB old_nt_hash_enc;
1867 static DATA_BLOB new_lm_pswd;
1868 static DATA_BLOB old_lm_hash_enc;
1869 static char *full_username = NULL;
1870 static char *username = NULL;
1871 static char *domain = NULL;
1872 static char *newpswd = NULL;
1873 static char *oldpswd = NULL;
1875 if (strequal(buf, ".")) {
1876 if(newpswd && oldpswd) {
1877 uchar old_nt_hash[16];
1878 uchar old_lm_hash[16];
1879 uchar new_nt_hash[16];
1880 uchar new_lm_hash[16];
1882 new_nt_pswd = data_blob(NULL, 516);
1883 old_nt_hash_enc = data_blob(NULL, 16);
1885 /* Calculate the MD4 hash (NT compatible) of the
1886 * password */
1887 E_md4hash(oldpswd, old_nt_hash);
1888 E_md4hash(newpswd, new_nt_hash);
1890 /* E_deshash returns false for 'long'
1891 passwords (> 14 DOS chars).
1893 Therefore, don't send a buffer
1894 encrypted with the truncated hash
1895 (it could allow an even easier
1896 attack on the password)
1898 Likewise, obey the admin's restriction
1901 if (lp_client_lanman_auth() &&
1902 E_deshash(newpswd, new_lm_hash) &&
1903 E_deshash(oldpswd, old_lm_hash)) {
1904 new_lm_pswd = data_blob(NULL, 516);
1905 old_lm_hash_enc = data_blob(NULL, 16);
1906 encode_pw_buffer(new_lm_pswd.data, newpswd,
1907 STR_UNICODE);
1909 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
1910 E_old_pw_hash(new_nt_hash, old_lm_hash,
1911 old_lm_hash_enc.data);
1912 } else {
1913 new_lm_pswd.data = NULL;
1914 new_lm_pswd.length = 0;
1915 old_lm_hash_enc.data = NULL;
1916 old_lm_hash_enc.length = 0;
1919 encode_pw_buffer(new_nt_pswd.data, newpswd,
1920 STR_UNICODE);
1922 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
1923 E_old_pw_hash(new_nt_hash, old_nt_hash,
1924 old_nt_hash_enc.data);
1927 if (!full_username && !username) {
1928 x_fprintf(x_stdout, "Error: No username supplied!\n");
1929 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1930 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1931 x_fprintf(x_stdout, "Error: No NT or LM password "
1932 "blobs supplied!\n");
1933 } else {
1934 char *error_string = NULL;
1936 if (full_username && !username) {
1937 fstring fstr_user;
1938 fstring fstr_domain;
1940 if (!parse_ntlm_auth_domain_user(full_username,
1941 fstr_user,
1942 fstr_domain)) {
1943 /* username might be 'tainted', don't
1944 * print into our new-line
1945 * deleimianted stream */
1946 x_fprintf(x_stdout, "Error: Could not "
1947 "parse into domain and "
1948 "username\n");
1949 SAFE_FREE(username);
1950 username = smb_xstrdup(full_username);
1951 } else {
1952 SAFE_FREE(username);
1953 SAFE_FREE(domain);
1954 username = smb_xstrdup(fstr_user);
1955 domain = smb_xstrdup(fstr_domain);
1960 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1961 username, domain,
1962 new_nt_pswd,
1963 old_nt_hash_enc,
1964 new_lm_pswd,
1965 old_lm_hash_enc,
1966 &error_string))) {
1967 x_fprintf(x_stdout, "Password-Change: No\n");
1968 x_fprintf(x_stdout, "Password-Change-Error: "
1969 "%s\n.\n", error_string);
1970 } else {
1971 x_fprintf(x_stdout, "Password-Change: Yes\n");
1974 SAFE_FREE(error_string);
1976 /* clear out the state */
1977 new_nt_pswd = data_blob_null;
1978 old_nt_hash_enc = data_blob_null;
1979 new_lm_pswd = data_blob_null;
1980 old_nt_hash_enc = data_blob_null;
1981 SAFE_FREE(full_username);
1982 SAFE_FREE(username);
1983 SAFE_FREE(domain);
1984 SAFE_FREE(newpswd);
1985 SAFE_FREE(oldpswd);
1986 x_fprintf(x_stdout, ".\n");
1988 return;
1991 request = buf;
1993 /* Indicates a base64 encoded structure */
1994 parameter = strstr_m(request, ":: ");
1995 if (!parameter) {
1996 parameter = strstr_m(request, ": ");
1998 if (!parameter) {
1999 DEBUG(0, ("Parameter not found!\n"));
2000 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2001 return;
2004 parameter[0] ='\0';
2005 parameter++;
2006 parameter[0] ='\0';
2007 parameter++;
2008 } else {
2009 parameter[0] ='\0';
2010 parameter++;
2011 parameter[0] ='\0';
2012 parameter++;
2013 parameter[0] ='\0';
2014 parameter++;
2016 base64_decode_inplace(parameter);
2019 if (strequal(request, "new-nt-password-blob")) {
2020 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2021 if (new_nt_pswd.length != 516) {
2022 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2023 "(got %d bytes, expected 516)\n.\n",
2024 parameter,
2025 (int)new_nt_pswd.length);
2026 new_nt_pswd = data_blob_null;
2028 } else if (strequal(request, "old-nt-hash-blob")) {
2029 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2030 if (old_nt_hash_enc.length != 16) {
2031 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2032 "(got %d bytes, expected 16)\n.\n",
2033 parameter,
2034 (int)old_nt_hash_enc.length);
2035 old_nt_hash_enc = data_blob_null;
2037 } else if (strequal(request, "new-lm-password-blob")) {
2038 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2039 if (new_lm_pswd.length != 516) {
2040 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2041 "(got %d bytes, expected 516)\n.\n",
2042 parameter,
2043 (int)new_lm_pswd.length);
2044 new_lm_pswd = data_blob_null;
2047 else if (strequal(request, "old-lm-hash-blob")) {
2048 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2049 if (old_lm_hash_enc.length != 16)
2051 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2052 "(got %d bytes, expected 16)\n.\n",
2053 parameter,
2054 (int)old_lm_hash_enc.length);
2055 old_lm_hash_enc = data_blob_null;
2057 } else if (strequal(request, "nt-domain")) {
2058 domain = smb_xstrdup(parameter);
2059 } else if(strequal(request, "username")) {
2060 username = smb_xstrdup(parameter);
2061 } else if(strequal(request, "full-username")) {
2062 username = smb_xstrdup(parameter);
2063 } else if(strequal(request, "new-password")) {
2064 newpswd = smb_xstrdup(parameter);
2065 } else if (strequal(request, "old-password")) {
2066 oldpswd = smb_xstrdup(parameter);
2067 } else {
2068 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2072 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2073 struct loadparm_context *lp_ctx,
2074 struct ntlm_auth_state *state,
2075 stdio_helper_function fn, void **private2)
2077 char *buf;
2078 char tmp[INITIAL_BUFFER_SIZE+1];
2079 int length, buf_size = 0;
2080 char *c;
2082 buf = talloc_strdup(state->mem_ctx, "");
2083 if (!buf) {
2084 DEBUG(0, ("Failed to allocate input buffer.\n"));
2085 x_fprintf(x_stderr, "ERR\n");
2086 exit(1);
2089 do {
2091 /* this is not a typo - x_fgets doesn't work too well under
2092 * squid */
2093 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2094 if (ferror(stdin)) {
2095 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2096 "(%s)\n", ferror(stdin),
2097 strerror(ferror(stdin))));
2099 exit(1);
2101 exit(0);
2104 buf = talloc_strdup_append_buffer(buf, tmp);
2105 buf_size += INITIAL_BUFFER_SIZE;
2107 if (buf_size > MAX_BUFFER_SIZE) {
2108 DEBUG(2, ("Oversized message\n"));
2109 x_fprintf(x_stderr, "ERR\n");
2110 talloc_free(buf);
2111 return;
2114 c = strchr(buf, '\n');
2115 } while (c == NULL);
2117 *c = '\0';
2118 length = c-buf;
2120 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2122 if (buf[0] == '\0') {
2123 DEBUG(2, ("Invalid Request\n"));
2124 x_fprintf(x_stderr, "ERR\n");
2125 talloc_free(buf);
2126 return;
2129 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2130 talloc_free(buf);
2134 static void squid_stream(enum stdio_helper_mode stdio_mode,
2135 struct loadparm_context *lp_ctx,
2136 stdio_helper_function fn) {
2137 TALLOC_CTX *mem_ctx;
2138 struct ntlm_auth_state *state;
2140 /* initialize FDescs */
2141 x_setbuf(x_stdout, NULL);
2142 x_setbuf(x_stderr, NULL);
2144 mem_ctx = talloc_init("ntlm_auth");
2145 if (!mem_ctx) {
2146 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2147 x_fprintf(x_stderr, "ERR\n");
2148 exit(1);
2151 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2152 if (!state) {
2153 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2154 x_fprintf(x_stderr, "ERR\n");
2155 exit(1);
2158 state->mem_ctx = mem_ctx;
2159 state->helper_mode = stdio_mode;
2161 while(1) {
2162 TALLOC_CTX *frame = talloc_stackframe();
2163 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2164 TALLOC_FREE(frame);
2169 /* Authenticate a user with a challenge/response */
2171 static bool check_auth_crap(void)
2173 NTSTATUS nt_status;
2174 uint32_t flags = 0;
2175 char lm_key[8];
2176 char user_session_key[16];
2177 char *hex_lm_key;
2178 char *hex_user_session_key;
2179 char *error_string;
2180 static uint8_t zeros[16];
2182 x_setbuf(x_stdout, NULL);
2184 if (request_lm_key)
2185 flags |= WBFLAG_PAM_LMKEY;
2187 if (request_user_session_key)
2188 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2190 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2192 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2193 opt_workstation,
2194 &opt_challenge,
2195 &opt_lm_response,
2196 &opt_nt_response,
2197 flags, 0,
2198 (unsigned char *)lm_key,
2199 (unsigned char *)user_session_key,
2200 &error_string, NULL);
2202 if (!NT_STATUS_IS_OK(nt_status)) {
2203 x_fprintf(x_stdout, "%s (0x%x)\n",
2204 error_string,
2205 NT_STATUS_V(nt_status));
2206 SAFE_FREE(error_string);
2207 return False;
2210 if (request_lm_key
2211 && (memcmp(zeros, lm_key,
2212 sizeof(lm_key)) != 0)) {
2213 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2214 sizeof(lm_key));
2215 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2216 TALLOC_FREE(hex_lm_key);
2218 if (request_user_session_key
2219 && (memcmp(zeros, user_session_key,
2220 sizeof(user_session_key)) != 0)) {
2221 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2222 sizeof(user_session_key));
2223 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2224 TALLOC_FREE(hex_user_session_key);
2227 return True;
2230 /* Main program */
2232 enum {
2233 OPT_USERNAME = 1000,
2234 OPT_DOMAIN,
2235 OPT_WORKSTATION,
2236 OPT_CHALLENGE,
2237 OPT_RESPONSE,
2238 OPT_LM,
2239 OPT_NT,
2240 OPT_PASSWORD,
2241 OPT_LM_KEY,
2242 OPT_USER_SESSION_KEY,
2243 OPT_DIAGNOSTICS,
2244 OPT_REQUIRE_MEMBERSHIP,
2245 OPT_USE_CACHED_CREDS,
2246 OPT_ALLOW_MSCHAPV2,
2247 OPT_PAM_WINBIND_CONF,
2248 OPT_TARGET_SERVICE,
2249 OPT_TARGET_HOSTNAME,
2250 OPT_OFFLINE_LOGON
2253 int main(int argc, const char **argv)
2255 TALLOC_CTX *frame = talloc_stackframe();
2256 int opt;
2257 static const char *helper_protocol;
2258 static int diagnostics;
2260 static const char *hex_challenge;
2261 static const char *hex_lm_response;
2262 static const char *hex_nt_response;
2263 struct loadparm_context *lp_ctx;
2264 poptContext pc;
2266 /* NOTE: DO NOT change this interface without considering the implications!
2267 This is an external interface, which other programs will use to interact
2268 with this helper.
2271 /* We do not use single-letter command abbreviations, because they harm future
2272 interface stability. */
2274 struct poptOption long_options[] = {
2275 POPT_AUTOHELP
2276 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2277 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2278 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2279 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2280 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2281 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2282 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2283 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2284 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2285 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2286 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2287 { "allow-mschapv2", 0, POPT_ARG_NONE, &opt_allow_mschapv2, OPT_ALLOW_MSCHAPV2, "Explicitly allow MSCHAPv2" },
2288 { "offline-logon", 0, POPT_ARG_NONE, &offline_logon,
2289 OPT_OFFLINE_LOGON,
2290 "Use cached passwords when DC is offline"},
2291 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2292 OPT_DIAGNOSTICS,
2293 "Perform diagnostics on the authentication chain"},
2294 { "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" },
2295 { "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" },
2296 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2297 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2298 POPT_COMMON_CONFIGFILE
2299 POPT_COMMON_VERSION
2300 POPT_COMMON_OPTION
2301 POPT_TABLEEND
2304 /* Samba client initialisation */
2305 smb_init_locale();
2307 setup_logging("ntlm_auth", DEBUG_STDERR);
2308 fault_setup();
2310 /* Parse options */
2312 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2314 /* Parse command line options */
2316 if (argc == 1) {
2317 poptPrintHelp(pc, stderr, 0);
2318 return 1;
2321 while((opt = poptGetNextOpt(pc)) != -1) {
2322 /* Get generic config options like --configfile */
2325 poptFreeContext(pc);
2327 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2328 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2329 get_dyn_CONFIGFILE(), strerror(errno));
2330 exit(1);
2333 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2334 POPT_CONTEXT_KEEP_FIRST);
2336 while((opt = poptGetNextOpt(pc)) != -1) {
2337 switch (opt) {
2338 case OPT_CHALLENGE:
2339 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2340 if (opt_challenge.length != 8) {
2341 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2342 hex_challenge,
2343 (int)opt_challenge.length);
2344 exit(1);
2346 break;
2347 case OPT_LM:
2348 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2349 if (opt_lm_response.length != 24) {
2350 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2351 hex_lm_response,
2352 (int)opt_lm_response.length);
2353 exit(1);
2355 break;
2357 case OPT_NT:
2358 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2359 if (opt_nt_response.length < 24) {
2360 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2361 hex_nt_response,
2362 (int)opt_nt_response.length);
2363 exit(1);
2365 break;
2367 case OPT_REQUIRE_MEMBERSHIP:
2368 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2369 require_membership_of_sid = require_membership_of;
2371 break;
2375 if (opt_username) {
2376 char *domain = SMB_STRDUP(opt_username);
2377 char *p = strchr_m(domain, *lp_winbind_separator());
2378 if (p) {
2379 opt_username = p+1;
2380 *p = '\0';
2381 if (opt_domain && !strequal(opt_domain, domain)) {
2382 x_fprintf(x_stderr, "Domain specified in username (%s) "
2383 "doesn't match specified domain (%s)!\n\n",
2384 domain, opt_domain);
2385 poptPrintHelp(pc, stderr, 0);
2386 exit(1);
2388 opt_domain = domain;
2389 } else {
2390 SAFE_FREE(domain);
2394 /* Note: if opt_domain is "" then send no domain */
2395 if (opt_domain == NULL) {
2396 opt_domain = get_winbind_domain();
2399 if (opt_workstation == NULL) {
2400 opt_workstation = "";
2403 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2404 if (lp_ctx == NULL) {
2405 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2406 exit(1);
2409 if (helper_protocol) {
2410 int i;
2411 for (i=0; i<NUM_HELPER_MODES; i++) {
2412 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2413 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2414 exit(0);
2417 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2419 for (i=0; i<NUM_HELPER_MODES; i++) {
2420 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2423 exit(1);
2426 if (!opt_username || !*opt_username) {
2427 x_fprintf(x_stderr, "username must be specified!\n\n");
2428 poptPrintHelp(pc, stderr, 0);
2429 exit(1);
2432 if (opt_challenge.length) {
2433 if (!check_auth_crap()) {
2434 exit(1);
2436 exit(0);
2439 if (!opt_password) {
2440 char pwd[256] = {0};
2441 int rc;
2443 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2444 if (rc == 0) {
2445 opt_password = SMB_STRDUP(pwd);
2449 if (diagnostics) {
2450 if (!diagnose_ntlm_auth()) {
2451 return 1;
2453 } else {
2454 fstring user;
2456 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2457 if (!check_plaintext_auth(user, opt_password, True)) {
2458 return 1;
2462 /* Exit code */
2464 poptFreeContext(pc);
2465 TALLOC_FREE(frame);
2466 return 0;