CVE-2022-2127: ntlm_auth: cap lanman response length value
[Samba.git] / source3 / utils / ntlm_auth.c
blobcff3c53845ffb1e097fb591bf72c8d983d28992e
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 "lib/cmdline/cmdline.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 "librpc/gen_ndr/krb5pac.h"
41 #include "auth/common_auth.h"
42 #include "source3/include/auth.h"
43 #include "source3/auth/proto.h"
44 #include "nsswitch/libwbclient/wbclient.h"
45 #include "nsswitch/winbind_struct_protocol.h"
46 #include "nsswitch/libwbclient/wbclient_internal.h"
47 #include "lib/param/loadparm.h"
48 #include "lib/util/base64.h"
49 #include "cmdline_contexts.h"
50 #include "lib/util/tevent_ntstatus.h"
51 #include "lib/util/string_wrappers.h"
53 #include <gnutls/gnutls.h>
54 #include <gnutls/crypto.h>
56 #ifdef HAVE_KRB5
57 #include "auth/kerberos/pac_utils.h"
58 #endif
60 #ifndef PAM_WINBIND_CONFIG_FILE
61 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
62 #endif
64 #define WINBIND_KRB5_AUTH 0x00000080
66 #undef DBGC_CLASS
67 #define DBGC_CLASS DBGC_WINBIND
69 #define INITIAL_BUFFER_SIZE 300
70 #define MAX_BUFFER_SIZE 630000
72 enum stdio_helper_mode {
73 SQUID_2_4_BASIC,
74 SQUID_2_5_BASIC,
75 SQUID_2_5_NTLMSSP,
76 NTLMSSP_CLIENT_1,
77 GSS_SPNEGO_SERVER,
78 GSS_SPNEGO_CLIENT,
79 NTLM_SERVER_1,
80 NTLM_CHANGE_PASSWORD_1,
81 NUM_HELPER_MODES
84 enum ntlm_auth_cli_state {
85 CLIENT_INITIAL = 0,
86 CLIENT_RESPONSE,
87 CLIENT_FINISHED,
88 CLIENT_ERROR
91 struct ntlm_auth_state {
92 TALLOC_CTX *mem_ctx;
93 enum stdio_helper_mode helper_mode;
94 enum ntlm_auth_cli_state cli_state;
95 struct ntlmssp_state *ntlmssp_state;
96 uint32_t neg_flags;
97 char *want_feature_list;
98 bool have_session_key;
99 DATA_BLOB session_key;
100 DATA_BLOB initial_message;
101 void *gensec_private_1;
103 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
104 struct loadparm_context *lp_ctx,
105 struct ntlm_auth_state *state, char *buf,
106 int length, void **private2);
108 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
109 struct loadparm_context *lp_ctx,
110 char *buf, int length, void **private1);
112 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
113 struct loadparm_context *lp_ctx,
114 struct ntlm_auth_state *state,
115 stdio_helper_function fn, void **private2);
117 static void manage_squid_basic_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_squid_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_client_ntlmssp_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_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_gss_spnego_client_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_server_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 void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
148 struct loadparm_context *lp_ctx,
149 struct ntlm_auth_state *state,
150 char *buf, int length, void **private2);
152 static const struct {
153 enum stdio_helper_mode mode;
154 const char *name;
155 stdio_helper_function fn;
156 } stdio_helper_protocols[] = {
157 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
158 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
159 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
160 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
161 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
162 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
163 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
164 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
165 { NUM_HELPER_MODES, NULL, NULL}
168 const char *opt_username;
169 const char *opt_domain;
170 const char *opt_workstation;
171 const char *opt_password;
172 static DATA_BLOB opt_challenge;
173 static DATA_BLOB opt_lm_response;
174 static DATA_BLOB opt_nt_response;
175 static int request_lm_key;
176 static int request_user_session_key;
177 static int use_cached_creds;
178 static int offline_logon;
179 static int opt_allow_mschapv2;
181 static const char *require_membership_of;
182 static const char *require_membership_of_sid;
183 static const char *opt_pam_winbind_conf;
185 const char *opt_target_service;
186 const char *opt_target_hostname;
189 /* This is a bit hairy, but the basic idea is to do a password callback
190 to the calling application. The callback comes from within gensec */
192 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
193 struct loadparm_context *lp_ctx,
194 struct ntlm_auth_state *state, char *buf, int length,
195 void **password)
197 DATA_BLOB in;
198 if (strlen(buf) < 2) {
199 DEBUG(1, ("query [%s] invalid", buf));
200 printf("BH Query invalid\n");
201 return;
204 if (strlen(buf) > 3) {
205 in = base64_decode_data_blob(buf + 3);
206 } else {
207 in = data_blob(NULL, 0);
210 if (strncmp(buf, "PW ", 3) == 0) {
212 *password = talloc_strndup(NULL,
213 (const char *)in.data, in.length);
215 if (*password == NULL) {
216 DEBUG(1, ("Out of memory\n"));
217 printf("BH Out of memory\n");
218 data_blob_free(&in);
219 return;
222 printf("OK\n");
223 data_blob_free(&in);
224 return;
226 DEBUG(1, ("Asked for (and expected) a password\n"));
227 printf("BH Expected a password\n");
228 data_blob_free(&in);
232 * Callback for password credentials. This is not async, and when
233 * GENSEC and the credentials code is made async, it will look rather
234 * different.
237 static const char *get_password(struct cli_credentials *credentials)
239 TALLOC_CTX *frame = talloc_stackframe();
240 char *password = NULL;
241 struct ntlm_auth_state *state;
243 state = talloc_zero(frame, struct ntlm_auth_state);
244 if (state == NULL) {
245 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
246 fprintf(stderr, "ERR\n");
247 exit(1);
250 state->mem_ctx = state;
252 /* Ask for a password */
253 printf("PW\n");
255 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, state, manage_gensec_get_pw_request, (void **)&password);
256 talloc_steal(credentials, password);
257 TALLOC_FREE(frame);
258 return password;
262 * A limited set of features are defined with text strings as needed
263 * by ntlm_auth
266 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
268 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
269 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
270 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
272 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
273 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
274 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
276 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
277 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
278 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
280 if (in_list("NTLMSSP_FEATURE_CCACHE", feature_list, true)) {
281 DEBUG(10, ("want GENSEC_FEATURE_NTLM_CCACHE\n"));
282 gensec_want_feature(state, GENSEC_FEATURE_NTLM_CCACHE);
286 static char winbind_separator(void)
288 struct wbcInterfaceDetails *details;
289 wbcErr ret;
290 static bool got_sep;
291 static char sep;
293 if (got_sep)
294 return sep;
296 ret = wbcInterfaceDetails(&details);
297 if (!WBC_ERROR_IS_OK(ret)) {
298 d_fprintf(stderr, "could not obtain winbind separator!\n");
299 return *lp_winbind_separator();
302 sep = details->winbind_separator;
304 wbcFreeMemory(details);
306 got_sep = True;
308 if (!sep) {
309 d_fprintf(stderr, "winbind separator was NULL!\n");
310 return *lp_winbind_separator();
313 return sep;
316 const char *get_winbind_domain(void)
318 struct wbcInterfaceDetails *details;
319 wbcErr ret;
321 static fstring winbind_domain;
322 if (*winbind_domain) {
323 return winbind_domain;
326 /* Send off request */
328 ret = wbcInterfaceDetails(&details);
329 if (!WBC_ERROR_IS_OK(ret)) {
330 DEBUG(1, ("could not obtain winbind domain name!\n"));
331 return lp_workgroup();
334 fstrcpy(winbind_domain, details->netbios_domain);
336 wbcFreeMemory(details);
338 return winbind_domain;
342 const char *get_winbind_netbios_name(void)
344 struct wbcInterfaceDetails *details;
345 wbcErr ret;
347 static fstring winbind_netbios_name;
349 if (*winbind_netbios_name) {
350 return winbind_netbios_name;
353 /* Send off request */
355 ret = wbcInterfaceDetails(&details);
356 if (!WBC_ERROR_IS_OK(ret)) {
357 DEBUG(1, ("could not obtain winbind netbios name!\n"));
358 return lp_netbios_name();
361 fstrcpy(winbind_netbios_name, details->netbios_name);
363 wbcFreeMemory(details);
365 return winbind_netbios_name;
369 DATA_BLOB get_challenge(void)
371 static DATA_BLOB chal;
372 if (opt_challenge.length)
373 return opt_challenge;
375 chal = data_blob(NULL, 8);
377 generate_random_buffer(chal.data, chal.length);
378 return chal;
381 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
382 form DOMAIN/user into a domain and a user */
384 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
385 fstring user)
388 char *p = strchr(domuser,winbind_separator());
390 if (!p) {
391 return False;
394 fstrcpy(user, p+1);
395 fstrcpy(domain, domuser);
396 domain[PTR_DIFF(p, domuser)] = 0;
397 return strupper_m(domain);
400 static bool get_require_membership_sid(void) {
401 fstring domain, name, sidbuf;
402 struct wbcDomainSid sid;
403 enum wbcSidType type;
404 wbcErr ret;
406 if (!require_membership_of) {
407 return True;
410 if (require_membership_of_sid) {
411 return True;
414 /* Otherwise, ask winbindd for the name->sid request */
416 if (!parse_ntlm_auth_domain_user(require_membership_of,
417 domain, name)) {
418 DEBUG(0, ("Could not parse %s into separate domain/name parts!\n",
419 require_membership_of));
420 return False;
423 ret = wbcLookupName(domain, name, &sid, &type);
424 if (!WBC_ERROR_IS_OK(ret)) {
425 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
426 require_membership_of));
427 return False;
430 wbcSidToStringBuf(&sid, sidbuf, sizeof(sidbuf));
432 require_membership_of_sid = SMB_STRDUP(sidbuf);
434 if (require_membership_of_sid)
435 return True;
437 return False;
441 * Get some configuration from pam_winbind.conf to see if we
442 * need to contact trusted domain
445 int get_pam_winbind_config(void)
447 int ctrl = 0;
448 struct tiniparser_dictionary *d = NULL;
450 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
451 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
454 d = tiniparser_load(opt_pam_winbind_conf);
456 if (!d) {
457 return 0;
460 if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
461 ctrl |= WINBIND_KRB5_AUTH;
464 tiniparser_freedict(d);
466 return ctrl;
469 /* Authenticate a user with a plaintext password */
471 static bool check_plaintext_auth(const char *user, const char *pass,
472 bool stdout_diagnostics)
474 struct winbindd_request request;
475 struct winbindd_response response;
476 wbcErr ret;
478 if (!get_require_membership_sid()) {
479 return False;
482 /* Send off request */
484 ZERO_STRUCT(request);
485 ZERO_STRUCT(response);
487 fstrcpy(request.data.auth.user, user);
488 fstrcpy(request.data.auth.pass, pass);
489 if (require_membership_of_sid) {
490 strlcpy(request.data.auth.require_membership_of_sid,
491 require_membership_of_sid,
492 sizeof(request.data.auth.require_membership_of_sid));
495 if (offline_logon) {
496 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
499 ret = wbcRequestResponse(NULL, WINBINDD_PAM_AUTH,
500 &request, &response);
502 /* Display response */
504 if (stdout_diagnostics) {
505 if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0)) {
506 d_fprintf(stderr, "Reading winbind reply failed! (0x01)\n");
509 d_printf("%s: %s (0x%x)\n",
510 response.data.auth.nt_status_string,
511 response.data.auth.error_string,
512 response.data.auth.nt_status);
513 } else {
514 if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0)) {
515 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
518 DEBUG(3, ("%s: %s (0x%x)\n",
519 response.data.auth.nt_status_string,
520 response.data.auth.error_string,
521 response.data.auth.nt_status));
524 return WBC_ERROR_IS_OK(ret);
527 /* authenticate a user with an encrypted username/password */
529 NTSTATUS contact_winbind_auth_crap(const char *username,
530 const char *domain,
531 const char *workstation,
532 const DATA_BLOB *challenge,
533 const DATA_BLOB *lm_response,
534 const DATA_BLOB *nt_response,
535 uint32_t flags,
536 uint32_t extra_logon_parameters,
537 uint8_t lm_key[8],
538 uint8_t user_session_key[16],
539 uint8_t *pauthoritative,
540 char **error_string,
541 char **unix_name)
543 NTSTATUS nt_status;
544 wbcErr ret;
545 struct winbindd_request request;
546 struct winbindd_response response;
548 *pauthoritative = 1;
550 if (!get_require_membership_sid()) {
551 return NT_STATUS_INVALID_PARAMETER;
554 ZERO_STRUCT(request);
555 ZERO_STRUCT(response);
557 request.flags = flags;
559 request.data.auth_crap.logon_parameters = extra_logon_parameters
560 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
562 if (opt_allow_mschapv2) {
563 request.data.auth_crap.logon_parameters |= MSV1_0_ALLOW_MSVCHAPV2;
566 if (require_membership_of_sid)
567 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
569 fstrcpy(request.data.auth_crap.user, username);
570 fstrcpy(request.data.auth_crap.domain, domain);
572 fstrcpy(request.data.auth_crap.workstation,
573 workstation);
575 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
577 if (lm_response && lm_response->length) {
578 size_t capped_lm_response_len = MIN(
579 lm_response->length,
580 sizeof(request.data.auth_crap.lm_resp));
582 memcpy(request.data.auth_crap.lm_resp,
583 lm_response->data,
584 capped_lm_response_len);
585 request.data.auth_crap.lm_resp_len = capped_lm_response_len;
588 if (nt_response && nt_response->length) {
589 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
590 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
591 request.extra_len = nt_response->length;
592 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
593 if (request.extra_data.data == NULL) {
594 return NT_STATUS_NO_MEMORY;
596 memcpy(request.extra_data.data, nt_response->data,
597 nt_response->length);
599 } else {
600 memcpy(request.data.auth_crap.nt_resp,
601 nt_response->data, nt_response->length);
603 request.data.auth_crap.nt_resp_len = nt_response->length;
606 ret = wbcRequestResponsePriv(
607 NULL,
608 WINBINDD_PAM_AUTH_CRAP,
609 &request,
610 &response);
611 SAFE_FREE(request.extra_data.data);
613 /* Display response */
615 if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0)) {
616 nt_status = NT_STATUS_UNSUCCESSFUL;
617 if (error_string)
618 *error_string = smb_xstrdup("Reading winbind reply failed!");
619 winbindd_free_response(&response);
620 return nt_status;
623 nt_status = (NT_STATUS(response.data.auth.nt_status));
624 if (!NT_STATUS_IS_OK(nt_status)) {
625 if (error_string)
626 *error_string = smb_xstrdup(response.data.auth.error_string);
627 *pauthoritative = response.data.auth.authoritative;
628 winbindd_free_response(&response);
629 return nt_status;
632 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
633 memcpy(lm_key, response.data.auth.first_8_lm_hash,
634 sizeof(response.data.auth.first_8_lm_hash));
636 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
637 memcpy(user_session_key, response.data.auth.user_session_key,
638 sizeof(response.data.auth.user_session_key));
641 if (flags & WBFLAG_PAM_UNIX_NAME) {
642 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
643 if (!*unix_name) {
644 winbindd_free_response(&response);
645 return NT_STATUS_NO_MEMORY;
649 winbindd_free_response(&response);
650 return nt_status;
653 /* contact server to change user password using auth crap */
654 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
655 const char *domain,
656 const DATA_BLOB new_nt_pswd,
657 const DATA_BLOB old_nt_hash_enc,
658 const DATA_BLOB new_lm_pswd,
659 const DATA_BLOB old_lm_hash_enc,
660 char **error_string)
662 NTSTATUS nt_status;
663 wbcErr ret;
664 struct winbindd_request request;
665 struct winbindd_response response;
667 if (!get_require_membership_sid())
669 if(error_string)
670 *error_string = smb_xstrdup("Can't get membership sid.");
671 return NT_STATUS_INVALID_PARAMETER;
674 ZERO_STRUCT(request);
675 ZERO_STRUCT(response);
677 if(username != NULL)
678 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
679 if(domain != NULL)
680 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
682 if(new_nt_pswd.length)
684 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
685 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
688 if(old_nt_hash_enc.length)
690 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));
691 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
694 if(new_lm_pswd.length)
696 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
697 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
700 if(old_lm_hash_enc.length)
702 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));
703 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
706 ret = wbcRequestResponse(NULL, WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP,
707 &request, &response);
709 /* Display response */
711 if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0))
713 nt_status = NT_STATUS_UNSUCCESSFUL;
714 if (error_string)
715 *error_string = smb_xstrdup("Reading winbind reply failed!");
716 winbindd_free_response(&response);
717 return nt_status;
720 nt_status = (NT_STATUS(response.data.auth.nt_status));
721 if (!NT_STATUS_IS_OK(nt_status))
723 if (error_string)
724 *error_string = smb_xstrdup(response.data.auth.error_string);
725 winbindd_free_response(&response);
726 return nt_status;
729 winbindd_free_response(&response);
731 return nt_status;
734 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
735 TALLOC_CTX *mem_ctx,
736 void *server_returned_info,
737 const char *original_user_name,
738 uint32_t session_info_flags,
739 struct auth_session_info **session_info_out)
741 const char *unix_username = (const char *)server_returned_info;
742 struct dom_sid *sids = NULL;
743 struct auth_session_info *session_info = NULL;
745 session_info = talloc_zero(mem_ctx, struct auth_session_info);
746 if (session_info == NULL) {
747 return NT_STATUS_NO_MEMORY;
750 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
751 if (session_info->unix_info == NULL) {
752 TALLOC_FREE(session_info);
753 return NT_STATUS_NO_MEMORY;
755 session_info->unix_info->unix_name = talloc_strdup(session_info->unix_info,
756 unix_username);
757 if (session_info->unix_info->unix_name == NULL) {
758 TALLOC_FREE(session_info);
759 return NT_STATUS_NO_MEMORY;
762 session_info->security_token = talloc_zero(session_info, struct security_token);
763 if (session_info->security_token == NULL) {
764 TALLOC_FREE(session_info);
765 return NT_STATUS_NO_MEMORY;
768 sids = talloc_zero_array(session_info->security_token,
769 struct dom_sid, 3);
770 if (sids == NULL) {
771 TALLOC_FREE(session_info);
772 return NT_STATUS_NO_MEMORY;
774 sid_copy(&sids[0], &global_sid_World);
775 sid_copy(&sids[1], &global_sid_Network);
776 sid_copy(&sids[2], &global_sid_Authenticated_Users);
778 session_info->security_token->num_sids = talloc_array_length(sids);
779 session_info->security_token->sids = sids;
781 *session_info_out = session_info;
783 return NT_STATUS_OK;
786 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
787 TALLOC_CTX *mem_ctx,
788 struct smb_krb5_context *smb_krb5_context,
789 DATA_BLOB *pac_blob,
790 const char *princ_name,
791 const struct tsocket_address *remote_address,
792 uint32_t session_info_flags,
793 struct auth_session_info **session_info)
795 TALLOC_CTX *tmp_ctx;
796 struct PAC_LOGON_INFO *logon_info = NULL;
797 char *unixuser;
798 NTSTATUS status;
799 const char *domain = "";
800 const char *user = "";
802 tmp_ctx = talloc_new(mem_ctx);
803 if (!tmp_ctx) {
804 return NT_STATUS_NO_MEMORY;
807 if (pac_blob) {
808 #ifdef HAVE_KRB5
809 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
810 NULL, NULL, 0, &logon_info);
811 #else
812 status = NT_STATUS_ACCESS_DENIED;
813 #endif
814 if (!NT_STATUS_IS_OK(status)) {
815 goto done;
817 } else {
818 status = NT_STATUS_ACCESS_DENIED;
819 DBG_WARNING("Kerberos ticket for[%s] has no PAC: %s\n",
820 princ_name, nt_errstr(status));
821 goto done;
824 if (logon_info->info3.base.account_name.string != NULL) {
825 user = logon_info->info3.base.account_name.string;
826 } else {
827 user = "";
829 if (logon_info->info3.base.logon_domain.string != NULL) {
830 domain = logon_info->info3.base.logon_domain.string;
831 } else {
832 domain = "";
835 if (strlen(user) == 0 || strlen(domain) == 0) {
836 status = NT_STATUS_ACCESS_DENIED;
837 DBG_WARNING("Kerberos ticket for[%s] has invalid "
838 "account_name[%s]/logon_domain[%s]: %s\n",
839 princ_name,
840 logon_info->info3.base.account_name.string,
841 logon_info->info3.base.logon_domain.string,
842 nt_errstr(status));
843 goto done;
846 DBG_NOTICE("Kerberos ticket principal name is [%s] "
847 "account_name[%s]/logon_domain[%s]\n",
848 princ_name, user, domain);
850 if (!strequal(domain, lp_workgroup())) {
851 if (!lp_allow_trusted_domains()) {
852 status = NT_STATUS_LOGON_FAILURE;
853 goto done;
857 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
858 if (!unixuser) {
859 status = NT_STATUS_NO_MEMORY;
860 goto done;
863 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
865 done:
866 TALLOC_FREE(tmp_ctx);
867 return status;
873 * Return the challenge as determined by the authentication subsystem
874 * @return an 8 byte random challenge
877 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
878 uint8_t chal[8])
880 if (auth_ctx->challenge.data.length == 8) {
881 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
882 auth_ctx->challenge.set_by));
883 memcpy(chal, auth_ctx->challenge.data.data, 8);
884 return NT_STATUS_OK;
887 if (!auth_ctx->challenge.set_by) {
888 generate_random_buffer(chal, 8);
890 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
891 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
892 auth_ctx->challenge.set_by = "random";
895 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
896 auth_ctx->challenge.set_by));
898 return NT_STATUS_OK;
902 * NTLM2 authentication modifies the effective challenge,
903 * @param challenge The new challenge value
905 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
907 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
908 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
910 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
911 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
913 return NT_STATUS_OK;
917 * Check the password on an NTLMSSP login.
919 * Return the session keys used on the connection.
922 struct winbind_pw_check_state {
923 uint8_t authoritative;
924 void *server_info;
925 DATA_BLOB nt_session_key;
926 DATA_BLOB lm_session_key;
929 static struct tevent_req *winbind_pw_check_send(
930 TALLOC_CTX *mem_ctx,
931 struct tevent_context *ev,
932 struct auth4_context *auth4_context,
933 const struct auth_usersupplied_info *user_info)
935 struct tevent_req *req = NULL;
936 struct winbind_pw_check_state *state = NULL;
937 NTSTATUS nt_status;
938 char *error_string = NULL;
939 uint8_t lm_key[8];
940 uint8_t user_sess_key[16];
941 char *unix_name = NULL;
943 req = tevent_req_create(
944 mem_ctx, &state, struct winbind_pw_check_state);
945 if (req == NULL) {
946 return NULL;
949 nt_status = contact_winbind_auth_crap(
950 user_info->client.account_name,
951 user_info->client.domain_name,
952 user_info->workstation_name,
953 &auth4_context->challenge.data,
954 &user_info->password.response.lanman,
955 &user_info->password.response.nt,
956 WBFLAG_PAM_LMKEY |
957 WBFLAG_PAM_USER_SESSION_KEY |
958 WBFLAG_PAM_UNIX_NAME,
960 lm_key, user_sess_key,
961 &state->authoritative,
962 &error_string,
963 &unix_name);
965 if (tevent_req_nterror(req, nt_status)) {
966 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
967 DBG_ERR("Login for user [%s]\\[%s]@[%s] failed due "
968 "to [%s]\n",
969 user_info->client.domain_name,
970 user_info->client.account_name,
971 user_info->workstation_name,
972 error_string ?
973 error_string :
974 "unknown error (NULL)");
975 } else {
976 DBG_NOTICE("Login for user [%s]\\[%s]@[%s] failed due "
977 "to [%s]\n",
978 user_info->client.domain_name,
979 user_info->client.account_name,
980 user_info->workstation_name,
981 error_string ?
982 error_string :
983 "unknown error (NULL)");
985 goto done;
988 if (!all_zero(lm_key, 8)) {
989 state->lm_session_key = data_blob_talloc(state, NULL, 16);
990 if (tevent_req_nomem(state->lm_session_key.data, req)) {
991 goto done;
993 memcpy(state->lm_session_key.data, lm_key, 8);
994 memset(state->lm_session_key.data+8, '\0', 8);
996 if (!all_zero(user_sess_key, 16)) {
997 state->nt_session_key = data_blob_talloc(
998 state, user_sess_key, 16);
999 if (tevent_req_nomem(state->nt_session_key.data, req)) {
1000 goto done;
1003 state->server_info = talloc_strdup(state, unix_name);
1004 if (tevent_req_nomem(state->server_info, req)) {
1005 goto done;
1007 tevent_req_done(req);
1009 done:
1010 SAFE_FREE(error_string);
1011 SAFE_FREE(unix_name);
1012 return tevent_req_post(req, ev);
1015 static NTSTATUS winbind_pw_check_recv(struct tevent_req *req,
1016 TALLOC_CTX *mem_ctx,
1017 uint8_t *pauthoritative,
1018 void **server_returned_info,
1019 DATA_BLOB *nt_session_key,
1020 DATA_BLOB *lm_session_key)
1022 struct winbind_pw_check_state *state = tevent_req_data(
1023 req, struct winbind_pw_check_state);
1024 NTSTATUS status;
1026 if (pauthoritative != NULL) {
1027 *pauthoritative = state->authoritative;
1030 if (tevent_req_is_nterror(req, &status)) {
1031 return status;
1034 if (server_returned_info != NULL) {
1035 *server_returned_info = talloc_move(
1036 mem_ctx, &state->server_info);
1038 if (nt_session_key != NULL) {
1039 *nt_session_key = (DATA_BLOB) {
1040 .data = talloc_move(
1041 mem_ctx, &state->nt_session_key.data),
1042 .length = state->nt_session_key.length,
1045 if (lm_session_key != NULL) {
1046 *lm_session_key = (DATA_BLOB) {
1047 .data = talloc_move(
1048 mem_ctx, &state->lm_session_key.data),
1049 .length = state->lm_session_key.length,
1053 return NT_STATUS_OK;
1056 struct local_pw_check_state {
1057 uint8_t authoritative;
1058 void *server_info;
1059 DATA_BLOB nt_session_key;
1060 DATA_BLOB lm_session_key;
1063 static struct tevent_req *local_pw_check_send(
1064 TALLOC_CTX *mem_ctx,
1065 struct tevent_context *ev,
1066 struct auth4_context *auth4_context,
1067 const struct auth_usersupplied_info *user_info)
1069 struct tevent_req *req = NULL;
1070 struct local_pw_check_state *state = NULL;
1071 struct samr_Password lm_pw, nt_pw;
1072 NTSTATUS nt_status;
1074 req = tevent_req_create(
1075 mem_ctx, &state, struct local_pw_check_state);
1076 if (req == NULL) {
1077 return NULL;
1079 state->authoritative = 1;
1081 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1083 nt_status = ntlm_password_check(
1084 state,
1085 true,
1086 NTLM_AUTH_ON,
1088 &auth4_context->challenge.data,
1089 &user_info->password.response.lanman,
1090 &user_info->password.response.nt,
1091 user_info->client.account_name,
1092 user_info->client.account_name,
1093 user_info->client.domain_name,
1094 &lm_pw,
1095 &nt_pw,
1096 &state->nt_session_key,
1097 &state->lm_session_key);
1099 if (tevent_req_nterror(req, nt_status)) {
1100 DBG_NOTICE("Login for user [%s]\\[%s]@[%s] failed due to "
1101 "[%s]\n",
1102 user_info->client.domain_name,
1103 user_info->client.account_name,
1104 user_info->workstation_name,
1105 nt_errstr(nt_status));
1106 return tevent_req_post(req, ev);
1109 state->server_info = talloc_asprintf(
1110 state,
1111 "%s%c%s",
1112 user_info->client.domain_name,
1113 *lp_winbind_separator(),
1114 user_info->client.account_name);
1115 if (tevent_req_nomem(state->server_info, req)) {
1116 return tevent_req_post(req, ev);
1119 tevent_req_done(req);
1120 return tevent_req_post(req, ev);
1123 static NTSTATUS local_pw_check_recv(struct tevent_req *req,
1124 TALLOC_CTX *mem_ctx,
1125 uint8_t *pauthoritative,
1126 void **server_returned_info,
1127 DATA_BLOB *nt_session_key,
1128 DATA_BLOB *lm_session_key)
1130 struct local_pw_check_state *state = tevent_req_data(
1131 req, struct local_pw_check_state);
1132 NTSTATUS status;
1134 if (pauthoritative != NULL) {
1135 *pauthoritative = state->authoritative;
1138 if (tevent_req_is_nterror(req, &status)) {
1139 return status;
1142 if (server_returned_info != NULL) {
1143 *server_returned_info = talloc_move(
1144 mem_ctx, &state->server_info);
1146 if (nt_session_key != NULL) {
1147 *nt_session_key = (DATA_BLOB) {
1148 .data = talloc_move(
1149 mem_ctx, &state->nt_session_key.data),
1150 .length = state->nt_session_key.length,
1153 if (lm_session_key != NULL) {
1154 *lm_session_key = (DATA_BLOB) {
1155 .data = talloc_move(
1156 mem_ctx, &state->lm_session_key.data),
1157 .length = state->lm_session_key.length,
1161 return NT_STATUS_OK;
1164 static NTSTATUS ntlm_auth_prepare_gensec_client(TALLOC_CTX *mem_ctx,
1165 struct loadparm_context *lp_ctx,
1166 struct gensec_security **gensec_security_out)
1168 struct gensec_security *gensec_security = NULL;
1169 NTSTATUS nt_status;
1170 TALLOC_CTX *tmp_ctx;
1171 const struct gensec_security_ops **backends = NULL;
1172 struct gensec_settings *gensec_settings = NULL;
1173 size_t idx = 0;
1175 tmp_ctx = talloc_new(mem_ctx);
1176 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1178 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1179 if (gensec_settings == NULL) {
1180 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1181 TALLOC_FREE(tmp_ctx);
1182 return NT_STATUS_NO_MEMORY;
1185 backends = talloc_zero_array(gensec_settings,
1186 const struct gensec_security_ops *, 4);
1187 if (backends == NULL) {
1188 TALLOC_FREE(tmp_ctx);
1189 return NT_STATUS_NO_MEMORY;
1191 gensec_settings->backends = backends;
1193 gensec_init();
1195 /* These need to be in priority order, krb5 before NTLMSSP */
1196 #if defined(HAVE_KRB5)
1197 backends[idx++] = &gensec_gse_krb5_security_ops;
1198 #endif
1200 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1202 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1204 nt_status = gensec_client_start(NULL, &gensec_security,
1205 gensec_settings);
1206 if (!NT_STATUS_IS_OK(nt_status)) {
1207 TALLOC_FREE(tmp_ctx);
1208 return nt_status;
1211 talloc_unlink(tmp_ctx, gensec_settings);
1213 if (opt_target_service != NULL) {
1214 nt_status = gensec_set_target_service(gensec_security,
1215 opt_target_service);
1216 if (!NT_STATUS_IS_OK(nt_status)) {
1217 TALLOC_FREE(tmp_ctx);
1218 return nt_status;
1222 if (opt_target_hostname != NULL) {
1223 nt_status = gensec_set_target_hostname(gensec_security,
1224 opt_target_hostname);
1225 if (!NT_STATUS_IS_OK(nt_status)) {
1226 TALLOC_FREE(tmp_ctx);
1227 return nt_status;
1231 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1232 TALLOC_FREE(tmp_ctx);
1233 return NT_STATUS_OK;
1236 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1238 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1239 if (auth4_context == NULL) {
1240 DEBUG(10, ("failed to allocate auth4_context\n"));
1241 return NULL;
1243 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1244 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1245 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1246 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1247 if (local_pw) {
1248 auth4_context->check_ntlm_password_send = local_pw_check_send;
1249 auth4_context->check_ntlm_password_recv = local_pw_check_recv;
1250 } else {
1251 auth4_context->check_ntlm_password_send =
1252 winbind_pw_check_send;
1253 auth4_context->check_ntlm_password_recv =
1254 winbind_pw_check_recv;
1256 auth4_context->private_data = NULL;
1257 return auth4_context;
1260 static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx,
1261 struct loadparm_context *lp_ctx,
1262 struct gensec_security **gensec_security_out)
1264 struct gensec_security *gensec_security;
1265 NTSTATUS nt_status;
1267 TALLOC_CTX *tmp_ctx;
1268 const struct gensec_security_ops **backends;
1269 struct gensec_settings *gensec_settings;
1270 size_t idx = 0;
1271 struct cli_credentials *server_credentials;
1273 struct auth4_context *auth4_context;
1275 tmp_ctx = talloc_new(mem_ctx);
1276 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1278 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1279 if (auth4_context == NULL) {
1280 TALLOC_FREE(tmp_ctx);
1281 return NT_STATUS_NO_MEMORY;
1284 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1285 if (lp_ctx == NULL) {
1286 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1287 TALLOC_FREE(tmp_ctx);
1288 return NT_STATUS_NO_MEMORY;
1292 * This should be a 'netbios domain -> DNS domain'
1293 * mapping, and can currently validly return NULL on
1294 * poorly configured systems.
1296 * This is used for the NTLMSSP server
1299 if (opt_password) {
1300 gensec_settings->server_netbios_name = lp_netbios_name();
1301 gensec_settings->server_netbios_domain = lp_workgroup();
1302 } else {
1303 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1304 gensec_settings->server_netbios_domain = get_winbind_domain();
1307 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1308 get_mydnsdomname(talloc_tos()));
1309 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1310 get_mydnsfullname());
1312 backends = talloc_zero_array(gensec_settings,
1313 const struct gensec_security_ops *, 4);
1315 if (backends == NULL) {
1316 TALLOC_FREE(tmp_ctx);
1317 return NT_STATUS_NO_MEMORY;
1319 gensec_settings->backends = backends;
1321 gensec_init();
1323 /* These need to be in priority order, krb5 before NTLMSSP */
1324 #if defined(HAVE_KRB5)
1325 backends[idx++] = &gensec_gse_krb5_security_ops;
1326 #endif
1328 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1330 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1333 * This is anonymous for now, because we just use it
1334 * to set the kerberos state at the moment
1336 server_credentials = cli_credentials_init_anon(tmp_ctx);
1337 if (!server_credentials) {
1338 DBG_ERR("Failed to init server credentials\n");
1339 return NT_STATUS_NO_MEMORY;
1342 cli_credentials_set_conf(server_credentials, lp_ctx);
1344 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1345 cli_credentials_set_kerberos_state(server_credentials,
1346 CRED_USE_KERBEROS_DESIRED,
1347 CRED_SPECIFIED);
1348 } else {
1349 cli_credentials_set_kerberos_state(server_credentials,
1350 CRED_USE_KERBEROS_DISABLED,
1351 CRED_SPECIFIED);
1354 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1355 auth4_context, &gensec_security);
1357 if (!NT_STATUS_IS_OK(nt_status)) {
1358 TALLOC_FREE(tmp_ctx);
1359 return nt_status;
1362 gensec_set_credentials(gensec_security, server_credentials);
1365 * TODO: Allow the caller to pass their own description here
1366 * via a command-line option
1368 nt_status = gensec_set_target_service_description(gensec_security,
1369 "ntlm_auth");
1370 if (!NT_STATUS_IS_OK(nt_status)) {
1371 TALLOC_FREE(tmp_ctx);
1372 return nt_status;
1375 talloc_unlink(tmp_ctx, lp_ctx);
1376 talloc_unlink(tmp_ctx, server_credentials);
1377 talloc_unlink(tmp_ctx, gensec_settings);
1378 talloc_unlink(tmp_ctx, auth4_context);
1380 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1381 TALLOC_FREE(tmp_ctx);
1382 return NT_STATUS_OK;
1385 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1386 struct loadparm_context *lp_ctx,
1387 struct ntlm_auth_state *state,
1388 char *buf, int length, void **private2)
1390 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1391 return;
1394 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1395 struct loadparm_context *lp_ctx,
1396 struct ntlm_auth_state *state,
1397 char *buf, int length, void **private2)
1399 char *user, *pass;
1400 user=buf;
1402 pass=(char *)memchr(buf,' ',length);
1403 if (!pass) {
1404 DEBUG(2, ("Password not found. Denying access\n"));
1405 printf("ERR\n");
1406 return;
1408 *pass='\0';
1409 pass++;
1411 if (state->helper_mode == SQUID_2_5_BASIC) {
1412 char *end = rfc1738_unescape(user);
1413 if (end == NULL || (end - user) != strlen(user)) {
1414 DEBUG(2, ("Badly rfc1738 encoded username: %s; "
1415 "denying access\n", user));
1416 printf("ERR\n");
1417 return;
1419 end = rfc1738_unescape(pass);
1420 if (end == NULL || (end - pass) != strlen(pass)) {
1421 DEBUG(2, ("Badly encoded password for %s; "
1422 "denying access\n", user));
1423 printf("ERR\n");
1424 return;
1428 if (check_plaintext_auth(user, pass, False)) {
1429 printf("OK\n");
1430 } else {
1431 printf("ERR\n");
1435 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1436 struct loadparm_context *lp_ctx,
1437 char *buf, int length, void **private1)
1439 DATA_BLOB in;
1440 DATA_BLOB out = data_blob(NULL, 0);
1441 char *out_base64 = NULL;
1442 const char *reply_arg = NULL;
1443 struct gensec_ntlm_state {
1444 struct gensec_security *gensec_state;
1445 const char *set_password;
1447 struct gensec_ntlm_state *state;
1449 NTSTATUS nt_status;
1450 bool first = false;
1451 const char *reply_code;
1452 struct cli_credentials *creds;
1454 static char *want_feature_list = NULL;
1455 static DATA_BLOB session_key;
1457 TALLOC_CTX *mem_ctx;
1459 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1460 if (mem_ctx == NULL) {
1461 printf("BH No Memory\n");
1462 exit(1);
1465 if (*private1) {
1466 state = talloc_get_type(*private1, struct gensec_ntlm_state);
1467 if (state == NULL) {
1468 DBG_WARNING("*private1 is of type %s\n",
1469 talloc_get_name(*private1));
1470 printf("BH *private1 is of type %s\n",
1471 talloc_get_name(*private1));
1472 exit(1);
1474 } else {
1475 state = talloc_zero(NULL, struct gensec_ntlm_state);
1476 if (!state) {
1477 printf("BH No Memory\n");
1478 exit(1);
1480 *private1 = state;
1481 if (opt_password) {
1482 state->set_password = opt_password;
1486 if (strlen(buf) < 2) {
1487 DEBUG(1, ("query [%s] invalid", buf));
1488 printf("BH Query invalid\n");
1489 talloc_free(mem_ctx);
1490 return;
1493 if (strlen(buf) > 3) {
1494 if(strncmp(buf, "SF ", 3) == 0) {
1495 DEBUG(10, ("Setting flags to negotiate\n"));
1496 talloc_free(want_feature_list);
1497 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1498 printf("OK\n");
1499 talloc_free(mem_ctx);
1500 return;
1502 in = base64_decode_data_blob_talloc(mem_ctx, buf + 3);
1503 } else {
1504 in = data_blob(NULL, 0);
1507 if (strncmp(buf, "YR", 2) == 0) {
1508 if (state->gensec_state) {
1509 talloc_free(state->gensec_state);
1510 state->gensec_state = NULL;
1512 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1513 /* Just return BH, like ntlm_auth from Samba 3 does. */
1514 printf("BH Command expected\n");
1515 talloc_free(mem_ctx);
1516 return;
1517 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1518 (strncmp(buf, "KK ", 3) != 0) &&
1519 (strncmp(buf, "AF ", 3) != 0) &&
1520 (strncmp(buf, "NA ", 3) != 0) &&
1521 (strncmp(buf, "UG", 2) != 0) &&
1522 (strncmp(buf, "PW ", 3) != 0) &&
1523 (strncmp(buf, "GK", 2) != 0) &&
1524 (strncmp(buf, "GF", 2) != 0)) {
1525 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1526 printf("BH SPNEGO request invalid prefix\n");
1527 talloc_free(mem_ctx);
1528 return;
1531 /* setup gensec */
1532 if (!(state->gensec_state)) {
1533 switch (stdio_helper_mode) {
1534 case GSS_SPNEGO_CLIENT:
1536 * cached credentials are only supported by
1537 * NTLMSSP_CLIENT_1 for now.
1539 use_cached_creds = false;
1540 FALL_THROUGH;
1541 case NTLMSSP_CLIENT_1:
1542 /* setup the client side */
1544 if (state->set_password != NULL) {
1545 use_cached_creds = false;
1548 if (use_cached_creds) {
1549 struct wbcCredentialCacheParams params;
1550 struct wbcCredentialCacheInfo *info = NULL;
1551 struct wbcAuthErrorInfo *error = NULL;
1552 wbcErr wbc_status;
1554 params.account_name = opt_username;
1555 params.domain_name = opt_domain;
1556 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1557 params.num_blobs = 0;
1558 params.blobs = NULL;
1560 wbc_status = wbcCredentialCache(&params, &info,
1561 &error);
1562 wbcFreeMemory(error);
1563 if (!WBC_ERROR_IS_OK(wbc_status)) {
1564 use_cached_creds = false;
1566 wbcFreeMemory(info);
1569 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1570 &state->gensec_state);
1571 if (!NT_STATUS_IS_OK(nt_status)) {
1572 printf("BH GENSEC mech failed to start: %s\n",
1573 nt_errstr(nt_status));
1574 talloc_free(mem_ctx);
1575 return;
1578 creds = cli_credentials_init(state->gensec_state);
1579 cli_credentials_set_conf(creds, lp_ctx);
1580 if (opt_username) {
1581 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1583 if (opt_domain) {
1584 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1586 if (use_cached_creds) {
1587 gensec_want_feature(state->gensec_state,
1588 GENSEC_FEATURE_NTLM_CCACHE);
1589 } else if (state->set_password) {
1590 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1591 } else {
1592 cli_credentials_set_password_callback(creds, get_password);
1594 if (opt_workstation) {
1595 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1598 gensec_set_credentials(state->gensec_state, creds);
1600 break;
1601 case GSS_SPNEGO_SERVER:
1602 case SQUID_2_5_NTLMSSP:
1604 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1605 &state->gensec_state);
1606 if (!NT_STATUS_IS_OK(nt_status)) {
1607 printf("BH GENSEC mech failed to start: %s\n",
1608 nt_errstr(nt_status));
1609 talloc_free(mem_ctx);
1610 return;
1612 break;
1614 default:
1615 talloc_free(mem_ctx);
1616 abort();
1619 gensec_want_feature_list(state->gensec_state, want_feature_list);
1621 /* Session info is not complete, do not pass to auth log */
1622 gensec_want_feature(state->gensec_state, GENSEC_FEATURE_NO_AUTHZ_LOG);
1624 switch (stdio_helper_mode) {
1625 case GSS_SPNEGO_CLIENT:
1626 case GSS_SPNEGO_SERVER:
1627 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1628 if (!in.length) {
1629 first = true;
1631 break;
1632 case NTLMSSP_CLIENT_1:
1633 if (!in.length) {
1634 first = true;
1636 FALL_THROUGH;
1637 case SQUID_2_5_NTLMSSP:
1638 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1639 break;
1640 default:
1641 talloc_free(mem_ctx);
1642 abort();
1645 if (!NT_STATUS_IS_OK(nt_status)) {
1646 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1647 printf("BH GENSEC mech failed to start\n");
1648 talloc_free(mem_ctx);
1649 return;
1654 /* update */
1656 if (strncmp(buf, "PW ", 3) == 0) {
1657 state->set_password = talloc_strndup(state,
1658 (const char *)in.data,
1659 in.length);
1661 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1662 state->set_password,
1663 CRED_SPECIFIED);
1664 printf("OK\n");
1665 talloc_free(mem_ctx);
1666 return;
1669 if (strncmp(buf, "GK", 2) == 0) {
1670 char *base64_key;
1671 DEBUG(10, ("Requested session key\n"));
1672 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1673 if(!NT_STATUS_IS_OK(nt_status)) {
1674 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1675 printf("BH No session key\n");
1676 talloc_free(mem_ctx);
1677 return;
1678 } else {
1679 base64_key = base64_encode_data_blob(state, session_key);
1680 SMB_ASSERT(base64_key != NULL);
1681 printf("GK %s\n", base64_key);
1682 talloc_free(base64_key);
1684 talloc_free(mem_ctx);
1685 return;
1688 if (strncmp(buf, "GF", 2) == 0) {
1689 uint32_t neg_flags;
1691 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1693 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1694 if (neg_flags == 0) {
1695 printf("BH\n");
1696 talloc_free(mem_ctx);
1697 return;
1700 printf("GF 0x%08x\n", neg_flags);
1701 talloc_free(mem_ctx);
1702 return;
1705 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1707 /* don't leak 'bad password'/'no such user' info to the network client */
1708 nt_status = nt_status_squash(nt_status);
1710 if (out.length) {
1711 out_base64 = base64_encode_data_blob(mem_ctx, out);
1712 SMB_ASSERT(out_base64 != NULL);
1713 } else {
1714 out_base64 = NULL;
1717 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1718 reply_arg = "*";
1719 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1720 reply_code = "YR";
1721 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1722 reply_code = "KK";
1723 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1724 reply_code = "TT";
1725 } else {
1726 abort();
1730 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1731 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1732 reply_arg = nt_errstr(nt_status);
1733 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1734 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1735 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1736 reply_arg = nt_errstr(nt_status);
1737 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1738 } else if (!NT_STATUS_IS_OK(nt_status)) {
1739 reply_code = "NA";
1740 reply_arg = nt_errstr(nt_status);
1741 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1742 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1743 struct auth_session_info *session_info;
1745 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1746 if (!NT_STATUS_IS_OK(nt_status)) {
1747 reply_code = "BH Failed to retrieve session info";
1748 reply_arg = nt_errstr(nt_status);
1749 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1750 } else {
1752 reply_code = "AF";
1753 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1754 if (reply_arg == NULL) {
1755 reply_code = "BH out of memory";
1756 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1758 talloc_free(session_info);
1760 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1761 reply_code = "AF";
1762 reply_arg = out_base64;
1763 } else {
1764 abort();
1767 switch (stdio_helper_mode) {
1768 case GSS_SPNEGO_SERVER:
1769 printf("%s %s %s\n", reply_code,
1770 out_base64 ? out_base64 : "*",
1771 reply_arg ? reply_arg : "*");
1772 break;
1773 default:
1774 if (out_base64) {
1775 printf("%s %s\n", reply_code, out_base64);
1776 } else if (reply_arg) {
1777 printf("%s %s\n", reply_code, reply_arg);
1778 } else {
1779 printf("%s\n", reply_code);
1783 talloc_free(mem_ctx);
1784 return;
1787 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1788 struct loadparm_context *lp_ctx,
1789 struct ntlm_auth_state *state,
1790 char *buf, int length, void **private2)
1792 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1793 return;
1796 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1797 struct loadparm_context *lp_ctx,
1798 struct ntlm_auth_state *state,
1799 char *buf, int length, void **private2)
1801 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1802 return;
1805 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1806 struct loadparm_context *lp_ctx,
1807 struct ntlm_auth_state *state,
1808 char *buf, int length, void **private2)
1810 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1811 return;
1814 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1815 struct loadparm_context *lp_ctx,
1816 struct ntlm_auth_state *state,
1817 char *buf, int length, void **private2)
1819 char *request, *parameter;
1820 static DATA_BLOB challenge;
1821 static DATA_BLOB lm_response;
1822 static DATA_BLOB nt_response;
1823 static char *full_username;
1824 static char *username;
1825 static char *domain;
1826 static char *plaintext_password;
1827 static bool ntlm_server_1_user_session_key;
1828 static bool ntlm_server_1_lm_session_key;
1830 if (strequal(buf, ".")) {
1831 if (!full_username && !username) {
1832 printf("Error: No username supplied!\n");
1833 } else if (plaintext_password) {
1834 /* handle this request as plaintext */
1835 if (!full_username) {
1836 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1837 printf("Error: Out of memory in "
1838 "asprintf!\n.\n");
1839 return;
1842 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1843 printf("Authenticated: Yes\n");
1844 } else {
1845 printf("Authenticated: No\n");
1847 } else if (!lm_response.data && !nt_response.data) {
1848 printf("Error: No password supplied!\n");
1849 } else if (!challenge.data) {
1850 printf("Error: No lanman-challenge supplied!\n");
1851 } else {
1852 char *error_string = NULL;
1853 uchar lm_key[8];
1854 uchar user_session_key[16];
1855 uint32_t flags = 0;
1856 NTSTATUS nt_status;
1857 if (full_username && !username) {
1858 fstring fstr_user;
1859 fstring fstr_domain;
1861 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1862 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1863 printf("Error: Could not parse into "
1864 "domain and username\n");
1866 SAFE_FREE(username);
1867 SAFE_FREE(domain);
1868 username = smb_xstrdup(fstr_user);
1869 domain = smb_xstrdup(fstr_domain);
1872 if (opt_password) {
1873 DATA_BLOB nt_session_key, lm_session_key;
1874 struct samr_Password lm_pw, nt_pw;
1875 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1876 ZERO_STRUCT(user_session_key);
1877 ZERO_STRUCT(lm_key);
1879 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1880 nt_status = ntlm_password_check(mem_ctx,
1881 true,
1882 NTLM_AUTH_ON,
1884 &challenge,
1885 &lm_response,
1886 &nt_response,
1887 username,
1888 username,
1889 domain,
1890 &lm_pw, &nt_pw,
1891 &nt_session_key,
1892 &lm_session_key);
1893 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1894 if (ntlm_server_1_user_session_key) {
1895 if (nt_session_key.length == sizeof(user_session_key)) {
1896 memcpy(user_session_key,
1897 nt_session_key.data,
1898 sizeof(user_session_key));
1901 if (ntlm_server_1_lm_session_key) {
1902 if (lm_session_key.length == sizeof(lm_key)) {
1903 memcpy(lm_key,
1904 lm_session_key.data,
1905 sizeof(lm_key));
1908 TALLOC_FREE(mem_ctx);
1910 } else {
1911 uint8_t authoritative = 1;
1913 if (!domain) {
1914 domain = smb_xstrdup(get_winbind_domain());
1917 if (ntlm_server_1_lm_session_key)
1918 flags |= WBFLAG_PAM_LMKEY;
1920 if (ntlm_server_1_user_session_key)
1921 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1923 nt_status = contact_winbind_auth_crap(username,
1924 domain,
1925 lp_netbios_name(),
1926 &challenge,
1927 &lm_response,
1928 &nt_response,
1929 flags, 0,
1930 lm_key,
1931 user_session_key,
1932 &authoritative,
1933 &error_string,
1934 NULL);
1937 if (!NT_STATUS_IS_OK(nt_status)) {
1938 printf("Authenticated: No\n");
1939 printf("Authentication-Error: %s\n.\n",
1940 error_string);
1941 } else {
1942 char *hex_lm_key;
1943 char *hex_user_session_key;
1945 printf("Authenticated: Yes\n");
1947 if (ntlm_server_1_lm_session_key
1948 && (!all_zero(lm_key,
1949 sizeof(lm_key)))) {
1950 hex_lm_key = hex_encode_talloc(NULL,
1951 (const unsigned char *)lm_key,
1952 sizeof(lm_key));
1953 printf("LANMAN-Session-Key: %s\n",
1954 hex_lm_key);
1955 TALLOC_FREE(hex_lm_key);
1958 if (ntlm_server_1_user_session_key
1959 && (!all_zero(user_session_key,
1960 sizeof(user_session_key)))) {
1961 hex_user_session_key = hex_encode_talloc(NULL,
1962 (const unsigned char *)user_session_key,
1963 sizeof(user_session_key));
1964 printf("User-Session-Key: %s\n",
1965 hex_user_session_key);
1966 TALLOC_FREE(hex_user_session_key);
1969 SAFE_FREE(error_string);
1971 /* clear out the state */
1972 challenge = data_blob_null;
1973 nt_response = data_blob_null;
1974 lm_response = data_blob_null;
1975 SAFE_FREE(full_username);
1976 SAFE_FREE(username);
1977 SAFE_FREE(domain);
1978 SAFE_FREE(plaintext_password);
1979 ntlm_server_1_user_session_key = False;
1980 ntlm_server_1_lm_session_key = False;
1981 printf(".\n");
1983 return;
1986 request = buf;
1988 /* Indicates a base64 encoded structure */
1989 parameter = strstr_m(request, ":: ");
1990 if (!parameter) {
1991 parameter = strstr_m(request, ": ");
1993 if (!parameter) {
1994 DEBUG(0, ("Parameter not found!\n"));
1995 printf("Error: Parameter not found!\n.\n");
1996 return;
1999 parameter[0] ='\0';
2000 parameter++;
2001 parameter[0] ='\0';
2002 parameter++;
2004 } else {
2005 parameter[0] ='\0';
2006 parameter++;
2007 parameter[0] ='\0';
2008 parameter++;
2009 parameter[0] ='\0';
2010 parameter++;
2012 base64_decode_inplace(parameter);
2015 if (strequal(request, "LANMAN-Challenge")) {
2016 challenge = strhex_to_data_blob(NULL, parameter);
2017 if (challenge.length != 8) {
2018 printf("Error: hex decode of %s failed! "
2019 "(got %d bytes, expected 8)\n.\n",
2020 parameter,
2021 (int)challenge.length);
2022 challenge = data_blob_null;
2024 } else if (strequal(request, "NT-Response")) {
2025 nt_response = strhex_to_data_blob(NULL, parameter);
2026 if (nt_response.length < 24) {
2027 printf("Error: hex decode of %s failed! "
2028 "(only got %d bytes, needed at least 24)\n.\n",
2029 parameter,
2030 (int)nt_response.length);
2031 nt_response = data_blob_null;
2033 } else if (strequal(request, "LANMAN-Response")) {
2034 lm_response = strhex_to_data_blob(NULL, parameter);
2035 if (lm_response.length != 24) {
2036 printf("Error: hex decode of %s failed! "
2037 "(got %d bytes, expected 24)\n.\n",
2038 parameter,
2039 (int)lm_response.length);
2040 lm_response = data_blob_null;
2042 } else if (strequal(request, "Password")) {
2043 plaintext_password = smb_xstrdup(parameter);
2044 } else if (strequal(request, "NT-Domain")) {
2045 domain = smb_xstrdup(parameter);
2046 } else if (strequal(request, "Username")) {
2047 username = smb_xstrdup(parameter);
2048 } else if (strequal(request, "Full-Username")) {
2049 full_username = smb_xstrdup(parameter);
2050 } else if (strequal(request, "Request-User-Session-Key")) {
2051 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2052 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2053 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2054 } else {
2055 printf("Error: Unknown request %s\n.\n", request);
2059 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2060 struct loadparm_context *lp_ctx,
2061 struct ntlm_auth_state *state,
2062 char *buf, int length, void **private2)
2064 char *request, *parameter;
2065 static DATA_BLOB new_nt_pswd;
2066 static DATA_BLOB old_nt_hash_enc;
2067 static DATA_BLOB new_lm_pswd;
2068 static DATA_BLOB old_lm_hash_enc;
2069 static char *full_username = NULL;
2070 static char *username = NULL;
2071 static char *domain = NULL;
2072 static char *newpswd = NULL;
2073 static char *oldpswd = NULL;
2075 if (strequal(buf, ".")) {
2076 if(newpswd && oldpswd) {
2077 uchar old_nt_hash[16];
2078 uchar old_lm_hash[16];
2079 uchar new_nt_hash[16];
2080 uchar new_lm_hash[16];
2082 gnutls_cipher_hd_t cipher_hnd = NULL;
2083 gnutls_datum_t old_nt_key = {
2084 .data = old_nt_hash,
2085 .size = sizeof(old_nt_hash),
2087 int rc;
2089 new_nt_pswd = data_blob(NULL, 516);
2090 old_nt_hash_enc = data_blob(NULL, 16);
2092 /* Calculate the MD4 hash (NT compatible) of the
2093 * password */
2094 E_md4hash(oldpswd, old_nt_hash);
2095 E_md4hash(newpswd, new_nt_hash);
2097 /* E_deshash returns false for 'long'
2098 passwords (> 14 DOS chars).
2100 Therefore, don't send a buffer
2101 encrypted with the truncated hash
2102 (it could allow an even easier
2103 attack on the password)
2105 Likewise, obey the admin's restriction
2108 rc = gnutls_cipher_init(&cipher_hnd,
2109 GNUTLS_CIPHER_ARCFOUR_128,
2110 &old_nt_key,
2111 NULL);
2112 if (rc < 0) {
2113 DBG_ERR("gnutls_cipher_init failed: %s\n",
2114 gnutls_strerror(rc));
2115 if (rc == GNUTLS_E_UNWANTED_ALGORITHM) {
2116 DBG_ERR("Running in FIPS mode, NTLM blocked\n");
2118 return;
2121 if (lp_client_lanman_auth() &&
2122 E_deshash(newpswd, new_lm_hash) &&
2123 E_deshash(oldpswd, old_lm_hash)) {
2124 new_lm_pswd = data_blob(NULL, 516);
2125 old_lm_hash_enc = data_blob(NULL, 16);
2126 encode_pw_buffer(new_lm_pswd.data, newpswd,
2127 STR_UNICODE);
2129 rc = gnutls_cipher_encrypt(cipher_hnd,
2130 new_lm_pswd.data,
2131 516);
2132 if (rc < 0) {
2133 gnutls_cipher_deinit(cipher_hnd);
2134 return;
2136 rc = E_old_pw_hash(new_nt_hash, old_lm_hash,
2137 old_lm_hash_enc.data);
2138 if (rc != 0) {
2139 DBG_ERR("E_old_pw_hash failed: %s\n",
2140 gnutls_strerror(rc));
2141 return;
2143 } else {
2144 new_lm_pswd.data = NULL;
2145 new_lm_pswd.length = 0;
2146 old_lm_hash_enc.data = NULL;
2147 old_lm_hash_enc.length = 0;
2150 encode_pw_buffer(new_nt_pswd.data, newpswd,
2151 STR_UNICODE);
2153 rc = gnutls_cipher_encrypt(cipher_hnd,
2154 new_nt_pswd.data,
2155 516);
2156 gnutls_cipher_deinit(cipher_hnd);
2157 if (rc < 0) {
2158 return;
2160 rc = E_old_pw_hash(new_nt_hash, old_nt_hash,
2161 old_nt_hash_enc.data);
2162 if (rc != 0) {
2163 DBG_ERR("E_old_pw_hash failed: %s\n",
2164 gnutls_strerror(rc));
2165 return;
2168 ZERO_ARRAY(old_nt_hash);
2169 ZERO_ARRAY(old_lm_hash);
2170 ZERO_ARRAY(new_nt_hash);
2171 ZERO_ARRAY(new_lm_hash);
2174 if (!full_username && !username) {
2175 printf("Error: No username supplied!\n");
2176 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2177 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2178 printf("Error: No NT or LM password "
2179 "blobs supplied!\n");
2180 } else {
2181 char *error_string = NULL;
2183 if (full_username && !username) {
2184 fstring fstr_user;
2185 fstring fstr_domain;
2187 if (!parse_ntlm_auth_domain_user(full_username,
2188 fstr_user,
2189 fstr_domain)) {
2190 /* username might be 'tainted', don't
2191 * print into our new-line
2192 * deleimianted stream */
2193 printf("Error: Could not "
2194 "parse into domain and "
2195 "username\n");
2196 SAFE_FREE(username);
2197 username = smb_xstrdup(full_username);
2198 } else {
2199 SAFE_FREE(username);
2200 SAFE_FREE(domain);
2201 username = smb_xstrdup(fstr_user);
2202 domain = smb_xstrdup(fstr_domain);
2207 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2208 username, domain,
2209 new_nt_pswd,
2210 old_nt_hash_enc,
2211 new_lm_pswd,
2212 old_lm_hash_enc,
2213 &error_string))) {
2214 printf("Password-Change: No\n");
2215 printf("Password-Change-Error: %s\n.\n",
2216 error_string);
2217 } else {
2218 printf("Password-Change: Yes\n");
2221 SAFE_FREE(error_string);
2223 /* clear out the state */
2224 new_nt_pswd = data_blob_null;
2225 old_nt_hash_enc = data_blob_null;
2226 new_lm_pswd = data_blob_null;
2227 old_nt_hash_enc = data_blob_null;
2228 SAFE_FREE(full_username);
2229 SAFE_FREE(username);
2230 SAFE_FREE(domain);
2231 SAFE_FREE(newpswd);
2232 SAFE_FREE(oldpswd);
2233 printf(".\n");
2235 return;
2238 request = buf;
2240 /* Indicates a base64 encoded structure */
2241 parameter = strstr_m(request, ":: ");
2242 if (!parameter) {
2243 parameter = strstr_m(request, ": ");
2245 if (!parameter) {
2246 DEBUG(0, ("Parameter not found!\n"));
2247 printf("Error: Parameter not found!\n.\n");
2248 return;
2251 parameter[0] ='\0';
2252 parameter++;
2253 parameter[0] ='\0';
2254 parameter++;
2255 } else {
2256 parameter[0] ='\0';
2257 parameter++;
2258 parameter[0] ='\0';
2259 parameter++;
2260 parameter[0] ='\0';
2261 parameter++;
2263 base64_decode_inplace(parameter);
2266 if (strequal(request, "new-nt-password-blob")) {
2267 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2268 if (new_nt_pswd.length != 516) {
2269 printf("Error: hex decode of %s failed! "
2270 "(got %d bytes, expected 516)\n.\n",
2271 parameter,
2272 (int)new_nt_pswd.length);
2273 new_nt_pswd = data_blob_null;
2275 } else if (strequal(request, "old-nt-hash-blob")) {
2276 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2277 if (old_nt_hash_enc.length != 16) {
2278 printf("Error: hex decode of %s failed! "
2279 "(got %d bytes, expected 16)\n.\n",
2280 parameter,
2281 (int)old_nt_hash_enc.length);
2282 old_nt_hash_enc = data_blob_null;
2284 } else if (strequal(request, "new-lm-password-blob")) {
2285 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2286 if (new_lm_pswd.length != 516) {
2287 printf("Error: hex decode of %s failed! "
2288 "(got %d bytes, expected 516)\n.\n",
2289 parameter,
2290 (int)new_lm_pswd.length);
2291 new_lm_pswd = data_blob_null;
2294 else if (strequal(request, "old-lm-hash-blob")) {
2295 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2296 if (old_lm_hash_enc.length != 16)
2298 printf("Error: hex decode of %s failed! "
2299 "(got %d bytes, expected 16)\n.\n",
2300 parameter,
2301 (int)old_lm_hash_enc.length);
2302 old_lm_hash_enc = data_blob_null;
2304 } else if (strequal(request, "nt-domain")) {
2305 domain = smb_xstrdup(parameter);
2306 } else if(strequal(request, "username")) {
2307 username = smb_xstrdup(parameter);
2308 } else if(strequal(request, "full-username")) {
2309 username = smb_xstrdup(parameter);
2310 } else if(strequal(request, "new-password")) {
2311 newpswd = smb_xstrdup(parameter);
2312 } else if (strequal(request, "old-password")) {
2313 oldpswd = smb_xstrdup(parameter);
2314 } else {
2315 printf("Error: Unknown request %s\n.\n", request);
2319 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2320 struct loadparm_context *lp_ctx,
2321 struct ntlm_auth_state *state,
2322 stdio_helper_function fn, void **private2)
2324 char *buf;
2325 char tmp[INITIAL_BUFFER_SIZE+1];
2326 int length, buf_size = 0;
2327 char *c;
2329 buf = talloc_strdup(state->mem_ctx, "");
2330 if (!buf) {
2331 DEBUG(0, ("Failed to allocate input buffer.\n"));
2332 fprintf(stderr, "ERR\n");
2333 exit(1);
2336 do {
2338 /* this is not a typo - x_fgets doesn't work too well under
2339 * squid */
2340 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2341 if (ferror(stdin)) {
2342 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2343 "(%s)\n", ferror(stdin),
2344 strerror(ferror(stdin))));
2346 exit(1);
2348 exit(0);
2351 buf = talloc_strdup_append_buffer(buf, tmp);
2352 buf_size += INITIAL_BUFFER_SIZE;
2354 if (buf_size > MAX_BUFFER_SIZE) {
2355 DEBUG(2, ("Oversized message\n"));
2356 fprintf(stderr, "ERR\n");
2357 talloc_free(buf);
2358 return;
2361 c = strchr(buf, '\n');
2362 } while (c == NULL);
2364 *c = '\0';
2365 length = c-buf;
2367 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2369 if (buf[0] == '\0') {
2370 DEBUG(2, ("Invalid Request\n"));
2371 fprintf(stderr, "ERR\n");
2372 talloc_free(buf);
2373 return;
2376 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2377 talloc_free(buf);
2381 static void squid_stream(enum stdio_helper_mode stdio_mode,
2382 struct loadparm_context *lp_ctx,
2383 stdio_helper_function fn) {
2384 TALLOC_CTX *mem_ctx;
2385 struct ntlm_auth_state *state;
2387 /* initialize FDescs */
2388 setbuf(stdout, NULL);
2389 setbuf(stderr, NULL);
2391 mem_ctx = talloc_init("ntlm_auth");
2392 if (!mem_ctx) {
2393 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2394 fprintf(stderr, "ERR\n");
2395 exit(1);
2398 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2399 if (!state) {
2400 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2401 fprintf(stderr, "ERR\n");
2402 exit(1);
2405 state->mem_ctx = mem_ctx;
2406 state->helper_mode = stdio_mode;
2408 while(1) {
2409 TALLOC_CTX *frame = talloc_stackframe();
2410 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2411 TALLOC_FREE(frame);
2416 /* Authenticate a user with a challenge/response */
2418 static bool check_auth_crap(void)
2420 NTSTATUS nt_status;
2421 uint32_t flags = 0;
2422 char lm_key[8];
2423 char user_session_key[16];
2424 char *hex_lm_key;
2425 char *hex_user_session_key;
2426 char *error_string;
2427 uint8_t authoritative = 1;
2429 setbuf(stdout, NULL);
2431 if (request_lm_key)
2432 flags |= WBFLAG_PAM_LMKEY;
2434 if (request_user_session_key)
2435 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2437 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2439 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2440 opt_workstation,
2441 &opt_challenge,
2442 &opt_lm_response,
2443 &opt_nt_response,
2444 flags, 0,
2445 (unsigned char *)lm_key,
2446 (unsigned char *)user_session_key,
2447 &authoritative,
2448 &error_string, NULL);
2450 if (!NT_STATUS_IS_OK(nt_status)) {
2451 printf("%s (0x%x)\n", error_string,
2452 NT_STATUS_V(nt_status));
2453 SAFE_FREE(error_string);
2454 return False;
2457 if (request_lm_key
2458 && (!all_zero((uint8_t *)lm_key, sizeof(lm_key)))) {
2459 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2460 sizeof(lm_key));
2461 printf("LM_KEY: %s\n", hex_lm_key);
2462 TALLOC_FREE(hex_lm_key);
2464 if (request_user_session_key
2465 && (!all_zero((uint8_t *)user_session_key,
2466 sizeof(user_session_key)))) {
2467 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2468 sizeof(user_session_key));
2469 printf("NT_KEY: %s\n", hex_user_session_key);
2470 TALLOC_FREE(hex_user_session_key);
2473 return True;
2476 /* Main program */
2478 enum {
2479 OPT_USERNAME = 1000,
2480 OPT_DOMAIN,
2481 OPT_WORKSTATION,
2482 OPT_CHALLENGE,
2483 OPT_RESPONSE,
2484 OPT_LM,
2485 OPT_NT,
2486 OPT_PASSWORD,
2487 OPT_LM_KEY,
2488 OPT_USER_SESSION_KEY,
2489 OPT_DIAGNOSTICS,
2490 OPT_REQUIRE_MEMBERSHIP,
2491 OPT_USE_CACHED_CREDS,
2492 OPT_ALLOW_MSCHAPV2,
2493 OPT_PAM_WINBIND_CONF,
2494 OPT_TARGET_SERVICE,
2495 OPT_TARGET_HOSTNAME,
2496 OPT_OFFLINE_LOGON
2499 int main(int argc, const char **argv)
2501 TALLOC_CTX *frame = talloc_stackframe();
2502 int opt;
2503 const char *helper_protocol = NULL;
2504 int diagnostics = 0;
2506 const char *hex_challenge = NULL;
2507 const char *hex_lm_response = NULL;
2508 const char *hex_nt_response = NULL;
2509 struct loadparm_context *lp_ctx;
2510 poptContext pc;
2511 bool ok;
2513 /* NOTE: DO NOT change this interface without considering the implications!
2514 This is an external interface, which other programs will use to interact
2515 with this helper.
2518 /* We do not use single-letter command abbreviations, because they harm future
2519 interface stability. */
2521 struct poptOption long_options[] = {
2522 POPT_AUTOHELP
2524 .longName = "helper-protocol",
2525 .shortName = 0,
2526 .argInfo = POPT_ARG_STRING,
2527 .arg = &helper_protocol,
2528 .val = OPT_DOMAIN,
2529 .descrip = "operate as a stdio-based helper",
2530 .argDescrip = "helper protocol to use"
2533 .longName = "username",
2534 .shortName = 0,
2535 .argInfo = POPT_ARG_STRING,
2536 .arg = &opt_username,
2537 .val = OPT_USERNAME,
2538 .descrip = "username"
2541 .longName = "domain",
2542 .shortName = 0,
2543 .argInfo = POPT_ARG_STRING,
2544 .arg = &opt_domain,
2545 .val = OPT_DOMAIN,
2546 .descrip = "domain name"
2549 .longName = "workstation",
2550 .shortName = 0,
2551 .argInfo = POPT_ARG_STRING,
2552 .arg = &opt_workstation,
2553 .val = OPT_WORKSTATION,
2554 .descrip = "workstation"
2557 .longName = "challenge",
2558 .shortName = 0,
2559 .argInfo = POPT_ARG_STRING,
2560 .arg = &hex_challenge,
2561 .val = OPT_CHALLENGE,
2562 .descrip = "challenge (HEX encoded)"
2565 .longName = "lm-response",
2566 .shortName = 0,
2567 .argInfo = POPT_ARG_STRING,
2568 .arg = &hex_lm_response,
2569 .val = OPT_LM,
2570 .descrip = "LM Response to the challenge (HEX encoded)"
2573 .longName = "nt-response",
2574 .shortName = 0,
2575 .argInfo = POPT_ARG_STRING,
2576 .arg = &hex_nt_response,
2577 .val = OPT_NT,
2578 .descrip = "NT or NTLMv2 Response to the challenge (HEX encoded)"
2581 .longName = "password",
2582 .shortName = 0,
2583 .argInfo = POPT_ARG_STRING,
2584 .arg = &opt_password,
2585 .val = OPT_PASSWORD,
2586 .descrip = "User's plaintext password"
2589 .longName = "request-lm-key",
2590 .shortName = 0,
2591 .argInfo = POPT_ARG_NONE,
2592 .arg = &request_lm_key,
2593 .val = OPT_LM_KEY,
2594 .descrip = "Retrieve LM session key (or, with --diagnostics, expect LM support)"
2597 .longName = "request-nt-key",
2598 .shortName = 0,
2599 .argInfo = POPT_ARG_NONE,
2600 .arg = &request_user_session_key,
2601 .val = OPT_USER_SESSION_KEY,
2602 .descrip = "Retrieve User (NT) session key"
2605 .longName = "use-cached-creds",
2606 .shortName = 0,
2607 .argInfo = POPT_ARG_NONE,
2608 .arg = &use_cached_creds,
2609 .val = OPT_USE_CACHED_CREDS,
2610 .descrip = "Use cached credentials if no password is given"
2613 .longName = "allow-mschapv2",
2614 .shortName = 0,
2615 .argInfo = POPT_ARG_NONE,
2616 .arg = &opt_allow_mschapv2,
2617 .val = OPT_ALLOW_MSCHAPV2,
2618 .descrip = "Explicitly allow MSCHAPv2",
2621 .longName = "offline-logon",
2622 .shortName = 0,
2623 .argInfo = POPT_ARG_NONE,
2624 .arg = &offline_logon,
2625 .val = OPT_OFFLINE_LOGON,
2626 .descrip = "Use cached passwords when DC is offline"
2629 .longName = "diagnostics",
2630 .shortName = 0,
2631 .argInfo = POPT_ARG_NONE,
2632 .arg = &diagnostics,
2633 .val = OPT_DIAGNOSTICS,
2634 .descrip = "Perform diagnostics on the authentication chain"
2637 .longName = "require-membership-of",
2638 .shortName = 0,
2639 .argInfo = POPT_ARG_STRING,
2640 .arg = &require_membership_of,
2641 .val = OPT_REQUIRE_MEMBERSHIP,
2642 .descrip = "Require that a user be a member of this group (either name or SID) for authentication to succeed",
2645 .longName = "pam-winbind-conf",
2646 .shortName = 0,
2647 .argInfo = POPT_ARG_STRING,
2648 .arg = &opt_pam_winbind_conf,
2649 .val = OPT_PAM_WINBIND_CONF,
2650 .descrip = "Require that request must set WBFLAG_PAM_CONTACT_TRUSTDOM when krb5 auth is required",
2653 .longName = "target-service",
2654 .shortName = 0,
2655 .argInfo = POPT_ARG_STRING,
2656 .arg = &opt_target_service,
2657 .val = OPT_TARGET_SERVICE,
2658 .descrip = "Target service (eg http)",
2661 .longName = "target-hostname",
2662 .shortName = 0,
2663 .argInfo = POPT_ARG_STRING,
2664 .arg = &opt_target_hostname,
2665 .val = OPT_TARGET_HOSTNAME,
2666 .descrip = "Target hostname",
2668 POPT_COMMON_DEBUG_ONLY
2669 POPT_COMMON_CONFIG_ONLY
2670 POPT_COMMON_OPTION_ONLY
2671 POPT_COMMON_VERSION
2672 POPT_TABLEEND
2675 /* Samba client initialisation */
2676 smb_init_locale();
2678 ok = samba_cmdline_init(frame,
2679 SAMBA_CMDLINE_CONFIG_CLIENT,
2680 false /* require_smbconf */);
2681 if (!ok) {
2682 DBG_ERR("Failed to init cmdline parser!\n");
2683 TALLOC_FREE(frame);
2684 exit(1);
2687 pc = samba_popt_get_context(getprogname(),
2688 argc,
2689 argv,
2690 long_options,
2691 POPT_CONTEXT_KEEP_FIRST);
2692 if (pc == NULL) {
2693 DBG_ERR("Failed to setup popt context!\n");
2694 TALLOC_FREE(frame);
2695 exit(1);
2698 while((opt = poptGetNextOpt(pc)) != -1) {
2699 switch (opt) {
2700 case OPT_CHALLENGE:
2701 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2702 if (opt_challenge.length != 8) {
2703 fprintf(stderr, "hex decode of %s failed! "
2704 "(only got %d bytes)\n",
2705 hex_challenge,
2706 (int)opt_challenge.length);
2707 exit(1);
2709 break;
2710 case OPT_LM:
2711 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2712 if (opt_lm_response.length != 24) {
2713 fprintf(stderr, "hex decode of %s failed! "
2714 "(only got %d bytes)\n",
2715 hex_lm_response,
2716 (int)opt_lm_response.length);
2717 exit(1);
2719 break;
2721 case OPT_NT:
2722 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2723 if (opt_nt_response.length < 24) {
2724 fprintf(stderr, "hex decode of %s failed! "
2725 "(only got %d bytes)\n",
2726 hex_nt_response,
2727 (int)opt_nt_response.length);
2728 exit(1);
2730 break;
2732 case OPT_REQUIRE_MEMBERSHIP:
2733 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2734 require_membership_of_sid = require_membership_of;
2736 break;
2738 case POPT_ERROR_BADOPT:
2739 fprintf(stderr, "\nInvalid option %s: %s\n\n",
2740 poptBadOption(pc, 0), poptStrerror(opt));
2741 poptPrintUsage(pc, stderr, 0);
2742 exit(1);
2746 if (opt_username) {
2747 char *domain = SMB_STRDUP(opt_username);
2748 char *p = strchr_m(domain, *lp_winbind_separator());
2749 if (p) {
2750 opt_username = p+1;
2751 *p = '\0';
2752 if (opt_domain && !strequal(opt_domain, domain)) {
2753 fprintf(stderr, "Domain specified in username (%s) "
2754 "doesn't match specified domain (%s)!\n\n",
2755 domain, opt_domain);
2756 poptPrintHelp(pc, stderr, 0);
2757 exit(1);
2759 opt_domain = domain;
2760 } else {
2761 SAFE_FREE(domain);
2765 /* Note: if opt_domain is "" then send no domain */
2766 if (opt_domain == NULL) {
2767 opt_domain = get_winbind_domain();
2770 if (opt_workstation == NULL) {
2771 opt_workstation = "";
2774 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2775 if (lp_ctx == NULL) {
2776 fprintf(stderr, "loadparm_init_s3() failed!\n");
2777 exit(1);
2780 if (helper_protocol) {
2781 int i;
2782 for (i=0; i<NUM_HELPER_MODES; i++) {
2783 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2784 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2785 exit(0);
2788 fprintf(stderr, "unknown helper protocol [%s]\n\n"
2789 "Valid helper protools:\n\n", helper_protocol);
2791 for (i=0; i<NUM_HELPER_MODES; i++) {
2792 fprintf(stderr, "%s\n",
2793 stdio_helper_protocols[i].name);
2796 exit(1);
2799 if (!opt_username || !*opt_username) {
2800 fprintf(stderr, "username must be specified!\n\n");
2801 poptPrintHelp(pc, stderr, 0);
2802 exit(1);
2805 if (opt_challenge.length) {
2806 if (!check_auth_crap()) {
2807 exit(1);
2809 exit(0);
2812 if (!opt_password) {
2813 char pwd[256] = {0};
2814 int rc;
2816 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2817 if (rc == 0) {
2818 opt_password = SMB_STRDUP(pwd);
2822 if (diagnostics) {
2823 if (!diagnose_ntlm_auth(request_lm_key)) {
2824 poptFreeContext(pc);
2825 return 1;
2827 } else {
2828 fstring user;
2830 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2831 if (!check_plaintext_auth(user, opt_password, True)) {
2832 poptFreeContext(pc);
2833 return 1;
2837 /* Exit code */
2839 poptFreeContext(pc);
2840 TALLOC_FREE(frame);
2841 return 0;