CVE-2018-1139 s3-utils: use enum ntlm_auth_level in ntlm_password_check().
[Samba.git] / source3 / utils / ntlm_auth.c
blob8f77680416feaff90b4ad6cb5630610cd1ed6150
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 printf("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 printf("BH Out of memory\n");
213 data_blob_free(&in);
214 return;
217 printf("OK\n");
218 data_blob_free(&in);
219 return;
221 DEBUG(1, ("Asked for (and expected) a password\n"));
222 printf("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 fprintf(stderr, "ERR\n");
242 exit(1);
245 state->mem_ctx = state;
247 /* Ask for a password */
248 printf("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 wbcInterfaceDetails *details;
284 wbcErr ret;
285 static bool got_sep;
286 static char sep;
288 if (got_sep)
289 return sep;
291 ret = wbcInterfaceDetails(&details);
292 if (!WBC_ERROR_IS_OK(ret)) {
293 d_fprintf(stderr, "could not obtain winbind separator!\n");
294 return *lp_winbind_separator();
297 sep = details->winbind_separator;
299 wbcFreeMemory(details);
301 got_sep = True;
303 if (!sep) {
304 d_fprintf(stderr, "winbind separator was NULL!\n");
305 return *lp_winbind_separator();
308 return sep;
311 const char *get_winbind_domain(void)
313 struct wbcInterfaceDetails *details;
314 wbcErr ret;
316 static fstring winbind_domain;
317 if (*winbind_domain) {
318 return winbind_domain;
321 /* Send off request */
323 ret = wbcInterfaceDetails(&details);
324 if (!WBC_ERROR_IS_OK(ret)) {
325 DEBUG(1, ("could not obtain winbind domain name!\n"));
326 return lp_workgroup();
329 fstrcpy(winbind_domain, details->netbios_domain);
331 wbcFreeMemory(details);
333 return winbind_domain;
337 const char *get_winbind_netbios_name(void)
339 struct wbcInterfaceDetails *details;
340 wbcErr ret;
342 static fstring winbind_netbios_name;
344 if (*winbind_netbios_name) {
345 return winbind_netbios_name;
348 /* Send off request */
350 ret = wbcInterfaceDetails(&details);
351 if (!WBC_ERROR_IS_OK(ret)) {
352 DEBUG(1, ("could not obtain winbind netbios name!\n"));
353 return lp_netbios_name();
356 fstrcpy(winbind_netbios_name, details->netbios_name);
358 wbcFreeMemory(details);
360 return winbind_netbios_name;
364 DATA_BLOB get_challenge(void)
366 static DATA_BLOB chal;
367 if (opt_challenge.length)
368 return opt_challenge;
370 chal = data_blob(NULL, 8);
372 generate_random_buffer(chal.data, chal.length);
373 return chal;
376 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
377 form DOMAIN/user into a domain and a user */
379 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
380 fstring user)
383 char *p = strchr(domuser,winbind_separator());
385 if (!p) {
386 return False;
389 fstrcpy(user, p+1);
390 fstrcpy(domain, domuser);
391 domain[PTR_DIFF(p, domuser)] = 0;
392 return strupper_m(domain);
395 static bool get_require_membership_sid(void) {
396 fstring domain, name, sidbuf;
397 struct wbcDomainSid sid;
398 enum wbcSidType type;
399 wbcErr ret;
401 if (!require_membership_of) {
402 return True;
405 if (require_membership_of_sid) {
406 return True;
409 /* Otherwise, ask winbindd for the name->sid request */
411 if (!parse_ntlm_auth_domain_user(require_membership_of,
412 domain, name)) {
413 DEBUG(0, ("Could not parse %s into separate domain/name parts!\n",
414 require_membership_of));
415 return False;
418 ret = wbcLookupName(domain, name, &sid, &type);
419 if (!WBC_ERROR_IS_OK(ret)) {
420 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
421 require_membership_of));
422 return False;
425 wbcSidToStringBuf(&sid, sidbuf, sizeof(sidbuf));
427 require_membership_of_sid = SMB_STRDUP(sidbuf);
429 if (require_membership_of_sid)
430 return True;
432 return False;
436 * Get some configuration from pam_winbind.conf to see if we
437 * need to contact trusted domain
440 int get_pam_winbind_config()
442 int ctrl = 0;
443 struct tiniparser_dictionary *d = NULL;
445 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
446 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
449 d = tiniparser_load(opt_pam_winbind_conf);
451 if (!d) {
452 return 0;
455 if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
456 ctrl |= WINBIND_KRB5_AUTH;
459 tiniparser_freedict(d);
461 return ctrl;
464 /* Authenticate a user with a plaintext password */
466 static bool check_plaintext_auth(const char *user, const char *pass,
467 bool stdout_diagnostics)
469 struct winbindd_request request;
470 struct winbindd_response response;
471 NSS_STATUS result;
473 if (!get_require_membership_sid()) {
474 return False;
477 /* Send off request */
479 ZERO_STRUCT(request);
480 ZERO_STRUCT(response);
482 fstrcpy(request.data.auth.user, user);
483 fstrcpy(request.data.auth.pass, pass);
484 if (require_membership_of_sid) {
485 strlcpy(request.data.auth.require_membership_of_sid,
486 require_membership_of_sid,
487 sizeof(request.data.auth.require_membership_of_sid));
490 if (offline_logon) {
491 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
494 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH, &request, &response);
496 /* Display response */
498 if (stdout_diagnostics) {
499 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
500 d_fprintf(stderr, "Reading winbind reply failed! (0x01)\n");
503 d_printf("%s: %s (0x%x)\n",
504 response.data.auth.nt_status_string,
505 response.data.auth.error_string,
506 response.data.auth.nt_status);
507 } else {
508 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
509 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
512 DEBUG(3, ("%s: %s (0x%x)\n",
513 response.data.auth.nt_status_string,
514 response.data.auth.error_string,
515 response.data.auth.nt_status));
518 return (result == NSS_STATUS_SUCCESS);
521 /* authenticate a user with an encrypted username/password */
523 NTSTATUS contact_winbind_auth_crap(const char *username,
524 const char *domain,
525 const char *workstation,
526 const DATA_BLOB *challenge,
527 const DATA_BLOB *lm_response,
528 const DATA_BLOB *nt_response,
529 uint32_t flags,
530 uint32_t extra_logon_parameters,
531 uint8_t lm_key[8],
532 uint8_t user_session_key[16],
533 uint8_t *pauthoritative,
534 char **error_string,
535 char **unix_name)
537 NTSTATUS nt_status;
538 NSS_STATUS result;
539 struct winbindd_request request;
540 struct winbindd_response response;
542 *pauthoritative = 1;
544 if (!get_require_membership_sid()) {
545 return NT_STATUS_INVALID_PARAMETER;
548 ZERO_STRUCT(request);
549 ZERO_STRUCT(response);
551 request.flags = flags;
553 request.data.auth_crap.logon_parameters = extra_logon_parameters
554 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
556 if (opt_allow_mschapv2) {
557 request.data.auth_crap.logon_parameters |= MSV1_0_ALLOW_MSVCHAPV2;
560 if (require_membership_of_sid)
561 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
563 fstrcpy(request.data.auth_crap.user, username);
564 fstrcpy(request.data.auth_crap.domain, domain);
566 fstrcpy(request.data.auth_crap.workstation,
567 workstation);
569 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
571 if (lm_response && lm_response->length) {
572 memcpy(request.data.auth_crap.lm_resp,
573 lm_response->data,
574 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
575 request.data.auth_crap.lm_resp_len = lm_response->length;
578 if (nt_response && nt_response->length) {
579 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
580 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
581 request.extra_len = nt_response->length;
582 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
583 if (request.extra_data.data == NULL) {
584 return NT_STATUS_NO_MEMORY;
586 memcpy(request.extra_data.data, nt_response->data,
587 nt_response->length);
589 } else {
590 memcpy(request.data.auth_crap.nt_resp,
591 nt_response->data, nt_response->length);
593 request.data.auth_crap.nt_resp_len = nt_response->length;
596 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH_CRAP, &request, &response);
597 SAFE_FREE(request.extra_data.data);
599 /* Display response */
601 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
602 nt_status = NT_STATUS_UNSUCCESSFUL;
603 if (error_string)
604 *error_string = smb_xstrdup("Reading winbind reply failed!");
605 winbindd_free_response(&response);
606 return nt_status;
609 nt_status = (NT_STATUS(response.data.auth.nt_status));
610 if (!NT_STATUS_IS_OK(nt_status)) {
611 if (error_string)
612 *error_string = smb_xstrdup(response.data.auth.error_string);
613 *pauthoritative = response.data.auth.authoritative;
614 winbindd_free_response(&response);
615 return nt_status;
618 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
619 memcpy(lm_key, response.data.auth.first_8_lm_hash,
620 sizeof(response.data.auth.first_8_lm_hash));
622 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
623 memcpy(user_session_key, response.data.auth.user_session_key,
624 sizeof(response.data.auth.user_session_key));
627 if (flags & WBFLAG_PAM_UNIX_NAME) {
628 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
629 if (!*unix_name) {
630 winbindd_free_response(&response);
631 return NT_STATUS_NO_MEMORY;
635 winbindd_free_response(&response);
636 return nt_status;
639 /* contact server to change user password using auth crap */
640 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
641 const char *domain,
642 const DATA_BLOB new_nt_pswd,
643 const DATA_BLOB old_nt_hash_enc,
644 const DATA_BLOB new_lm_pswd,
645 const DATA_BLOB old_lm_hash_enc,
646 char **error_string)
648 NTSTATUS nt_status;
649 NSS_STATUS result;
650 struct winbindd_request request;
651 struct winbindd_response response;
653 if (!get_require_membership_sid())
655 if(error_string)
656 *error_string = smb_xstrdup("Can't get membership sid.");
657 return NT_STATUS_INVALID_PARAMETER;
660 ZERO_STRUCT(request);
661 ZERO_STRUCT(response);
663 if(username != NULL)
664 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
665 if(domain != NULL)
666 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
668 if(new_nt_pswd.length)
670 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
671 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
674 if(old_nt_hash_enc.length)
676 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));
677 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
680 if(new_lm_pswd.length)
682 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
683 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
686 if(old_lm_hash_enc.length)
688 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));
689 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
692 result = winbindd_request_response(NULL, WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
694 /* Display response */
696 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
698 nt_status = NT_STATUS_UNSUCCESSFUL;
699 if (error_string)
700 *error_string = smb_xstrdup("Reading winbind reply failed!");
701 winbindd_free_response(&response);
702 return nt_status;
705 nt_status = (NT_STATUS(response.data.auth.nt_status));
706 if (!NT_STATUS_IS_OK(nt_status))
708 if (error_string)
709 *error_string = smb_xstrdup(response.data.auth.error_string);
710 winbindd_free_response(&response);
711 return nt_status;
714 winbindd_free_response(&response);
716 return nt_status;
719 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
720 TALLOC_CTX *mem_ctx,
721 void *server_returned_info,
722 const char *original_user_name,
723 uint32_t session_info_flags,
724 struct auth_session_info **session_info_out)
726 const char *unix_username = (const char *)server_returned_info;
727 bool ok;
728 struct dom_sid *sids = NULL;
729 struct auth_session_info *session_info = NULL;
731 session_info = talloc_zero(mem_ctx, struct auth_session_info);
732 if (session_info == NULL) {
733 return NT_STATUS_NO_MEMORY;
736 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
737 if (session_info->unix_info == NULL) {
738 TALLOC_FREE(session_info);
739 return NT_STATUS_NO_MEMORY;
741 session_info->unix_info->unix_name = talloc_strdup(session_info->unix_info,
742 unix_username);
743 if (session_info->unix_info->unix_name == NULL) {
744 TALLOC_FREE(session_info);
745 return NT_STATUS_NO_MEMORY;
748 session_info->security_token = talloc_zero(session_info, struct security_token);
749 if (session_info->security_token == NULL) {
750 TALLOC_FREE(session_info);
751 return NT_STATUS_NO_MEMORY;
754 sids = talloc_zero_array(session_info->security_token,
755 struct dom_sid, 3);
756 if (sids == NULL) {
757 TALLOC_FREE(session_info);
758 return NT_STATUS_NO_MEMORY;
760 ok = dom_sid_parse(SID_WORLD, &sids[0]);
761 if (!ok) {
762 TALLOC_FREE(session_info);
763 return NT_STATUS_INTERNAL_ERROR;
765 ok = dom_sid_parse(SID_NT_NETWORK, &sids[1]);
766 if (!ok) {
767 TALLOC_FREE(session_info);
768 return NT_STATUS_INTERNAL_ERROR;
770 ok = dom_sid_parse(SID_NT_AUTHENTICATED_USERS, &sids[2]);
771 if (!ok) {
772 TALLOC_FREE(session_info);
773 return NT_STATUS_INTERNAL_ERROR;
776 session_info->security_token->num_sids = talloc_array_length(sids);
777 session_info->security_token->sids = sids;
779 *session_info_out = session_info;
781 return NT_STATUS_OK;
784 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
785 TALLOC_CTX *mem_ctx,
786 struct smb_krb5_context *smb_krb5_context,
787 DATA_BLOB *pac_blob,
788 const char *princ_name,
789 const struct tsocket_address *remote_address,
790 uint32_t session_info_flags,
791 struct auth_session_info **session_info)
793 TALLOC_CTX *tmp_ctx;
794 struct PAC_LOGON_INFO *logon_info = NULL;
795 char *unixuser;
796 NTSTATUS status;
797 char *domain = NULL;
798 char *realm = NULL;
799 char *user = NULL;
800 char *p;
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;
819 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
821 p = strchr_m(princ_name, '@');
822 if (!p) {
823 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
824 princ_name));
825 return NT_STATUS_LOGON_FAILURE;
828 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
829 if (!user) {
830 return NT_STATUS_NO_MEMORY;
833 realm = talloc_strdup(talloc_tos(), p + 1);
834 if (!realm) {
835 return NT_STATUS_NO_MEMORY;
838 if (!strequal(realm, lp_realm())) {
839 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
840 if (!lp_allow_trusted_domains()) {
841 return NT_STATUS_LOGON_FAILURE;
845 if (logon_info && logon_info->info3.base.logon_domain.string) {
846 domain = talloc_strdup(mem_ctx,
847 logon_info->info3.base.logon_domain.string);
848 if (!domain) {
849 return NT_STATUS_NO_MEMORY;
851 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
852 } else {
854 /* If we have winbind running, we can (and must) shorten the
855 username by using the short netbios name. Otherwise we will
856 have inconsistent user names. With Kerberos, we get the
857 fully qualified realm, with ntlmssp we get the short
858 name. And even w2k3 does use ntlmssp if you for example
859 connect to an ip address. */
861 wbcErr wbc_status;
862 struct wbcDomainInfo *info = NULL;
864 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
865 realm));
867 wbc_status = wbcDomainInfo(realm, &info);
869 if (WBC_ERROR_IS_OK(wbc_status)) {
870 domain = talloc_strdup(mem_ctx,
871 info->short_name);
872 wbcFreeMemory(info);
873 } else {
874 DEBUG(3, ("Could not find short name: %s\n",
875 wbcErrorString(wbc_status)));
876 domain = talloc_strdup(mem_ctx, realm);
878 if (!domain) {
879 return NT_STATUS_NO_MEMORY;
881 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
884 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
885 if (!unixuser) {
886 status = NT_STATUS_NO_MEMORY;
887 goto done;
890 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
892 done:
893 TALLOC_FREE(tmp_ctx);
894 return status;
900 * Return the challenge as determined by the authentication subsystem
901 * @return an 8 byte random challenge
904 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
905 uint8_t chal[8])
907 if (auth_ctx->challenge.data.length == 8) {
908 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
909 auth_ctx->challenge.set_by));
910 memcpy(chal, auth_ctx->challenge.data.data, 8);
911 return NT_STATUS_OK;
914 if (!auth_ctx->challenge.set_by) {
915 generate_random_buffer(chal, 8);
917 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
918 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
919 auth_ctx->challenge.set_by = "random";
922 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
923 auth_ctx->challenge.set_by));
925 return NT_STATUS_OK;
929 * NTLM2 authentication modifies the effective challenge,
930 * @param challenge The new challenge value
932 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
934 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
935 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
937 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
938 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
940 return NT_STATUS_OK;
944 * Check the password on an NTLMSSP login.
946 * Return the session keys used on the connection.
949 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
950 TALLOC_CTX *mem_ctx,
951 const struct auth_usersupplied_info *user_info,
952 uint8_t *pauthoritative,
953 void **server_returned_info,
954 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
956 NTSTATUS nt_status;
957 char *error_string = NULL;
958 uint8_t lm_key[8];
959 uint8_t user_sess_key[16];
960 char *unix_name = NULL;
962 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
963 user_info->workstation_name,
964 &auth4_context->challenge.data,
965 &user_info->password.response.lanman,
966 &user_info->password.response.nt,
967 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
969 lm_key, user_sess_key,
970 pauthoritative,
971 &error_string, &unix_name);
973 if (NT_STATUS_IS_OK(nt_status)) {
974 if (!all_zero(lm_key, 8)) {
975 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
976 memcpy(lm_session_key->data, lm_key, 8);
977 memset(lm_session_key->data+8, '\0', 8);
980 if (!all_zero(user_sess_key, 16)) {
981 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
983 *server_returned_info = talloc_strdup(mem_ctx,
984 unix_name);
985 } else {
986 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
987 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
988 user_info->client.domain_name, user_info->client.account_name,
989 user_info->workstation_name,
990 error_string ? error_string : "unknown error (NULL)"));
993 SAFE_FREE(error_string);
994 SAFE_FREE(unix_name);
995 return nt_status;
998 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
999 TALLOC_CTX *mem_ctx,
1000 const struct auth_usersupplied_info *user_info,
1001 uint8_t *pauthoritative,
1002 void **server_returned_info,
1003 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
1005 NTSTATUS nt_status;
1006 struct samr_Password lm_pw, nt_pw;
1008 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1010 *pauthoritative = 1;
1012 nt_status = ntlm_password_check(mem_ctx,
1013 true, NTLM_AUTH_ON, 0,
1014 &auth4_context->challenge.data,
1015 &user_info->password.response.lanman,
1016 &user_info->password.response.nt,
1017 user_info->client.account_name,
1018 user_info->client.account_name,
1019 user_info->client.domain_name,
1020 &lm_pw, &nt_pw, session_key, lm_session_key);
1022 if (NT_STATUS_IS_OK(nt_status)) {
1023 *server_returned_info = talloc_asprintf(mem_ctx,
1024 "%s%c%s", user_info->client.domain_name,
1025 *lp_winbind_separator(),
1026 user_info->client.account_name);
1027 } else {
1028 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
1029 user_info->client.domain_name, user_info->client.account_name,
1030 user_info->workstation_name,
1031 nt_errstr(nt_status)));
1033 return nt_status;
1036 static NTSTATUS ntlm_auth_prepare_gensec_client(TALLOC_CTX *mem_ctx,
1037 struct loadparm_context *lp_ctx,
1038 struct gensec_security **gensec_security_out)
1040 struct gensec_security *gensec_security = NULL;
1041 NTSTATUS nt_status;
1042 TALLOC_CTX *tmp_ctx;
1043 const struct gensec_security_ops **backends = NULL;
1044 struct gensec_settings *gensec_settings = NULL;
1045 size_t idx = 0;
1047 tmp_ctx = talloc_new(mem_ctx);
1048 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1050 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1051 if (gensec_settings == NULL) {
1052 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1053 TALLOC_FREE(tmp_ctx);
1054 return NT_STATUS_NO_MEMORY;
1057 backends = talloc_zero_array(gensec_settings,
1058 const struct gensec_security_ops *, 4);
1059 if (backends == NULL) {
1060 TALLOC_FREE(tmp_ctx);
1061 return NT_STATUS_NO_MEMORY;
1063 gensec_settings->backends = backends;
1065 gensec_init();
1067 /* These need to be in priority order, krb5 before NTLMSSP */
1068 #if defined(HAVE_KRB5)
1069 backends[idx++] = &gensec_gse_krb5_security_ops;
1070 #endif
1072 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1074 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1076 nt_status = gensec_client_start(NULL, &gensec_security,
1077 gensec_settings);
1078 if (!NT_STATUS_IS_OK(nt_status)) {
1079 TALLOC_FREE(tmp_ctx);
1080 return nt_status;
1083 talloc_unlink(tmp_ctx, gensec_settings);
1085 if (opt_target_service != NULL) {
1086 nt_status = gensec_set_target_service(gensec_security,
1087 opt_target_service);
1088 if (!NT_STATUS_IS_OK(nt_status)) {
1089 TALLOC_FREE(tmp_ctx);
1090 return nt_status;
1094 if (opt_target_hostname != NULL) {
1095 nt_status = gensec_set_target_hostname(gensec_security,
1096 opt_target_hostname);
1097 if (!NT_STATUS_IS_OK(nt_status)) {
1098 TALLOC_FREE(tmp_ctx);
1099 return nt_status;
1103 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1104 TALLOC_FREE(tmp_ctx);
1105 return NT_STATUS_OK;
1108 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1110 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1111 if (auth4_context == NULL) {
1112 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1113 return NULL;
1115 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1116 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1117 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1118 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1119 if (local_pw) {
1120 auth4_context->check_ntlm_password = local_pw_check;
1121 } else {
1122 auth4_context->check_ntlm_password = winbind_pw_check;
1124 auth4_context->private_data = NULL;
1125 return auth4_context;
1128 static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx,
1129 struct loadparm_context *lp_ctx,
1130 struct gensec_security **gensec_security_out)
1132 struct gensec_security *gensec_security;
1133 NTSTATUS nt_status;
1135 TALLOC_CTX *tmp_ctx;
1136 const struct gensec_security_ops **backends;
1137 struct gensec_settings *gensec_settings;
1138 size_t idx = 0;
1139 struct cli_credentials *server_credentials;
1141 struct auth4_context *auth4_context;
1143 tmp_ctx = talloc_new(mem_ctx);
1144 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1146 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1147 if (auth4_context == NULL) {
1148 TALLOC_FREE(tmp_ctx);
1149 return NT_STATUS_NO_MEMORY;
1152 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1153 if (lp_ctx == NULL) {
1154 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1155 TALLOC_FREE(tmp_ctx);
1156 return NT_STATUS_NO_MEMORY;
1160 * This should be a 'netbios domain -> DNS domain'
1161 * mapping, and can currently validly return NULL on
1162 * poorly configured systems.
1164 * This is used for the NTLMSSP server
1167 if (opt_password) {
1168 gensec_settings->server_netbios_name = lp_netbios_name();
1169 gensec_settings->server_netbios_domain = lp_workgroup();
1170 } else {
1171 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1172 gensec_settings->server_netbios_domain = get_winbind_domain();
1175 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1176 get_mydnsdomname(talloc_tos()));
1177 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1178 get_mydnsfullname());
1180 backends = talloc_zero_array(gensec_settings,
1181 const struct gensec_security_ops *, 4);
1183 if (backends == NULL) {
1184 TALLOC_FREE(tmp_ctx);
1185 return NT_STATUS_NO_MEMORY;
1187 gensec_settings->backends = backends;
1189 gensec_init();
1191 /* These need to be in priority order, krb5 before NTLMSSP */
1192 #if defined(HAVE_KRB5)
1193 backends[idx++] = &gensec_gse_krb5_security_ops;
1194 #endif
1196 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1198 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1201 * This is anonymous for now, because we just use it
1202 * to set the kerberos state at the moment
1204 server_credentials = cli_credentials_init_anon(tmp_ctx);
1205 if (!server_credentials) {
1206 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1207 return NT_STATUS_NO_MEMORY;
1210 cli_credentials_set_conf(server_credentials, lp_ctx);
1212 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1213 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1214 } else {
1215 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1218 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1219 auth4_context, &gensec_security);
1221 if (!NT_STATUS_IS_OK(nt_status)) {
1222 TALLOC_FREE(tmp_ctx);
1223 return nt_status;
1226 gensec_set_credentials(gensec_security, server_credentials);
1229 * TODO: Allow the caller to pass their own description here
1230 * via a command-line option
1232 nt_status = gensec_set_target_service_description(gensec_security,
1233 "ntlm_auth");
1234 if (!NT_STATUS_IS_OK(nt_status)) {
1235 TALLOC_FREE(tmp_ctx);
1236 return nt_status;
1239 talloc_unlink(tmp_ctx, lp_ctx);
1240 talloc_unlink(tmp_ctx, server_credentials);
1241 talloc_unlink(tmp_ctx, gensec_settings);
1242 talloc_unlink(tmp_ctx, auth4_context);
1244 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1245 TALLOC_FREE(tmp_ctx);
1246 return NT_STATUS_OK;
1249 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1250 struct loadparm_context *lp_ctx,
1251 struct ntlm_auth_state *state,
1252 char *buf, int length, void **private2)
1254 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1255 return;
1258 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1259 struct loadparm_context *lp_ctx,
1260 struct ntlm_auth_state *state,
1261 char *buf, int length, void **private2)
1263 char *user, *pass;
1264 user=buf;
1266 pass=(char *)memchr(buf,' ',length);
1267 if (!pass) {
1268 DEBUG(2, ("Password not found. Denying access\n"));
1269 printf("ERR\n");
1270 return;
1272 *pass='\0';
1273 pass++;
1275 if (state->helper_mode == SQUID_2_5_BASIC) {
1276 rfc1738_unescape(user);
1277 rfc1738_unescape(pass);
1280 if (check_plaintext_auth(user, pass, False)) {
1281 printf("OK\n");
1282 } else {
1283 printf("ERR\n");
1287 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1288 struct loadparm_context *lp_ctx,
1289 char *buf, int length, void **private1)
1291 DATA_BLOB in;
1292 DATA_BLOB out = data_blob(NULL, 0);
1293 char *out_base64 = NULL;
1294 const char *reply_arg = NULL;
1295 struct gensec_ntlm_state {
1296 struct gensec_security *gensec_state;
1297 const char *set_password;
1299 struct gensec_ntlm_state *state;
1301 NTSTATUS nt_status;
1302 bool first = false;
1303 const char *reply_code;
1304 struct cli_credentials *creds;
1306 static char *want_feature_list = NULL;
1307 static DATA_BLOB session_key;
1309 TALLOC_CTX *mem_ctx;
1311 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1313 if (*private1) {
1314 state = (struct gensec_ntlm_state *)*private1;
1315 } else {
1316 state = talloc_zero(NULL, struct gensec_ntlm_state);
1317 if (!state) {
1318 printf("BH No Memory\n");
1319 exit(1);
1321 *private1 = state;
1322 if (opt_password) {
1323 state->set_password = opt_password;
1327 if (strlen(buf) < 2) {
1328 DEBUG(1, ("query [%s] invalid", buf));
1329 printf("BH Query invalid\n");
1330 talloc_free(mem_ctx);
1331 return;
1334 if (strlen(buf) > 3) {
1335 if(strncmp(buf, "SF ", 3) == 0) {
1336 DEBUG(10, ("Setting flags to negotiate\n"));
1337 talloc_free(want_feature_list);
1338 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1339 printf("OK\n");
1340 talloc_free(mem_ctx);
1341 return;
1343 in = base64_decode_data_blob_talloc(mem_ctx, buf + 3);
1344 } else {
1345 in = data_blob(NULL, 0);
1348 if (strncmp(buf, "YR", 2) == 0) {
1349 if (state->gensec_state) {
1350 talloc_free(state->gensec_state);
1351 state->gensec_state = NULL;
1353 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1354 /* Just return BH, like ntlm_auth from Samba 3 does. */
1355 printf("BH Command expected\n");
1356 talloc_free(mem_ctx);
1357 return;
1358 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1359 (strncmp(buf, "KK ", 3) != 0) &&
1360 (strncmp(buf, "AF ", 3) != 0) &&
1361 (strncmp(buf, "NA ", 3) != 0) &&
1362 (strncmp(buf, "UG", 2) != 0) &&
1363 (strncmp(buf, "PW ", 3) != 0) &&
1364 (strncmp(buf, "GK", 2) != 0) &&
1365 (strncmp(buf, "GF", 2) != 0)) {
1366 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1367 printf("BH SPNEGO request invalid prefix\n");
1368 talloc_free(mem_ctx);
1369 return;
1372 /* setup gensec */
1373 if (!(state->gensec_state)) {
1374 switch (stdio_helper_mode) {
1375 case GSS_SPNEGO_CLIENT:
1377 * cached credentials are only supported by
1378 * NTLMSSP_CLIENT_1 for now.
1380 use_cached_creds = false;
1381 /* fall through */
1382 case NTLMSSP_CLIENT_1:
1383 /* setup the client side */
1385 if (state->set_password != NULL) {
1386 use_cached_creds = false;
1389 if (use_cached_creds) {
1390 struct wbcCredentialCacheParams params;
1391 struct wbcCredentialCacheInfo *info = NULL;
1392 struct wbcAuthErrorInfo *error = NULL;
1393 wbcErr wbc_status;
1395 params.account_name = opt_username;
1396 params.domain_name = opt_domain;
1397 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1398 params.num_blobs = 0;
1399 params.blobs = NULL;
1401 wbc_status = wbcCredentialCache(&params, &info,
1402 &error);
1403 wbcFreeMemory(error);
1404 if (!WBC_ERROR_IS_OK(wbc_status)) {
1405 use_cached_creds = false;
1407 wbcFreeMemory(info);
1410 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1411 &state->gensec_state);
1412 if (!NT_STATUS_IS_OK(nt_status)) {
1413 printf("BH GENSEC mech failed to start: %s\n",
1414 nt_errstr(nt_status));
1415 talloc_free(mem_ctx);
1416 return;
1419 creds = cli_credentials_init(state->gensec_state);
1420 cli_credentials_set_conf(creds, lp_ctx);
1421 if (opt_username) {
1422 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1424 if (opt_domain) {
1425 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1427 if (use_cached_creds) {
1428 gensec_want_feature(state->gensec_state,
1429 GENSEC_FEATURE_NTLM_CCACHE);
1430 } else if (state->set_password) {
1431 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1432 } else {
1433 cli_credentials_set_password_callback(creds, get_password);
1435 if (opt_workstation) {
1436 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1439 gensec_set_credentials(state->gensec_state, creds);
1441 break;
1442 case GSS_SPNEGO_SERVER:
1443 case SQUID_2_5_NTLMSSP:
1445 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1446 &state->gensec_state);
1447 if (!NT_STATUS_IS_OK(nt_status)) {
1448 printf("BH GENSEC mech failed to start: %s\n",
1449 nt_errstr(nt_status));
1450 talloc_free(mem_ctx);
1451 return;
1453 break;
1455 default:
1456 talloc_free(mem_ctx);
1457 abort();
1460 gensec_want_feature_list(state->gensec_state, want_feature_list);
1462 /* Session info is not complete, do not pass to auth log */
1463 gensec_want_feature(state->gensec_state, GENSEC_FEATURE_NO_AUTHZ_LOG);
1465 switch (stdio_helper_mode) {
1466 case GSS_SPNEGO_CLIENT:
1467 case GSS_SPNEGO_SERVER:
1468 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1469 if (!in.length) {
1470 first = true;
1472 break;
1473 case NTLMSSP_CLIENT_1:
1474 if (!in.length) {
1475 first = true;
1477 /* fall through */
1478 case SQUID_2_5_NTLMSSP:
1479 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1480 break;
1481 default:
1482 talloc_free(mem_ctx);
1483 abort();
1486 if (!NT_STATUS_IS_OK(nt_status)) {
1487 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1488 printf("BH GENSEC mech failed to start\n");
1489 talloc_free(mem_ctx);
1490 return;
1495 /* update */
1497 if (strncmp(buf, "PW ", 3) == 0) {
1498 state->set_password = talloc_strndup(state,
1499 (const char *)in.data,
1500 in.length);
1502 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1503 state->set_password,
1504 CRED_SPECIFIED);
1505 printf("OK\n");
1506 talloc_free(mem_ctx);
1507 return;
1510 if (strncmp(buf, "GK", 2) == 0) {
1511 char *base64_key;
1512 DEBUG(10, ("Requested session key\n"));
1513 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1514 if(!NT_STATUS_IS_OK(nt_status)) {
1515 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1516 printf("BH No session key\n");
1517 talloc_free(mem_ctx);
1518 return;
1519 } else {
1520 base64_key = base64_encode_data_blob(state, session_key);
1521 SMB_ASSERT(base64_key != NULL);
1522 printf("GK %s\n", base64_key);
1523 talloc_free(base64_key);
1525 talloc_free(mem_ctx);
1526 return;
1529 if (strncmp(buf, "GF", 2) == 0) {
1530 uint32_t neg_flags;
1532 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1534 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1535 if (neg_flags == 0) {
1536 printf("BH\n");
1537 talloc_free(mem_ctx);
1538 return;
1541 printf("GF 0x%08x\n", neg_flags);
1542 talloc_free(mem_ctx);
1543 return;
1546 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1548 /* don't leak 'bad password'/'no such user' info to the network client */
1549 nt_status = nt_status_squash(nt_status);
1551 if (out.length) {
1552 out_base64 = base64_encode_data_blob(mem_ctx, out);
1553 SMB_ASSERT(out_base64 != NULL);
1554 } else {
1555 out_base64 = NULL;
1558 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1559 reply_arg = "*";
1560 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1561 reply_code = "YR";
1562 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1563 reply_code = "KK";
1564 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1565 reply_code = "TT";
1566 } else {
1567 abort();
1571 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1572 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1573 reply_arg = nt_errstr(nt_status);
1574 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1575 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1576 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1577 reply_arg = nt_errstr(nt_status);
1578 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1579 } else if (!NT_STATUS_IS_OK(nt_status)) {
1580 reply_code = "NA";
1581 reply_arg = nt_errstr(nt_status);
1582 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1583 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1584 struct auth_session_info *session_info;
1586 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1587 if (!NT_STATUS_IS_OK(nt_status)) {
1588 reply_code = "BH Failed to retrive session info";
1589 reply_arg = nt_errstr(nt_status);
1590 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1591 } else {
1593 reply_code = "AF";
1594 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1595 if (reply_arg == NULL) {
1596 reply_code = "BH out of memory";
1597 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1599 talloc_free(session_info);
1601 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1602 reply_code = "AF";
1603 reply_arg = out_base64;
1604 } else {
1605 abort();
1608 switch (stdio_helper_mode) {
1609 case GSS_SPNEGO_SERVER:
1610 printf("%s %s %s\n", reply_code,
1611 out_base64 ? out_base64 : "*",
1612 reply_arg ? reply_arg : "*");
1613 break;
1614 default:
1615 if (out_base64) {
1616 printf("%s %s\n", reply_code, out_base64);
1617 } else if (reply_arg) {
1618 printf("%s %s\n", reply_code, reply_arg);
1619 } else {
1620 printf("%s\n", reply_code);
1624 talloc_free(mem_ctx);
1625 return;
1628 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1629 struct loadparm_context *lp_ctx,
1630 struct ntlm_auth_state *state,
1631 char *buf, int length, void **private2)
1633 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1634 return;
1637 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1638 struct loadparm_context *lp_ctx,
1639 struct ntlm_auth_state *state,
1640 char *buf, int length, void **private2)
1642 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1643 return;
1646 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1647 struct loadparm_context *lp_ctx,
1648 struct ntlm_auth_state *state,
1649 char *buf, int length, void **private2)
1651 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1652 return;
1655 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1656 struct loadparm_context *lp_ctx,
1657 struct ntlm_auth_state *state,
1658 char *buf, int length, void **private2)
1660 char *request, *parameter;
1661 static DATA_BLOB challenge;
1662 static DATA_BLOB lm_response;
1663 static DATA_BLOB nt_response;
1664 static char *full_username;
1665 static char *username;
1666 static char *domain;
1667 static char *plaintext_password;
1668 static bool ntlm_server_1_user_session_key;
1669 static bool ntlm_server_1_lm_session_key;
1671 if (strequal(buf, ".")) {
1672 if (!full_username && !username) {
1673 printf("Error: No username supplied!\n");
1674 } else if (plaintext_password) {
1675 /* handle this request as plaintext */
1676 if (!full_username) {
1677 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1678 printf("Error: Out of memory in "
1679 "asprintf!\n.\n");
1680 return;
1683 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1684 printf("Authenticated: Yes\n");
1685 } else {
1686 printf("Authenticated: No\n");
1688 } else if (!lm_response.data && !nt_response.data) {
1689 printf("Error: No password supplied!\n");
1690 } else if (!challenge.data) {
1691 printf("Error: No lanman-challenge supplied!\n");
1692 } else {
1693 char *error_string = NULL;
1694 uchar lm_key[8];
1695 uchar user_session_key[16];
1696 uint32_t flags = 0;
1697 NTSTATUS nt_status;
1698 if (full_username && !username) {
1699 fstring fstr_user;
1700 fstring fstr_domain;
1702 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1703 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1704 printf("Error: Could not parse into "
1705 "domain and username\n");
1707 SAFE_FREE(username);
1708 SAFE_FREE(domain);
1709 username = smb_xstrdup(fstr_user);
1710 domain = smb_xstrdup(fstr_domain);
1713 if (opt_password) {
1714 DATA_BLOB nt_session_key, lm_session_key;
1715 struct samr_Password lm_pw, nt_pw;
1716 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1717 ZERO_STRUCT(user_session_key);
1718 ZERO_STRUCT(lm_key);
1720 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1721 nt_status = ntlm_password_check(mem_ctx,
1722 true,
1723 NTLM_AUTH_ON,
1725 &challenge,
1726 &lm_response,
1727 &nt_response,
1728 username,
1729 username,
1730 domain,
1731 &lm_pw, &nt_pw,
1732 &nt_session_key,
1733 &lm_session_key);
1734 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1735 if (ntlm_server_1_user_session_key) {
1736 if (nt_session_key.length == sizeof(user_session_key)) {
1737 memcpy(user_session_key,
1738 nt_session_key.data,
1739 sizeof(user_session_key));
1742 if (ntlm_server_1_lm_session_key) {
1743 if (lm_session_key.length == sizeof(lm_key)) {
1744 memcpy(lm_key,
1745 lm_session_key.data,
1746 sizeof(lm_key));
1749 TALLOC_FREE(mem_ctx);
1751 } else {
1752 uint8_t authoritative = 0;
1754 if (!domain) {
1755 domain = smb_xstrdup(get_winbind_domain());
1758 if (ntlm_server_1_lm_session_key)
1759 flags |= WBFLAG_PAM_LMKEY;
1761 if (ntlm_server_1_user_session_key)
1762 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1764 nt_status = contact_winbind_auth_crap(username,
1765 domain,
1766 lp_netbios_name(),
1767 &challenge,
1768 &lm_response,
1769 &nt_response,
1770 flags, 0,
1771 lm_key,
1772 user_session_key,
1773 &authoritative,
1774 &error_string,
1775 NULL);
1778 if (!NT_STATUS_IS_OK(nt_status)) {
1779 printf("Authenticated: No\n");
1780 printf("Authentication-Error: %s\n.\n",
1781 error_string);
1782 } else {
1783 char *hex_lm_key;
1784 char *hex_user_session_key;
1786 printf("Authenticated: Yes\n");
1788 if (ntlm_server_1_lm_session_key
1789 && (!all_zero(lm_key,
1790 sizeof(lm_key)))) {
1791 hex_lm_key = hex_encode_talloc(NULL,
1792 (const unsigned char *)lm_key,
1793 sizeof(lm_key));
1794 printf("LANMAN-Session-Key: %s\n",
1795 hex_lm_key);
1796 TALLOC_FREE(hex_lm_key);
1799 if (ntlm_server_1_user_session_key
1800 && (!all_zero(user_session_key,
1801 sizeof(user_session_key)))) {
1802 hex_user_session_key = hex_encode_talloc(NULL,
1803 (const unsigned char *)user_session_key,
1804 sizeof(user_session_key));
1805 printf("User-Session-Key: %s\n",
1806 hex_user_session_key);
1807 TALLOC_FREE(hex_user_session_key);
1810 SAFE_FREE(error_string);
1812 /* clear out the state */
1813 challenge = data_blob_null;
1814 nt_response = data_blob_null;
1815 lm_response = data_blob_null;
1816 SAFE_FREE(full_username);
1817 SAFE_FREE(username);
1818 SAFE_FREE(domain);
1819 SAFE_FREE(plaintext_password);
1820 ntlm_server_1_user_session_key = False;
1821 ntlm_server_1_lm_session_key = False;
1822 printf(".\n");
1824 return;
1827 request = buf;
1829 /* Indicates a base64 encoded structure */
1830 parameter = strstr_m(request, ":: ");
1831 if (!parameter) {
1832 parameter = strstr_m(request, ": ");
1834 if (!parameter) {
1835 DEBUG(0, ("Parameter not found!\n"));
1836 printf("Error: Parameter not found!\n.\n");
1837 return;
1840 parameter[0] ='\0';
1841 parameter++;
1842 parameter[0] ='\0';
1843 parameter++;
1845 } else {
1846 parameter[0] ='\0';
1847 parameter++;
1848 parameter[0] ='\0';
1849 parameter++;
1850 parameter[0] ='\0';
1851 parameter++;
1853 base64_decode_inplace(parameter);
1856 if (strequal(request, "LANMAN-Challenge")) {
1857 challenge = strhex_to_data_blob(NULL, parameter);
1858 if (challenge.length != 8) {
1859 printf("Error: hex decode of %s failed! "
1860 "(got %d bytes, expected 8)\n.\n",
1861 parameter,
1862 (int)challenge.length);
1863 challenge = data_blob_null;
1865 } else if (strequal(request, "NT-Response")) {
1866 nt_response = strhex_to_data_blob(NULL, parameter);
1867 if (nt_response.length < 24) {
1868 printf("Error: hex decode of %s failed! "
1869 "(only got %d bytes, needed at least 24)\n.\n",
1870 parameter,
1871 (int)nt_response.length);
1872 nt_response = data_blob_null;
1874 } else if (strequal(request, "LANMAN-Response")) {
1875 lm_response = strhex_to_data_blob(NULL, parameter);
1876 if (lm_response.length != 24) {
1877 printf("Error: hex decode of %s failed! "
1878 "(got %d bytes, expected 24)\n.\n",
1879 parameter,
1880 (int)lm_response.length);
1881 lm_response = data_blob_null;
1883 } else if (strequal(request, "Password")) {
1884 plaintext_password = smb_xstrdup(parameter);
1885 } else if (strequal(request, "NT-Domain")) {
1886 domain = smb_xstrdup(parameter);
1887 } else if (strequal(request, "Username")) {
1888 username = smb_xstrdup(parameter);
1889 } else if (strequal(request, "Full-Username")) {
1890 full_username = smb_xstrdup(parameter);
1891 } else if (strequal(request, "Request-User-Session-Key")) {
1892 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1893 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1894 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1895 } else {
1896 printf("Error: Unknown request %s\n.\n", request);
1900 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
1901 struct loadparm_context *lp_ctx,
1902 struct ntlm_auth_state *state,
1903 char *buf, int length, void **private2)
1905 char *request, *parameter;
1906 static DATA_BLOB new_nt_pswd;
1907 static DATA_BLOB old_nt_hash_enc;
1908 static DATA_BLOB new_lm_pswd;
1909 static DATA_BLOB old_lm_hash_enc;
1910 static char *full_username = NULL;
1911 static char *username = NULL;
1912 static char *domain = NULL;
1913 static char *newpswd = NULL;
1914 static char *oldpswd = NULL;
1916 if (strequal(buf, ".")) {
1917 if(newpswd && oldpswd) {
1918 uchar old_nt_hash[16];
1919 uchar old_lm_hash[16];
1920 uchar new_nt_hash[16];
1921 uchar new_lm_hash[16];
1923 new_nt_pswd = data_blob(NULL, 516);
1924 old_nt_hash_enc = data_blob(NULL, 16);
1926 /* Calculate the MD4 hash (NT compatible) of the
1927 * password */
1928 E_md4hash(oldpswd, old_nt_hash);
1929 E_md4hash(newpswd, new_nt_hash);
1931 /* E_deshash returns false for 'long'
1932 passwords (> 14 DOS chars).
1934 Therefore, don't send a buffer
1935 encrypted with the truncated hash
1936 (it could allow an even easier
1937 attack on the password)
1939 Likewise, obey the admin's restriction
1942 if (lp_client_lanman_auth() &&
1943 E_deshash(newpswd, new_lm_hash) &&
1944 E_deshash(oldpswd, old_lm_hash)) {
1945 new_lm_pswd = data_blob(NULL, 516);
1946 old_lm_hash_enc = data_blob(NULL, 16);
1947 encode_pw_buffer(new_lm_pswd.data, newpswd,
1948 STR_UNICODE);
1950 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
1951 E_old_pw_hash(new_nt_hash, old_lm_hash,
1952 old_lm_hash_enc.data);
1953 } else {
1954 new_lm_pswd.data = NULL;
1955 new_lm_pswd.length = 0;
1956 old_lm_hash_enc.data = NULL;
1957 old_lm_hash_enc.length = 0;
1960 encode_pw_buffer(new_nt_pswd.data, newpswd,
1961 STR_UNICODE);
1963 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
1964 E_old_pw_hash(new_nt_hash, old_nt_hash,
1965 old_nt_hash_enc.data);
1968 if (!full_username && !username) {
1969 printf("Error: No username supplied!\n");
1970 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1971 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1972 printf("Error: No NT or LM password "
1973 "blobs supplied!\n");
1974 } else {
1975 char *error_string = NULL;
1977 if (full_username && !username) {
1978 fstring fstr_user;
1979 fstring fstr_domain;
1981 if (!parse_ntlm_auth_domain_user(full_username,
1982 fstr_user,
1983 fstr_domain)) {
1984 /* username might be 'tainted', don't
1985 * print into our new-line
1986 * deleimianted stream */
1987 printf("Error: Could not "
1988 "parse into domain and "
1989 "username\n");
1990 SAFE_FREE(username);
1991 username = smb_xstrdup(full_username);
1992 } else {
1993 SAFE_FREE(username);
1994 SAFE_FREE(domain);
1995 username = smb_xstrdup(fstr_user);
1996 domain = smb_xstrdup(fstr_domain);
2001 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2002 username, domain,
2003 new_nt_pswd,
2004 old_nt_hash_enc,
2005 new_lm_pswd,
2006 old_lm_hash_enc,
2007 &error_string))) {
2008 printf("Password-Change: No\n");
2009 printf("Password-Change-Error: %s\n.\n",
2010 error_string);
2011 } else {
2012 printf("Password-Change: Yes\n");
2015 SAFE_FREE(error_string);
2017 /* clear out the state */
2018 new_nt_pswd = data_blob_null;
2019 old_nt_hash_enc = data_blob_null;
2020 new_lm_pswd = data_blob_null;
2021 old_nt_hash_enc = data_blob_null;
2022 SAFE_FREE(full_username);
2023 SAFE_FREE(username);
2024 SAFE_FREE(domain);
2025 SAFE_FREE(newpswd);
2026 SAFE_FREE(oldpswd);
2027 printf(".\n");
2029 return;
2032 request = buf;
2034 /* Indicates a base64 encoded structure */
2035 parameter = strstr_m(request, ":: ");
2036 if (!parameter) {
2037 parameter = strstr_m(request, ": ");
2039 if (!parameter) {
2040 DEBUG(0, ("Parameter not found!\n"));
2041 printf("Error: Parameter not found!\n.\n");
2042 return;
2045 parameter[0] ='\0';
2046 parameter++;
2047 parameter[0] ='\0';
2048 parameter++;
2049 } else {
2050 parameter[0] ='\0';
2051 parameter++;
2052 parameter[0] ='\0';
2053 parameter++;
2054 parameter[0] ='\0';
2055 parameter++;
2057 base64_decode_inplace(parameter);
2060 if (strequal(request, "new-nt-password-blob")) {
2061 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2062 if (new_nt_pswd.length != 516) {
2063 printf("Error: hex decode of %s failed! "
2064 "(got %d bytes, expected 516)\n.\n",
2065 parameter,
2066 (int)new_nt_pswd.length);
2067 new_nt_pswd = data_blob_null;
2069 } else if (strequal(request, "old-nt-hash-blob")) {
2070 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2071 if (old_nt_hash_enc.length != 16) {
2072 printf("Error: hex decode of %s failed! "
2073 "(got %d bytes, expected 16)\n.\n",
2074 parameter,
2075 (int)old_nt_hash_enc.length);
2076 old_nt_hash_enc = data_blob_null;
2078 } else if (strequal(request, "new-lm-password-blob")) {
2079 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2080 if (new_lm_pswd.length != 516) {
2081 printf("Error: hex decode of %s failed! "
2082 "(got %d bytes, expected 516)\n.\n",
2083 parameter,
2084 (int)new_lm_pswd.length);
2085 new_lm_pswd = data_blob_null;
2088 else if (strequal(request, "old-lm-hash-blob")) {
2089 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2090 if (old_lm_hash_enc.length != 16)
2092 printf("Error: hex decode of %s failed! "
2093 "(got %d bytes, expected 16)\n.\n",
2094 parameter,
2095 (int)old_lm_hash_enc.length);
2096 old_lm_hash_enc = data_blob_null;
2098 } else if (strequal(request, "nt-domain")) {
2099 domain = smb_xstrdup(parameter);
2100 } else if(strequal(request, "username")) {
2101 username = smb_xstrdup(parameter);
2102 } else if(strequal(request, "full-username")) {
2103 username = smb_xstrdup(parameter);
2104 } else if(strequal(request, "new-password")) {
2105 newpswd = smb_xstrdup(parameter);
2106 } else if (strequal(request, "old-password")) {
2107 oldpswd = smb_xstrdup(parameter);
2108 } else {
2109 printf("Error: Unknown request %s\n.\n", request);
2113 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2114 struct loadparm_context *lp_ctx,
2115 struct ntlm_auth_state *state,
2116 stdio_helper_function fn, void **private2)
2118 char *buf;
2119 char tmp[INITIAL_BUFFER_SIZE+1];
2120 int length, buf_size = 0;
2121 char *c;
2123 buf = talloc_strdup(state->mem_ctx, "");
2124 if (!buf) {
2125 DEBUG(0, ("Failed to allocate input buffer.\n"));
2126 fprintf(stderr, "ERR\n");
2127 exit(1);
2130 do {
2132 /* this is not a typo - x_fgets doesn't work too well under
2133 * squid */
2134 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2135 if (ferror(stdin)) {
2136 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2137 "(%s)\n", ferror(stdin),
2138 strerror(ferror(stdin))));
2140 exit(1);
2142 exit(0);
2145 buf = talloc_strdup_append_buffer(buf, tmp);
2146 buf_size += INITIAL_BUFFER_SIZE;
2148 if (buf_size > MAX_BUFFER_SIZE) {
2149 DEBUG(2, ("Oversized message\n"));
2150 fprintf(stderr, "ERR\n");
2151 talloc_free(buf);
2152 return;
2155 c = strchr(buf, '\n');
2156 } while (c == NULL);
2158 *c = '\0';
2159 length = c-buf;
2161 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2163 if (buf[0] == '\0') {
2164 DEBUG(2, ("Invalid Request\n"));
2165 fprintf(stderr, "ERR\n");
2166 talloc_free(buf);
2167 return;
2170 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2171 talloc_free(buf);
2175 static void squid_stream(enum stdio_helper_mode stdio_mode,
2176 struct loadparm_context *lp_ctx,
2177 stdio_helper_function fn) {
2178 TALLOC_CTX *mem_ctx;
2179 struct ntlm_auth_state *state;
2181 /* initialize FDescs */
2182 setbuf(stdout, NULL);
2183 setbuf(stderr, NULL);
2185 mem_ctx = talloc_init("ntlm_auth");
2186 if (!mem_ctx) {
2187 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2188 fprintf(stderr, "ERR\n");
2189 exit(1);
2192 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2193 if (!state) {
2194 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2195 fprintf(stderr, "ERR\n");
2196 exit(1);
2199 state->mem_ctx = mem_ctx;
2200 state->helper_mode = stdio_mode;
2202 while(1) {
2203 TALLOC_CTX *frame = talloc_stackframe();
2204 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2205 TALLOC_FREE(frame);
2210 /* Authenticate a user with a challenge/response */
2212 static bool check_auth_crap(void)
2214 NTSTATUS nt_status;
2215 uint32_t flags = 0;
2216 char lm_key[8];
2217 char user_session_key[16];
2218 char *hex_lm_key;
2219 char *hex_user_session_key;
2220 char *error_string;
2221 uint8_t authoritative = 0;
2223 setbuf(stdout, NULL);
2225 if (request_lm_key)
2226 flags |= WBFLAG_PAM_LMKEY;
2228 if (request_user_session_key)
2229 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2231 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2233 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2234 opt_workstation,
2235 &opt_challenge,
2236 &opt_lm_response,
2237 &opt_nt_response,
2238 flags, 0,
2239 (unsigned char *)lm_key,
2240 (unsigned char *)user_session_key,
2241 &authoritative,
2242 &error_string, NULL);
2244 if (!NT_STATUS_IS_OK(nt_status)) {
2245 printf("%s (0x%x)\n", error_string,
2246 NT_STATUS_V(nt_status));
2247 SAFE_FREE(error_string);
2248 return False;
2251 if (request_lm_key
2252 && (!all_zero((uint8_t *)lm_key, sizeof(lm_key)))) {
2253 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2254 sizeof(lm_key));
2255 printf("LM_KEY: %s\n", hex_lm_key);
2256 TALLOC_FREE(hex_lm_key);
2258 if (request_user_session_key
2259 && (!all_zero((uint8_t *)user_session_key,
2260 sizeof(user_session_key)))) {
2261 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2262 sizeof(user_session_key));
2263 printf("NT_KEY: %s\n", hex_user_session_key);
2264 TALLOC_FREE(hex_user_session_key);
2267 return True;
2270 /* Main program */
2272 enum {
2273 OPT_USERNAME = 1000,
2274 OPT_DOMAIN,
2275 OPT_WORKSTATION,
2276 OPT_CHALLENGE,
2277 OPT_RESPONSE,
2278 OPT_LM,
2279 OPT_NT,
2280 OPT_PASSWORD,
2281 OPT_LM_KEY,
2282 OPT_USER_SESSION_KEY,
2283 OPT_DIAGNOSTICS,
2284 OPT_REQUIRE_MEMBERSHIP,
2285 OPT_USE_CACHED_CREDS,
2286 OPT_ALLOW_MSCHAPV2,
2287 OPT_PAM_WINBIND_CONF,
2288 OPT_TARGET_SERVICE,
2289 OPT_TARGET_HOSTNAME,
2290 OPT_OFFLINE_LOGON
2293 int main(int argc, const char **argv)
2295 TALLOC_CTX *frame = talloc_stackframe();
2296 int opt;
2297 const char *helper_protocol = NULL;
2298 int diagnostics = 0;
2300 const char *hex_challenge = NULL;
2301 const char *hex_lm_response = NULL;
2302 const char *hex_nt_response = NULL;
2303 struct loadparm_context *lp_ctx;
2304 poptContext pc;
2306 /* NOTE: DO NOT change this interface without considering the implications!
2307 This is an external interface, which other programs will use to interact
2308 with this helper.
2311 /* We do not use single-letter command abbreviations, because they harm future
2312 interface stability. */
2314 struct poptOption long_options[] = {
2315 POPT_AUTOHELP
2316 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2317 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2318 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2319 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2320 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2321 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2322 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2323 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2324 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2325 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2326 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2327 { "allow-mschapv2", 0, POPT_ARG_NONE, &opt_allow_mschapv2, OPT_ALLOW_MSCHAPV2, "Explicitly allow MSCHAPv2" },
2328 { "offline-logon", 0, POPT_ARG_NONE, &offline_logon,
2329 OPT_OFFLINE_LOGON,
2330 "Use cached passwords when DC is offline"},
2331 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2332 OPT_DIAGNOSTICS,
2333 "Perform diagnostics on the authentication chain"},
2334 { "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" },
2335 { "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" },
2336 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2337 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2338 POPT_COMMON_CONFIGFILE
2339 POPT_COMMON_VERSION
2340 POPT_COMMON_OPTION
2341 POPT_TABLEEND
2344 /* Samba client initialisation */
2345 smb_init_locale();
2347 setup_logging("ntlm_auth", DEBUG_STDERR);
2348 fault_setup();
2350 /* Parse options */
2352 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2354 /* Parse command line options */
2356 if (argc == 1) {
2357 poptPrintHelp(pc, stderr, 0);
2358 return 1;
2361 while((opt = poptGetNextOpt(pc)) != -1) {
2362 /* Get generic config options like --configfile */
2365 poptFreeContext(pc);
2367 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2368 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2369 get_dyn_CONFIGFILE(), strerror(errno));
2370 exit(1);
2373 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2374 POPT_CONTEXT_KEEP_FIRST);
2376 while((opt = poptGetNextOpt(pc)) != -1) {
2377 switch (opt) {
2378 case OPT_CHALLENGE:
2379 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2380 if (opt_challenge.length != 8) {
2381 fprintf(stderr, "hex decode of %s failed! "
2382 "(only got %d bytes)\n",
2383 hex_challenge,
2384 (int)opt_challenge.length);
2385 exit(1);
2387 break;
2388 case OPT_LM:
2389 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2390 if (opt_lm_response.length != 24) {
2391 fprintf(stderr, "hex decode of %s failed! "
2392 "(only got %d bytes)\n",
2393 hex_lm_response,
2394 (int)opt_lm_response.length);
2395 exit(1);
2397 break;
2399 case OPT_NT:
2400 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2401 if (opt_nt_response.length < 24) {
2402 fprintf(stderr, "hex decode of %s failed! "
2403 "(only got %d bytes)\n",
2404 hex_nt_response,
2405 (int)opt_nt_response.length);
2406 exit(1);
2408 break;
2410 case OPT_REQUIRE_MEMBERSHIP:
2411 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2412 require_membership_of_sid = require_membership_of;
2414 break;
2418 if (opt_username) {
2419 char *domain = SMB_STRDUP(opt_username);
2420 char *p = strchr_m(domain, *lp_winbind_separator());
2421 if (p) {
2422 opt_username = p+1;
2423 *p = '\0';
2424 if (opt_domain && !strequal(opt_domain, domain)) {
2425 fprintf(stderr, "Domain specified in username (%s) "
2426 "doesn't match specified domain (%s)!\n\n",
2427 domain, opt_domain);
2428 poptPrintHelp(pc, stderr, 0);
2429 exit(1);
2431 opt_domain = domain;
2432 } else {
2433 SAFE_FREE(domain);
2437 /* Note: if opt_domain is "" then send no domain */
2438 if (opt_domain == NULL) {
2439 opt_domain = get_winbind_domain();
2442 if (opt_workstation == NULL) {
2443 opt_workstation = "";
2446 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2447 if (lp_ctx == NULL) {
2448 fprintf(stderr, "loadparm_init_s3() failed!\n");
2449 exit(1);
2452 if (helper_protocol) {
2453 int i;
2454 for (i=0; i<NUM_HELPER_MODES; i++) {
2455 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2456 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2457 exit(0);
2460 fprintf(stderr, "unknown helper protocol [%s]\n\n"
2461 "Valid helper protools:\n\n", helper_protocol);
2463 for (i=0; i<NUM_HELPER_MODES; i++) {
2464 fprintf(stderr, "%s\n",
2465 stdio_helper_protocols[i].name);
2468 exit(1);
2471 if (!opt_username || !*opt_username) {
2472 fprintf(stderr, "username must be specified!\n\n");
2473 poptPrintHelp(pc, stderr, 0);
2474 exit(1);
2477 if (opt_challenge.length) {
2478 if (!check_auth_crap()) {
2479 exit(1);
2481 exit(0);
2484 if (!opt_password) {
2485 char pwd[256] = {0};
2486 int rc;
2488 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2489 if (rc == 0) {
2490 opt_password = SMB_STRDUP(pwd);
2494 if (diagnostics) {
2495 if (!diagnose_ntlm_auth()) {
2496 return 1;
2498 } else {
2499 fstring user;
2501 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2502 if (!check_plaintext_auth(user, opt_password, True)) {
2503 return 1;
2507 /* Exit code */
2509 poptFreeContext(pc);
2510 TALLOC_FREE(frame);
2511 return 0;