dsdb: Align integer types
[Samba.git] / source3 / utils / ntlm_auth.c
blob7f8d2688978d7d4a644c636a56f3e98b420761b7
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 "nsswitch/winbind_client.h"
41 #include "librpc/gen_ndr/krb5pac.h"
42 #include "../lib/util/asn1.h"
43 #include "auth/common_auth.h"
44 #include "source3/include/auth.h"
45 #include "source3/auth/proto.h"
46 #include "nsswitch/libwbclient/wbclient.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"
52 #include <gnutls/gnutls.h>
53 #include <gnutls/crypto.h>
55 #ifdef HAVE_KRB5
56 #include "auth/kerberos/pac_utils.h"
57 #endif
59 #ifndef PAM_WINBIND_CONFIG_FILE
60 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
61 #endif
63 #define WINBIND_KRB5_AUTH 0x00000080
65 #undef DBGC_CLASS
66 #define DBGC_CLASS DBGC_WINBIND
68 #define INITIAL_BUFFER_SIZE 300
69 #define MAX_BUFFER_SIZE 630000
71 enum stdio_helper_mode {
72 SQUID_2_4_BASIC,
73 SQUID_2_5_BASIC,
74 SQUID_2_5_NTLMSSP,
75 NTLMSSP_CLIENT_1,
76 GSS_SPNEGO_SERVER,
77 GSS_SPNEGO_CLIENT,
78 NTLM_SERVER_1,
79 NTLM_CHANGE_PASSWORD_1,
80 NUM_HELPER_MODES
83 enum ntlm_auth_cli_state {
84 CLIENT_INITIAL = 0,
85 CLIENT_RESPONSE,
86 CLIENT_FINISHED,
87 CLIENT_ERROR
90 struct ntlm_auth_state {
91 TALLOC_CTX *mem_ctx;
92 enum stdio_helper_mode helper_mode;
93 enum ntlm_auth_cli_state cli_state;
94 struct ntlmssp_state *ntlmssp_state;
95 uint32_t neg_flags;
96 char *want_feature_list;
97 bool have_session_key;
98 DATA_BLOB session_key;
99 DATA_BLOB initial_message;
100 void *gensec_private_1;
102 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
103 struct loadparm_context *lp_ctx,
104 struct ntlm_auth_state *state, char *buf,
105 int length, void **private2);
107 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
108 struct loadparm_context *lp_ctx,
109 char *buf, int length, void **private1);
111 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
112 struct loadparm_context *lp_ctx,
113 struct ntlm_auth_state *state,
114 stdio_helper_function fn, void **private2);
116 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
117 struct loadparm_context *lp_ctx,
118 struct ntlm_auth_state *state,
119 char *buf, int length, void **private2);
121 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
122 struct loadparm_context *lp_ctx,
123 struct ntlm_auth_state *state,
124 char *buf, int length, void **private2);
126 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
127 struct loadparm_context *lp_ctx,
128 struct ntlm_auth_state *state,
129 char *buf, int length, void **private2);
131 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
132 struct loadparm_context *lp_ctx,
133 struct ntlm_auth_state *state,
134 char *buf, int length, void **private2);
136 static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
137 struct loadparm_context *lp_ctx,
138 struct ntlm_auth_state *state,
139 char *buf, int length, void **private2);
141 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
142 struct loadparm_context *lp_ctx,
143 struct ntlm_auth_state *state,
144 char *buf, int length, void **private2);
146 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
147 struct loadparm_context *lp_ctx,
148 struct ntlm_auth_state *state,
149 char *buf, int length, void **private2);
151 static const struct {
152 enum stdio_helper_mode mode;
153 const char *name;
154 stdio_helper_function fn;
155 } stdio_helper_protocols[] = {
156 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
157 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
158 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
159 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
160 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
161 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
162 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
163 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
164 { NUM_HELPER_MODES, NULL, NULL}
167 const char *opt_username;
168 const char *opt_domain;
169 const char *opt_workstation;
170 const char *opt_password;
171 static DATA_BLOB opt_challenge;
172 static DATA_BLOB opt_lm_response;
173 static DATA_BLOB opt_nt_response;
174 static int request_lm_key;
175 static int request_user_session_key;
176 static int use_cached_creds;
177 static int offline_logon;
178 static int opt_allow_mschapv2;
180 static const char *require_membership_of;
181 static const char *require_membership_of_sid;
182 static const char *opt_pam_winbind_conf;
184 const char *opt_target_service;
185 const char *opt_target_hostname;
188 /* This is a bit hairy, but the basic idea is to do a password callback
189 to the calling application. The callback comes from within gensec */
191 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
192 struct loadparm_context *lp_ctx,
193 struct ntlm_auth_state *state, char *buf, int length,
194 void **password)
196 DATA_BLOB in;
197 if (strlen(buf) < 2) {
198 DEBUG(1, ("query [%s] invalid", buf));
199 printf("BH Query invalid\n");
200 return;
203 if (strlen(buf) > 3) {
204 in = base64_decode_data_blob(buf + 3);
205 } else {
206 in = data_blob(NULL, 0);
209 if (strncmp(buf, "PW ", 3) == 0) {
211 *password = talloc_strndup(NULL,
212 (const char *)in.data, in.length);
214 if (*password == NULL) {
215 DEBUG(1, ("Out of memory\n"));
216 printf("BH Out of memory\n");
217 data_blob_free(&in);
218 return;
221 printf("OK\n");
222 data_blob_free(&in);
223 return;
225 DEBUG(1, ("Asked for (and expected) a password\n"));
226 printf("BH Expected a password\n");
227 data_blob_free(&in);
231 * Callback for password credentials. This is not async, and when
232 * GENSEC and the credentials code is made async, it will look rather
233 * different.
236 static const char *get_password(struct cli_credentials *credentials)
238 TALLOC_CTX *frame = talloc_stackframe();
239 char *password = NULL;
240 struct ntlm_auth_state *state;
242 state = talloc_zero(frame, struct ntlm_auth_state);
243 if (state == NULL) {
244 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
245 fprintf(stderr, "ERR\n");
246 exit(1);
249 state->mem_ctx = state;
251 /* Ask for a password */
252 printf("PW\n");
254 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, state, manage_gensec_get_pw_request, (void **)&password);
255 talloc_steal(credentials, password);
256 TALLOC_FREE(frame);
257 return password;
261 * A limited set of features are defined with text strings as needed
262 * by ntlm_auth
265 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
267 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
268 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
269 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
271 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
272 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
273 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
275 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
276 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
277 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
279 if (in_list("NTLMSSP_FEATURE_CCACHE", feature_list, true)) {
280 DEBUG(10, ("want GENSEC_FEATURE_NTLM_CCACHE\n"));
281 gensec_want_feature(state, GENSEC_FEATURE_NTLM_CCACHE);
285 static char winbind_separator(void)
287 struct wbcInterfaceDetails *details;
288 wbcErr ret;
289 static bool got_sep;
290 static char sep;
292 if (got_sep)
293 return sep;
295 ret = wbcInterfaceDetails(&details);
296 if (!WBC_ERROR_IS_OK(ret)) {
297 d_fprintf(stderr, "could not obtain winbind separator!\n");
298 return *lp_winbind_separator();
301 sep = details->winbind_separator;
303 wbcFreeMemory(details);
305 got_sep = True;
307 if (!sep) {
308 d_fprintf(stderr, "winbind separator was NULL!\n");
309 return *lp_winbind_separator();
312 return sep;
315 const char *get_winbind_domain(void)
317 struct wbcInterfaceDetails *details;
318 wbcErr ret;
320 static fstring winbind_domain;
321 if (*winbind_domain) {
322 return winbind_domain;
325 /* Send off request */
327 ret = wbcInterfaceDetails(&details);
328 if (!WBC_ERROR_IS_OK(ret)) {
329 DEBUG(1, ("could not obtain winbind domain name!\n"));
330 return lp_workgroup();
333 fstrcpy(winbind_domain, details->netbios_domain);
335 wbcFreeMemory(details);
337 return winbind_domain;
341 const char *get_winbind_netbios_name(void)
343 struct wbcInterfaceDetails *details;
344 wbcErr ret;
346 static fstring winbind_netbios_name;
348 if (*winbind_netbios_name) {
349 return winbind_netbios_name;
352 /* Send off request */
354 ret = wbcInterfaceDetails(&details);
355 if (!WBC_ERROR_IS_OK(ret)) {
356 DEBUG(1, ("could not obtain winbind netbios name!\n"));
357 return lp_netbios_name();
360 fstrcpy(winbind_netbios_name, details->netbios_name);
362 wbcFreeMemory(details);
364 return winbind_netbios_name;
368 DATA_BLOB get_challenge(void)
370 static DATA_BLOB chal;
371 if (opt_challenge.length)
372 return opt_challenge;
374 chal = data_blob(NULL, 8);
376 generate_random_buffer(chal.data, chal.length);
377 return chal;
380 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
381 form DOMAIN/user into a domain and a user */
383 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
384 fstring user)
387 char *p = strchr(domuser,winbind_separator());
389 if (!p) {
390 return False;
393 fstrcpy(user, p+1);
394 fstrcpy(domain, domuser);
395 domain[PTR_DIFF(p, domuser)] = 0;
396 return strupper_m(domain);
399 static bool get_require_membership_sid(void) {
400 fstring domain, name, sidbuf;
401 struct wbcDomainSid sid;
402 enum wbcSidType type;
403 wbcErr ret;
405 if (!require_membership_of) {
406 return True;
409 if (require_membership_of_sid) {
410 return True;
413 /* Otherwise, ask winbindd for the name->sid request */
415 if (!parse_ntlm_auth_domain_user(require_membership_of,
416 domain, name)) {
417 DEBUG(0, ("Could not parse %s into separate domain/name parts!\n",
418 require_membership_of));
419 return False;
422 ret = wbcLookupName(domain, name, &sid, &type);
423 if (!WBC_ERROR_IS_OK(ret)) {
424 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
425 require_membership_of));
426 return False;
429 wbcSidToStringBuf(&sid, sidbuf, sizeof(sidbuf));
431 require_membership_of_sid = SMB_STRDUP(sidbuf);
433 if (require_membership_of_sid)
434 return True;
436 return False;
440 * Get some configuration from pam_winbind.conf to see if we
441 * need to contact trusted domain
444 int get_pam_winbind_config()
446 int ctrl = 0;
447 struct tiniparser_dictionary *d = NULL;
449 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
450 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
453 d = tiniparser_load(opt_pam_winbind_conf);
455 if (!d) {
456 return 0;
459 if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
460 ctrl |= WINBIND_KRB5_AUTH;
463 tiniparser_freedict(d);
465 return ctrl;
468 /* Authenticate a user with a plaintext password */
470 static bool check_plaintext_auth(const char *user, const char *pass,
471 bool stdout_diagnostics)
473 struct winbindd_request request;
474 struct winbindd_response response;
475 NSS_STATUS result;
477 if (!get_require_membership_sid()) {
478 return False;
481 /* Send off request */
483 ZERO_STRUCT(request);
484 ZERO_STRUCT(response);
486 fstrcpy(request.data.auth.user, user);
487 fstrcpy(request.data.auth.pass, pass);
488 if (require_membership_of_sid) {
489 strlcpy(request.data.auth.require_membership_of_sid,
490 require_membership_of_sid,
491 sizeof(request.data.auth.require_membership_of_sid));
494 if (offline_logon) {
495 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
498 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH, &request, &response);
500 /* Display response */
502 if (stdout_diagnostics) {
503 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
504 d_fprintf(stderr, "Reading winbind reply failed! (0x01)\n");
507 d_printf("%s: %s (0x%x)\n",
508 response.data.auth.nt_status_string,
509 response.data.auth.error_string,
510 response.data.auth.nt_status);
511 } else {
512 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
513 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
516 DEBUG(3, ("%s: %s (0x%x)\n",
517 response.data.auth.nt_status_string,
518 response.data.auth.error_string,
519 response.data.auth.nt_status));
522 return (result == NSS_STATUS_SUCCESS);
525 /* authenticate a user with an encrypted username/password */
527 NTSTATUS contact_winbind_auth_crap(const char *username,
528 const char *domain,
529 const char *workstation,
530 const DATA_BLOB *challenge,
531 const DATA_BLOB *lm_response,
532 const DATA_BLOB *nt_response,
533 uint32_t flags,
534 uint32_t extra_logon_parameters,
535 uint8_t lm_key[8],
536 uint8_t user_session_key[16],
537 uint8_t *pauthoritative,
538 char **error_string,
539 char **unix_name)
541 NTSTATUS nt_status;
542 NSS_STATUS result;
543 struct winbindd_request request;
544 struct winbindd_response response;
546 *pauthoritative = 1;
548 if (!get_require_membership_sid()) {
549 return NT_STATUS_INVALID_PARAMETER;
552 ZERO_STRUCT(request);
553 ZERO_STRUCT(response);
555 request.flags = flags;
557 request.data.auth_crap.logon_parameters = extra_logon_parameters
558 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
560 if (opt_allow_mschapv2) {
561 request.data.auth_crap.logon_parameters |= MSV1_0_ALLOW_MSVCHAPV2;
564 if (require_membership_of_sid)
565 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
567 fstrcpy(request.data.auth_crap.user, username);
568 fstrcpy(request.data.auth_crap.domain, domain);
570 fstrcpy(request.data.auth_crap.workstation,
571 workstation);
573 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
575 if (lm_response && lm_response->length) {
576 memcpy(request.data.auth_crap.lm_resp,
577 lm_response->data,
578 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
579 request.data.auth_crap.lm_resp_len = lm_response->length;
582 if (nt_response && nt_response->length) {
583 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
584 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
585 request.extra_len = nt_response->length;
586 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
587 if (request.extra_data.data == NULL) {
588 return NT_STATUS_NO_MEMORY;
590 memcpy(request.extra_data.data, nt_response->data,
591 nt_response->length);
593 } else {
594 memcpy(request.data.auth_crap.nt_resp,
595 nt_response->data, nt_response->length);
597 request.data.auth_crap.nt_resp_len = nt_response->length;
600 result = winbindd_priv_request_response(
601 NULL,
602 WINBINDD_PAM_AUTH_CRAP,
603 &request,
604 &response);
605 SAFE_FREE(request.extra_data.data);
607 /* Display response */
609 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
610 nt_status = NT_STATUS_UNSUCCESSFUL;
611 if (error_string)
612 *error_string = smb_xstrdup("Reading winbind reply failed!");
613 winbindd_free_response(&response);
614 return nt_status;
617 nt_status = (NT_STATUS(response.data.auth.nt_status));
618 if (!NT_STATUS_IS_OK(nt_status)) {
619 if (error_string)
620 *error_string = smb_xstrdup(response.data.auth.error_string);
621 *pauthoritative = response.data.auth.authoritative;
622 winbindd_free_response(&response);
623 return nt_status;
626 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
627 memcpy(lm_key, response.data.auth.first_8_lm_hash,
628 sizeof(response.data.auth.first_8_lm_hash));
630 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
631 memcpy(user_session_key, response.data.auth.user_session_key,
632 sizeof(response.data.auth.user_session_key));
635 if (flags & WBFLAG_PAM_UNIX_NAME) {
636 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
637 if (!*unix_name) {
638 winbindd_free_response(&response);
639 return NT_STATUS_NO_MEMORY;
643 winbindd_free_response(&response);
644 return nt_status;
647 /* contact server to change user password using auth crap */
648 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
649 const char *domain,
650 const DATA_BLOB new_nt_pswd,
651 const DATA_BLOB old_nt_hash_enc,
652 const DATA_BLOB new_lm_pswd,
653 const DATA_BLOB old_lm_hash_enc,
654 char **error_string)
656 NTSTATUS nt_status;
657 NSS_STATUS result;
658 struct winbindd_request request;
659 struct winbindd_response response;
661 if (!get_require_membership_sid())
663 if(error_string)
664 *error_string = smb_xstrdup("Can't get membership sid.");
665 return NT_STATUS_INVALID_PARAMETER;
668 ZERO_STRUCT(request);
669 ZERO_STRUCT(response);
671 if(username != NULL)
672 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
673 if(domain != NULL)
674 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
676 if(new_nt_pswd.length)
678 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
679 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
682 if(old_nt_hash_enc.length)
684 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));
685 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
688 if(new_lm_pswd.length)
690 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
691 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
694 if(old_lm_hash_enc.length)
696 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));
697 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
700 result = winbindd_request_response(NULL, WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
702 /* Display response */
704 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
706 nt_status = NT_STATUS_UNSUCCESSFUL;
707 if (error_string)
708 *error_string = smb_xstrdup("Reading winbind reply failed!");
709 winbindd_free_response(&response);
710 return nt_status;
713 nt_status = (NT_STATUS(response.data.auth.nt_status));
714 if (!NT_STATUS_IS_OK(nt_status))
716 if (error_string)
717 *error_string = smb_xstrdup(response.data.auth.error_string);
718 winbindd_free_response(&response);
719 return nt_status;
722 winbindd_free_response(&response);
724 return nt_status;
727 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
728 TALLOC_CTX *mem_ctx,
729 void *server_returned_info,
730 const char *original_user_name,
731 uint32_t session_info_flags,
732 struct auth_session_info **session_info_out)
734 const char *unix_username = (const char *)server_returned_info;
735 struct dom_sid *sids = NULL;
736 struct auth_session_info *session_info = NULL;
738 session_info = talloc_zero(mem_ctx, struct auth_session_info);
739 if (session_info == NULL) {
740 return NT_STATUS_NO_MEMORY;
743 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
744 if (session_info->unix_info == NULL) {
745 TALLOC_FREE(session_info);
746 return NT_STATUS_NO_MEMORY;
748 session_info->unix_info->unix_name = talloc_strdup(session_info->unix_info,
749 unix_username);
750 if (session_info->unix_info->unix_name == NULL) {
751 TALLOC_FREE(session_info);
752 return NT_STATUS_NO_MEMORY;
755 session_info->security_token = talloc_zero(session_info, struct security_token);
756 if (session_info->security_token == NULL) {
757 TALLOC_FREE(session_info);
758 return NT_STATUS_NO_MEMORY;
761 sids = talloc_zero_array(session_info->security_token,
762 struct dom_sid, 3);
763 if (sids == NULL) {
764 TALLOC_FREE(session_info);
765 return NT_STATUS_NO_MEMORY;
767 sid_copy(&sids[0], &global_sid_World);
768 sid_copy(&sids[1], &global_sid_Network);
769 sid_copy(&sids[2], &global_sid_Authenticated_Users);
771 session_info->security_token->num_sids = talloc_array_length(sids);
772 session_info->security_token->sids = sids;
774 *session_info_out = session_info;
776 return NT_STATUS_OK;
779 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
780 TALLOC_CTX *mem_ctx,
781 struct smb_krb5_context *smb_krb5_context,
782 DATA_BLOB *pac_blob,
783 const char *princ_name,
784 const struct tsocket_address *remote_address,
785 uint32_t session_info_flags,
786 struct auth_session_info **session_info)
788 TALLOC_CTX *tmp_ctx;
789 struct PAC_LOGON_INFO *logon_info = NULL;
790 char *unixuser;
791 NTSTATUS status;
792 char *domain = NULL;
793 char *realm = NULL;
794 char *user = NULL;
795 char *p;
797 tmp_ctx = talloc_new(mem_ctx);
798 if (!tmp_ctx) {
799 return NT_STATUS_NO_MEMORY;
802 if (pac_blob) {
803 #ifdef HAVE_KRB5
804 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
805 NULL, NULL, 0, &logon_info);
806 #else
807 status = NT_STATUS_ACCESS_DENIED;
808 #endif
809 if (!NT_STATUS_IS_OK(status)) {
810 goto done;
814 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
816 p = strchr_m(princ_name, '@');
817 if (!p) {
818 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
819 princ_name));
820 return NT_STATUS_LOGON_FAILURE;
823 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
824 if (!user) {
825 return NT_STATUS_NO_MEMORY;
828 realm = talloc_strdup(talloc_tos(), p + 1);
829 if (!realm) {
830 return NT_STATUS_NO_MEMORY;
833 if (!strequal(realm, lp_realm())) {
834 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
835 if (!lp_allow_trusted_domains()) {
836 return NT_STATUS_LOGON_FAILURE;
840 if (logon_info && logon_info->info3.base.logon_domain.string) {
841 domain = talloc_strdup(mem_ctx,
842 logon_info->info3.base.logon_domain.string);
843 if (!domain) {
844 return NT_STATUS_NO_MEMORY;
846 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
847 } else {
849 /* If we have winbind running, we can (and must) shorten the
850 username by using the short netbios name. Otherwise we will
851 have inconsistent user names. With Kerberos, we get the
852 fully qualified realm, with ntlmssp we get the short
853 name. And even w2k3 does use ntlmssp if you for example
854 connect to an ip address. */
856 wbcErr wbc_status;
857 struct wbcDomainInfo *info = NULL;
859 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
860 realm));
862 wbc_status = wbcDomainInfo(realm, &info);
864 if (WBC_ERROR_IS_OK(wbc_status)) {
865 domain = talloc_strdup(mem_ctx,
866 info->short_name);
867 wbcFreeMemory(info);
868 } else {
869 DEBUG(3, ("Could not find short name: %s\n",
870 wbcErrorString(wbc_status)));
871 domain = talloc_strdup(mem_ctx, realm);
873 if (!domain) {
874 return NT_STATUS_NO_MEMORY;
876 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
879 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
880 if (!unixuser) {
881 status = NT_STATUS_NO_MEMORY;
882 goto done;
885 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
887 done:
888 TALLOC_FREE(tmp_ctx);
889 return status;
895 * Return the challenge as determined by the authentication subsystem
896 * @return an 8 byte random challenge
899 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
900 uint8_t chal[8])
902 if (auth_ctx->challenge.data.length == 8) {
903 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
904 auth_ctx->challenge.set_by));
905 memcpy(chal, auth_ctx->challenge.data.data, 8);
906 return NT_STATUS_OK;
909 if (!auth_ctx->challenge.set_by) {
910 generate_random_buffer(chal, 8);
912 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
913 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
914 auth_ctx->challenge.set_by = "random";
917 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
918 auth_ctx->challenge.set_by));
920 return NT_STATUS_OK;
924 * NTLM2 authentication modifies the effective challenge,
925 * @param challenge The new challenge value
927 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
929 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
930 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
932 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
933 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
935 return NT_STATUS_OK;
939 * Check the password on an NTLMSSP login.
941 * Return the session keys used on the connection.
944 struct winbind_pw_check_state {
945 uint8_t authoritative;
946 void *server_info;
947 DATA_BLOB nt_session_key;
948 DATA_BLOB lm_session_key;
951 static struct tevent_req *winbind_pw_check_send(
952 TALLOC_CTX *mem_ctx,
953 struct tevent_context *ev,
954 struct auth4_context *auth4_context,
955 const struct auth_usersupplied_info *user_info)
957 struct tevent_req *req = NULL;
958 struct winbind_pw_check_state *state = NULL;
959 NTSTATUS nt_status;
960 char *error_string = NULL;
961 uint8_t lm_key[8];
962 uint8_t user_sess_key[16];
963 char *unix_name = NULL;
965 req = tevent_req_create(
966 mem_ctx, &state, struct winbind_pw_check_state);
967 if (req == NULL) {
968 return NULL;
971 nt_status = contact_winbind_auth_crap(
972 user_info->client.account_name,
973 user_info->client.domain_name,
974 user_info->workstation_name,
975 &auth4_context->challenge.data,
976 &user_info->password.response.lanman,
977 &user_info->password.response.nt,
978 WBFLAG_PAM_LMKEY |
979 WBFLAG_PAM_USER_SESSION_KEY |
980 WBFLAG_PAM_UNIX_NAME,
982 lm_key, user_sess_key,
983 &state->authoritative,
984 &error_string,
985 &unix_name);
987 if (tevent_req_nterror(req, nt_status)) {
988 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
989 DBG_ERR("Login for user [%s]\\[%s]@[%s] failed due "
990 "to [%s]\n",
991 user_info->client.domain_name,
992 user_info->client.account_name,
993 user_info->workstation_name,
994 error_string ?
995 error_string :
996 "unknown error (NULL)");
997 } else {
998 DBG_NOTICE("Login for user [%s]\\[%s]@[%s] failed due "
999 "to [%s]\n",
1000 user_info->client.domain_name,
1001 user_info->client.account_name,
1002 user_info->workstation_name,
1003 error_string ?
1004 error_string :
1005 "unknown error (NULL)");
1007 goto done;
1010 if (!all_zero(lm_key, 8)) {
1011 state->lm_session_key = data_blob_talloc(state, NULL, 16);
1012 if (tevent_req_nomem(state->lm_session_key.data, req)) {
1013 goto done;
1015 memcpy(state->lm_session_key.data, lm_key, 8);
1016 memset(state->lm_session_key.data+8, '\0', 8);
1018 if (!all_zero(user_sess_key, 16)) {
1019 state->nt_session_key = data_blob_talloc(
1020 state, user_sess_key, 16);
1021 if (tevent_req_nomem(state->nt_session_key.data, req)) {
1022 goto done;
1025 state->server_info = talloc_strdup(state, unix_name);
1026 if (tevent_req_nomem(state->server_info, req)) {
1027 goto done;
1029 tevent_req_done(req);
1031 done:
1032 SAFE_FREE(error_string);
1033 SAFE_FREE(unix_name);
1034 return tevent_req_post(req, ev);
1037 static NTSTATUS winbind_pw_check_recv(struct tevent_req *req,
1038 TALLOC_CTX *mem_ctx,
1039 uint8_t *pauthoritative,
1040 void **server_returned_info,
1041 DATA_BLOB *nt_session_key,
1042 DATA_BLOB *lm_session_key)
1044 struct winbind_pw_check_state *state = tevent_req_data(
1045 req, struct winbind_pw_check_state);
1046 NTSTATUS status;
1048 if (pauthoritative != NULL) {
1049 *pauthoritative = state->authoritative;
1052 if (tevent_req_is_nterror(req, &status)) {
1053 return status;
1056 if (server_returned_info != NULL) {
1057 *server_returned_info = talloc_move(
1058 mem_ctx, &state->server_info);
1060 if (nt_session_key != NULL) {
1061 *nt_session_key = (DATA_BLOB) {
1062 .data = talloc_move(
1063 mem_ctx, &state->nt_session_key.data),
1064 .length = state->nt_session_key.length,
1067 if (lm_session_key != NULL) {
1068 *lm_session_key = (DATA_BLOB) {
1069 .data = talloc_move(
1070 mem_ctx, &state->lm_session_key.data),
1071 .length = state->lm_session_key.length,
1075 return NT_STATUS_OK;
1078 struct local_pw_check_state {
1079 uint8_t authoritative;
1080 void *server_info;
1081 DATA_BLOB nt_session_key;
1082 DATA_BLOB lm_session_key;
1085 static struct tevent_req *local_pw_check_send(
1086 TALLOC_CTX *mem_ctx,
1087 struct tevent_context *ev,
1088 struct auth4_context *auth4_context,
1089 const struct auth_usersupplied_info *user_info)
1091 struct tevent_req *req = NULL;
1092 struct local_pw_check_state *state = NULL;
1093 struct samr_Password lm_pw, nt_pw;
1094 NTSTATUS nt_status;
1096 req = tevent_req_create(
1097 mem_ctx, &state, struct local_pw_check_state);
1098 if (req == NULL) {
1099 return NULL;
1101 state->authoritative = 1;
1103 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1105 nt_status = ntlm_password_check(
1106 state,
1107 true,
1108 NTLM_AUTH_ON,
1110 &auth4_context->challenge.data,
1111 &user_info->password.response.lanman,
1112 &user_info->password.response.nt,
1113 user_info->client.account_name,
1114 user_info->client.account_name,
1115 user_info->client.domain_name,
1116 &lm_pw,
1117 &nt_pw,
1118 &state->nt_session_key,
1119 &state->lm_session_key);
1121 if (tevent_req_nterror(req, nt_status)) {
1122 DBG_NOTICE("Login for user [%s]\\[%s]@[%s] failed due to "
1123 "[%s]\n",
1124 user_info->client.domain_name,
1125 user_info->client.account_name,
1126 user_info->workstation_name,
1127 nt_errstr(nt_status));
1128 return tevent_req_post(req, ev);
1131 state->server_info = talloc_asprintf(
1132 state,
1133 "%s%c%s",
1134 user_info->client.domain_name,
1135 *lp_winbind_separator(),
1136 user_info->client.account_name);
1137 if (tevent_req_nomem(state->server_info, req)) {
1138 return tevent_req_post(req, ev);
1141 tevent_req_done(req);
1142 return tevent_req_post(req, ev);
1145 static NTSTATUS local_pw_check_recv(struct tevent_req *req,
1146 TALLOC_CTX *mem_ctx,
1147 uint8_t *pauthoritative,
1148 void **server_returned_info,
1149 DATA_BLOB *nt_session_key,
1150 DATA_BLOB *lm_session_key)
1152 struct local_pw_check_state *state = tevent_req_data(
1153 req, struct local_pw_check_state);
1154 NTSTATUS status;
1156 if (pauthoritative != NULL) {
1157 *pauthoritative = state->authoritative;
1160 if (tevent_req_is_nterror(req, &status)) {
1161 return status;
1164 if (server_returned_info != NULL) {
1165 *server_returned_info = talloc_move(
1166 mem_ctx, &state->server_info);
1168 if (nt_session_key != NULL) {
1169 *nt_session_key = (DATA_BLOB) {
1170 .data = talloc_move(
1171 mem_ctx, &state->nt_session_key.data),
1172 .length = state->nt_session_key.length,
1175 if (lm_session_key != NULL) {
1176 *lm_session_key = (DATA_BLOB) {
1177 .data = talloc_move(
1178 mem_ctx, &state->lm_session_key.data),
1179 .length = state->lm_session_key.length,
1183 return NT_STATUS_OK;
1186 static NTSTATUS ntlm_auth_prepare_gensec_client(TALLOC_CTX *mem_ctx,
1187 struct loadparm_context *lp_ctx,
1188 struct gensec_security **gensec_security_out)
1190 struct gensec_security *gensec_security = NULL;
1191 NTSTATUS nt_status;
1192 TALLOC_CTX *tmp_ctx;
1193 const struct gensec_security_ops **backends = NULL;
1194 struct gensec_settings *gensec_settings = NULL;
1195 size_t idx = 0;
1197 tmp_ctx = talloc_new(mem_ctx);
1198 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1200 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1201 if (gensec_settings == NULL) {
1202 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1203 TALLOC_FREE(tmp_ctx);
1204 return NT_STATUS_NO_MEMORY;
1207 backends = talloc_zero_array(gensec_settings,
1208 const struct gensec_security_ops *, 4);
1209 if (backends == NULL) {
1210 TALLOC_FREE(tmp_ctx);
1211 return NT_STATUS_NO_MEMORY;
1213 gensec_settings->backends = backends;
1215 gensec_init();
1217 /* These need to be in priority order, krb5 before NTLMSSP */
1218 #if defined(HAVE_KRB5)
1219 backends[idx++] = &gensec_gse_krb5_security_ops;
1220 #endif
1222 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1224 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1226 nt_status = gensec_client_start(NULL, &gensec_security,
1227 gensec_settings);
1228 if (!NT_STATUS_IS_OK(nt_status)) {
1229 TALLOC_FREE(tmp_ctx);
1230 return nt_status;
1233 talloc_unlink(tmp_ctx, gensec_settings);
1235 if (opt_target_service != NULL) {
1236 nt_status = gensec_set_target_service(gensec_security,
1237 opt_target_service);
1238 if (!NT_STATUS_IS_OK(nt_status)) {
1239 TALLOC_FREE(tmp_ctx);
1240 return nt_status;
1244 if (opt_target_hostname != NULL) {
1245 nt_status = gensec_set_target_hostname(gensec_security,
1246 opt_target_hostname);
1247 if (!NT_STATUS_IS_OK(nt_status)) {
1248 TALLOC_FREE(tmp_ctx);
1249 return nt_status;
1253 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1254 TALLOC_FREE(tmp_ctx);
1255 return NT_STATUS_OK;
1258 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1260 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1261 if (auth4_context == NULL) {
1262 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1263 return NULL;
1265 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1266 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1267 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1268 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1269 if (local_pw) {
1270 auth4_context->check_ntlm_password_send = local_pw_check_send;
1271 auth4_context->check_ntlm_password_recv = local_pw_check_recv;
1272 } else {
1273 auth4_context->check_ntlm_password_send =
1274 winbind_pw_check_send;
1275 auth4_context->check_ntlm_password_recv =
1276 winbind_pw_check_recv;
1278 auth4_context->private_data = NULL;
1279 return auth4_context;
1282 static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx,
1283 struct loadparm_context *lp_ctx,
1284 struct gensec_security **gensec_security_out)
1286 struct gensec_security *gensec_security;
1287 NTSTATUS nt_status;
1289 TALLOC_CTX *tmp_ctx;
1290 const struct gensec_security_ops **backends;
1291 struct gensec_settings *gensec_settings;
1292 size_t idx = 0;
1293 struct cli_credentials *server_credentials;
1295 struct auth4_context *auth4_context;
1297 tmp_ctx = talloc_new(mem_ctx);
1298 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1300 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1301 if (auth4_context == NULL) {
1302 TALLOC_FREE(tmp_ctx);
1303 return NT_STATUS_NO_MEMORY;
1306 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1307 if (lp_ctx == NULL) {
1308 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1309 TALLOC_FREE(tmp_ctx);
1310 return NT_STATUS_NO_MEMORY;
1314 * This should be a 'netbios domain -> DNS domain'
1315 * mapping, and can currently validly return NULL on
1316 * poorly configured systems.
1318 * This is used for the NTLMSSP server
1321 if (opt_password) {
1322 gensec_settings->server_netbios_name = lp_netbios_name();
1323 gensec_settings->server_netbios_domain = lp_workgroup();
1324 } else {
1325 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1326 gensec_settings->server_netbios_domain = get_winbind_domain();
1329 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1330 get_mydnsdomname(talloc_tos()));
1331 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1332 get_mydnsfullname());
1334 backends = talloc_zero_array(gensec_settings,
1335 const struct gensec_security_ops *, 4);
1337 if (backends == NULL) {
1338 TALLOC_FREE(tmp_ctx);
1339 return NT_STATUS_NO_MEMORY;
1341 gensec_settings->backends = backends;
1343 gensec_init();
1345 /* These need to be in priority order, krb5 before NTLMSSP */
1346 #if defined(HAVE_KRB5)
1347 backends[idx++] = &gensec_gse_krb5_security_ops;
1348 #endif
1350 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1352 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1355 * This is anonymous for now, because we just use it
1356 * to set the kerberos state at the moment
1358 server_credentials = cli_credentials_init_anon(tmp_ctx);
1359 if (!server_credentials) {
1360 DBG_ERR("Failed to init server credentials\n");
1361 return NT_STATUS_NO_MEMORY;
1364 cli_credentials_set_conf(server_credentials, lp_ctx);
1366 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1367 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1368 } else {
1369 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1372 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1373 auth4_context, &gensec_security);
1375 if (!NT_STATUS_IS_OK(nt_status)) {
1376 TALLOC_FREE(tmp_ctx);
1377 return nt_status;
1380 gensec_set_credentials(gensec_security, server_credentials);
1383 * TODO: Allow the caller to pass their own description here
1384 * via a command-line option
1386 nt_status = gensec_set_target_service_description(gensec_security,
1387 "ntlm_auth");
1388 if (!NT_STATUS_IS_OK(nt_status)) {
1389 TALLOC_FREE(tmp_ctx);
1390 return nt_status;
1393 talloc_unlink(tmp_ctx, lp_ctx);
1394 talloc_unlink(tmp_ctx, server_credentials);
1395 talloc_unlink(tmp_ctx, gensec_settings);
1396 talloc_unlink(tmp_ctx, auth4_context);
1398 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1399 TALLOC_FREE(tmp_ctx);
1400 return NT_STATUS_OK;
1403 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1404 struct loadparm_context *lp_ctx,
1405 struct ntlm_auth_state *state,
1406 char *buf, int length, void **private2)
1408 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1409 return;
1412 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1413 struct loadparm_context *lp_ctx,
1414 struct ntlm_auth_state *state,
1415 char *buf, int length, void **private2)
1417 char *user, *pass;
1418 user=buf;
1420 pass=(char *)memchr(buf,' ',length);
1421 if (!pass) {
1422 DEBUG(2, ("Password not found. Denying access\n"));
1423 printf("ERR\n");
1424 return;
1426 *pass='\0';
1427 pass++;
1429 if (state->helper_mode == SQUID_2_5_BASIC) {
1430 char *end = rfc1738_unescape(user);
1431 if (end == NULL || (end - user) != strlen(user)) {
1432 DEBUG(2, ("Badly rfc1738 encoded username: %s; "
1433 "denying access\n", user));
1434 printf("ERR\n");
1435 return;
1437 end = rfc1738_unescape(pass);
1438 if (end == NULL || (end - pass) != strlen(pass)) {
1439 DEBUG(2, ("Badly encoded password for %s; "
1440 "denying access\n", user));
1441 printf("ERR\n");
1442 return;
1446 if (check_plaintext_auth(user, pass, False)) {
1447 printf("OK\n");
1448 } else {
1449 printf("ERR\n");
1453 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1454 struct loadparm_context *lp_ctx,
1455 char *buf, int length, void **private1)
1457 DATA_BLOB in;
1458 DATA_BLOB out = data_blob(NULL, 0);
1459 char *out_base64 = NULL;
1460 const char *reply_arg = NULL;
1461 struct gensec_ntlm_state {
1462 struct gensec_security *gensec_state;
1463 const char *set_password;
1465 struct gensec_ntlm_state *state;
1467 NTSTATUS nt_status;
1468 bool first = false;
1469 const char *reply_code;
1470 struct cli_credentials *creds;
1472 static char *want_feature_list = NULL;
1473 static DATA_BLOB session_key;
1475 TALLOC_CTX *mem_ctx;
1477 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1478 if (mem_ctx == NULL) {
1479 printf("BH No Memory\n");
1480 exit(1);
1483 if (*private1) {
1484 state = talloc_get_type(*private1, struct gensec_ntlm_state);
1485 if (state == NULL) {
1486 DBG_WARNING("*private1 is of type %s\n",
1487 talloc_get_name(*private1));
1488 printf("BH *private1 is of type %s\n",
1489 talloc_get_name(*private1));
1490 exit(1);
1492 } else {
1493 state = talloc_zero(NULL, struct gensec_ntlm_state);
1494 if (!state) {
1495 printf("BH No Memory\n");
1496 exit(1);
1498 *private1 = state;
1499 if (opt_password) {
1500 state->set_password = opt_password;
1504 if (strlen(buf) < 2) {
1505 DEBUG(1, ("query [%s] invalid", buf));
1506 printf("BH Query invalid\n");
1507 talloc_free(mem_ctx);
1508 return;
1511 if (strlen(buf) > 3) {
1512 if(strncmp(buf, "SF ", 3) == 0) {
1513 DEBUG(10, ("Setting flags to negotiate\n"));
1514 talloc_free(want_feature_list);
1515 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1516 printf("OK\n");
1517 talloc_free(mem_ctx);
1518 return;
1520 in = base64_decode_data_blob_talloc(mem_ctx, buf + 3);
1521 } else {
1522 in = data_blob(NULL, 0);
1525 if (strncmp(buf, "YR", 2) == 0) {
1526 if (state->gensec_state) {
1527 talloc_free(state->gensec_state);
1528 state->gensec_state = NULL;
1530 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1531 /* Just return BH, like ntlm_auth from Samba 3 does. */
1532 printf("BH Command expected\n");
1533 talloc_free(mem_ctx);
1534 return;
1535 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1536 (strncmp(buf, "KK ", 3) != 0) &&
1537 (strncmp(buf, "AF ", 3) != 0) &&
1538 (strncmp(buf, "NA ", 3) != 0) &&
1539 (strncmp(buf, "UG", 2) != 0) &&
1540 (strncmp(buf, "PW ", 3) != 0) &&
1541 (strncmp(buf, "GK", 2) != 0) &&
1542 (strncmp(buf, "GF", 2) != 0)) {
1543 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1544 printf("BH SPNEGO request invalid prefix\n");
1545 talloc_free(mem_ctx);
1546 return;
1549 /* setup gensec */
1550 if (!(state->gensec_state)) {
1551 switch (stdio_helper_mode) {
1552 case GSS_SPNEGO_CLIENT:
1554 * cached credentials are only supported by
1555 * NTLMSSP_CLIENT_1 for now.
1557 use_cached_creds = false;
1558 FALL_THROUGH;
1559 case NTLMSSP_CLIENT_1:
1560 /* setup the client side */
1562 if (state->set_password != NULL) {
1563 use_cached_creds = false;
1566 if (use_cached_creds) {
1567 struct wbcCredentialCacheParams params;
1568 struct wbcCredentialCacheInfo *info = NULL;
1569 struct wbcAuthErrorInfo *error = NULL;
1570 wbcErr wbc_status;
1572 params.account_name = opt_username;
1573 params.domain_name = opt_domain;
1574 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1575 params.num_blobs = 0;
1576 params.blobs = NULL;
1578 wbc_status = wbcCredentialCache(&params, &info,
1579 &error);
1580 wbcFreeMemory(error);
1581 if (!WBC_ERROR_IS_OK(wbc_status)) {
1582 use_cached_creds = false;
1584 wbcFreeMemory(info);
1587 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1588 &state->gensec_state);
1589 if (!NT_STATUS_IS_OK(nt_status)) {
1590 printf("BH GENSEC mech failed to start: %s\n",
1591 nt_errstr(nt_status));
1592 talloc_free(mem_ctx);
1593 return;
1596 creds = cli_credentials_init(state->gensec_state);
1597 cli_credentials_set_conf(creds, lp_ctx);
1598 if (opt_username) {
1599 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1601 if (opt_domain) {
1602 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1604 if (use_cached_creds) {
1605 gensec_want_feature(state->gensec_state,
1606 GENSEC_FEATURE_NTLM_CCACHE);
1607 } else if (state->set_password) {
1608 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1609 } else {
1610 cli_credentials_set_password_callback(creds, get_password);
1612 if (opt_workstation) {
1613 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1616 gensec_set_credentials(state->gensec_state, creds);
1618 break;
1619 case GSS_SPNEGO_SERVER:
1620 case SQUID_2_5_NTLMSSP:
1622 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1623 &state->gensec_state);
1624 if (!NT_STATUS_IS_OK(nt_status)) {
1625 printf("BH GENSEC mech failed to start: %s\n",
1626 nt_errstr(nt_status));
1627 talloc_free(mem_ctx);
1628 return;
1630 break;
1632 default:
1633 talloc_free(mem_ctx);
1634 abort();
1637 gensec_want_feature_list(state->gensec_state, want_feature_list);
1639 /* Session info is not complete, do not pass to auth log */
1640 gensec_want_feature(state->gensec_state, GENSEC_FEATURE_NO_AUTHZ_LOG);
1642 switch (stdio_helper_mode) {
1643 case GSS_SPNEGO_CLIENT:
1644 case GSS_SPNEGO_SERVER:
1645 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1646 if (!in.length) {
1647 first = true;
1649 break;
1650 case NTLMSSP_CLIENT_1:
1651 if (!in.length) {
1652 first = true;
1654 FALL_THROUGH;
1655 case SQUID_2_5_NTLMSSP:
1656 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1657 break;
1658 default:
1659 talloc_free(mem_ctx);
1660 abort();
1663 if (!NT_STATUS_IS_OK(nt_status)) {
1664 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1665 printf("BH GENSEC mech failed to start\n");
1666 talloc_free(mem_ctx);
1667 return;
1672 /* update */
1674 if (strncmp(buf, "PW ", 3) == 0) {
1675 state->set_password = talloc_strndup(state,
1676 (const char *)in.data,
1677 in.length);
1679 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1680 state->set_password,
1681 CRED_SPECIFIED);
1682 printf("OK\n");
1683 talloc_free(mem_ctx);
1684 return;
1687 if (strncmp(buf, "GK", 2) == 0) {
1688 char *base64_key;
1689 DEBUG(10, ("Requested session key\n"));
1690 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1691 if(!NT_STATUS_IS_OK(nt_status)) {
1692 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1693 printf("BH No session key\n");
1694 talloc_free(mem_ctx);
1695 return;
1696 } else {
1697 base64_key = base64_encode_data_blob(state, session_key);
1698 SMB_ASSERT(base64_key != NULL);
1699 printf("GK %s\n", base64_key);
1700 talloc_free(base64_key);
1702 talloc_free(mem_ctx);
1703 return;
1706 if (strncmp(buf, "GF", 2) == 0) {
1707 uint32_t neg_flags;
1709 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1711 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1712 if (neg_flags == 0) {
1713 printf("BH\n");
1714 talloc_free(mem_ctx);
1715 return;
1718 printf("GF 0x%08x\n", neg_flags);
1719 talloc_free(mem_ctx);
1720 return;
1723 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1725 /* don't leak 'bad password'/'no such user' info to the network client */
1726 nt_status = nt_status_squash(nt_status);
1728 if (out.length) {
1729 out_base64 = base64_encode_data_blob(mem_ctx, out);
1730 SMB_ASSERT(out_base64 != NULL);
1731 } else {
1732 out_base64 = NULL;
1735 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1736 reply_arg = "*";
1737 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1738 reply_code = "YR";
1739 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1740 reply_code = "KK";
1741 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1742 reply_code = "TT";
1743 } else {
1744 abort();
1748 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1749 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1750 reply_arg = nt_errstr(nt_status);
1751 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1752 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1753 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1754 reply_arg = nt_errstr(nt_status);
1755 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1756 } else if (!NT_STATUS_IS_OK(nt_status)) {
1757 reply_code = "NA";
1758 reply_arg = nt_errstr(nt_status);
1759 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1760 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1761 struct auth_session_info *session_info;
1763 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1764 if (!NT_STATUS_IS_OK(nt_status)) {
1765 reply_code = "BH Failed to retrieve session info";
1766 reply_arg = nt_errstr(nt_status);
1767 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1768 } else {
1770 reply_code = "AF";
1771 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1772 if (reply_arg == NULL) {
1773 reply_code = "BH out of memory";
1774 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1776 talloc_free(session_info);
1778 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1779 reply_code = "AF";
1780 reply_arg = out_base64;
1781 } else {
1782 abort();
1785 switch (stdio_helper_mode) {
1786 case GSS_SPNEGO_SERVER:
1787 printf("%s %s %s\n", reply_code,
1788 out_base64 ? out_base64 : "*",
1789 reply_arg ? reply_arg : "*");
1790 break;
1791 default:
1792 if (out_base64) {
1793 printf("%s %s\n", reply_code, out_base64);
1794 } else if (reply_arg) {
1795 printf("%s %s\n", reply_code, reply_arg);
1796 } else {
1797 printf("%s\n", reply_code);
1801 talloc_free(mem_ctx);
1802 return;
1805 static void manage_gss_spnego_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_squid_ntlmssp_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 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1820 return;
1823 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1824 struct loadparm_context *lp_ctx,
1825 struct ntlm_auth_state *state,
1826 char *buf, int length, void **private2)
1828 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1829 return;
1832 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1833 struct loadparm_context *lp_ctx,
1834 struct ntlm_auth_state *state,
1835 char *buf, int length, void **private2)
1837 char *request, *parameter;
1838 static DATA_BLOB challenge;
1839 static DATA_BLOB lm_response;
1840 static DATA_BLOB nt_response;
1841 static char *full_username;
1842 static char *username;
1843 static char *domain;
1844 static char *plaintext_password;
1845 static bool ntlm_server_1_user_session_key;
1846 static bool ntlm_server_1_lm_session_key;
1848 if (strequal(buf, ".")) {
1849 if (!full_username && !username) {
1850 printf("Error: No username supplied!\n");
1851 } else if (plaintext_password) {
1852 /* handle this request as plaintext */
1853 if (!full_username) {
1854 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1855 printf("Error: Out of memory in "
1856 "asprintf!\n.\n");
1857 return;
1860 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1861 printf("Authenticated: Yes\n");
1862 } else {
1863 printf("Authenticated: No\n");
1865 } else if (!lm_response.data && !nt_response.data) {
1866 printf("Error: No password supplied!\n");
1867 } else if (!challenge.data) {
1868 printf("Error: No lanman-challenge supplied!\n");
1869 } else {
1870 char *error_string = NULL;
1871 uchar lm_key[8];
1872 uchar user_session_key[16];
1873 uint32_t flags = 0;
1874 NTSTATUS nt_status;
1875 if (full_username && !username) {
1876 fstring fstr_user;
1877 fstring fstr_domain;
1879 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1880 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1881 printf("Error: Could not parse into "
1882 "domain and username\n");
1884 SAFE_FREE(username);
1885 SAFE_FREE(domain);
1886 username = smb_xstrdup(fstr_user);
1887 domain = smb_xstrdup(fstr_domain);
1890 if (opt_password) {
1891 DATA_BLOB nt_session_key, lm_session_key;
1892 struct samr_Password lm_pw, nt_pw;
1893 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1894 ZERO_STRUCT(user_session_key);
1895 ZERO_STRUCT(lm_key);
1897 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1898 nt_status = ntlm_password_check(mem_ctx,
1899 true,
1900 NTLM_AUTH_ON,
1902 &challenge,
1903 &lm_response,
1904 &nt_response,
1905 username,
1906 username,
1907 domain,
1908 &lm_pw, &nt_pw,
1909 &nt_session_key,
1910 &lm_session_key);
1911 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1912 if (ntlm_server_1_user_session_key) {
1913 if (nt_session_key.length == sizeof(user_session_key)) {
1914 memcpy(user_session_key,
1915 nt_session_key.data,
1916 sizeof(user_session_key));
1919 if (ntlm_server_1_lm_session_key) {
1920 if (lm_session_key.length == sizeof(lm_key)) {
1921 memcpy(lm_key,
1922 lm_session_key.data,
1923 sizeof(lm_key));
1926 TALLOC_FREE(mem_ctx);
1928 } else {
1929 uint8_t authoritative = 0;
1931 if (!domain) {
1932 domain = smb_xstrdup(get_winbind_domain());
1935 if (ntlm_server_1_lm_session_key)
1936 flags |= WBFLAG_PAM_LMKEY;
1938 if (ntlm_server_1_user_session_key)
1939 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1941 nt_status = contact_winbind_auth_crap(username,
1942 domain,
1943 lp_netbios_name(),
1944 &challenge,
1945 &lm_response,
1946 &nt_response,
1947 flags, 0,
1948 lm_key,
1949 user_session_key,
1950 &authoritative,
1951 &error_string,
1952 NULL);
1955 if (!NT_STATUS_IS_OK(nt_status)) {
1956 printf("Authenticated: No\n");
1957 printf("Authentication-Error: %s\n.\n",
1958 error_string);
1959 } else {
1960 char *hex_lm_key;
1961 char *hex_user_session_key;
1963 printf("Authenticated: Yes\n");
1965 if (ntlm_server_1_lm_session_key
1966 && (!all_zero(lm_key,
1967 sizeof(lm_key)))) {
1968 hex_lm_key = hex_encode_talloc(NULL,
1969 (const unsigned char *)lm_key,
1970 sizeof(lm_key));
1971 printf("LANMAN-Session-Key: %s\n",
1972 hex_lm_key);
1973 TALLOC_FREE(hex_lm_key);
1976 if (ntlm_server_1_user_session_key
1977 && (!all_zero(user_session_key,
1978 sizeof(user_session_key)))) {
1979 hex_user_session_key = hex_encode_talloc(NULL,
1980 (const unsigned char *)user_session_key,
1981 sizeof(user_session_key));
1982 printf("User-Session-Key: %s\n",
1983 hex_user_session_key);
1984 TALLOC_FREE(hex_user_session_key);
1987 SAFE_FREE(error_string);
1989 /* clear out the state */
1990 challenge = data_blob_null;
1991 nt_response = data_blob_null;
1992 lm_response = data_blob_null;
1993 SAFE_FREE(full_username);
1994 SAFE_FREE(username);
1995 SAFE_FREE(domain);
1996 SAFE_FREE(plaintext_password);
1997 ntlm_server_1_user_session_key = False;
1998 ntlm_server_1_lm_session_key = False;
1999 printf(".\n");
2001 return;
2004 request = buf;
2006 /* Indicates a base64 encoded structure */
2007 parameter = strstr_m(request, ":: ");
2008 if (!parameter) {
2009 parameter = strstr_m(request, ": ");
2011 if (!parameter) {
2012 DEBUG(0, ("Parameter not found!\n"));
2013 printf("Error: Parameter not found!\n.\n");
2014 return;
2017 parameter[0] ='\0';
2018 parameter++;
2019 parameter[0] ='\0';
2020 parameter++;
2022 } else {
2023 parameter[0] ='\0';
2024 parameter++;
2025 parameter[0] ='\0';
2026 parameter++;
2027 parameter[0] ='\0';
2028 parameter++;
2030 base64_decode_inplace(parameter);
2033 if (strequal(request, "LANMAN-Challenge")) {
2034 challenge = strhex_to_data_blob(NULL, parameter);
2035 if (challenge.length != 8) {
2036 printf("Error: hex decode of %s failed! "
2037 "(got %d bytes, expected 8)\n.\n",
2038 parameter,
2039 (int)challenge.length);
2040 challenge = data_blob_null;
2042 } else if (strequal(request, "NT-Response")) {
2043 nt_response = strhex_to_data_blob(NULL, parameter);
2044 if (nt_response.length < 24) {
2045 printf("Error: hex decode of %s failed! "
2046 "(only got %d bytes, needed at least 24)\n.\n",
2047 parameter,
2048 (int)nt_response.length);
2049 nt_response = data_blob_null;
2051 } else if (strequal(request, "LANMAN-Response")) {
2052 lm_response = strhex_to_data_blob(NULL, parameter);
2053 if (lm_response.length != 24) {
2054 printf("Error: hex decode of %s failed! "
2055 "(got %d bytes, expected 24)\n.\n",
2056 parameter,
2057 (int)lm_response.length);
2058 lm_response = data_blob_null;
2060 } else if (strequal(request, "Password")) {
2061 plaintext_password = smb_xstrdup(parameter);
2062 } else if (strequal(request, "NT-Domain")) {
2063 domain = smb_xstrdup(parameter);
2064 } else if (strequal(request, "Username")) {
2065 username = smb_xstrdup(parameter);
2066 } else if (strequal(request, "Full-Username")) {
2067 full_username = smb_xstrdup(parameter);
2068 } else if (strequal(request, "Request-User-Session-Key")) {
2069 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2070 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2071 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2072 } else {
2073 printf("Error: Unknown request %s\n.\n", request);
2077 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2078 struct loadparm_context *lp_ctx,
2079 struct ntlm_auth_state *state,
2080 char *buf, int length, void **private2)
2082 char *request, *parameter;
2083 static DATA_BLOB new_nt_pswd;
2084 static DATA_BLOB old_nt_hash_enc;
2085 static DATA_BLOB new_lm_pswd;
2086 static DATA_BLOB old_lm_hash_enc;
2087 static char *full_username = NULL;
2088 static char *username = NULL;
2089 static char *domain = NULL;
2090 static char *newpswd = NULL;
2091 static char *oldpswd = NULL;
2093 if (strequal(buf, ".")) {
2094 if(newpswd && oldpswd) {
2095 uchar old_nt_hash[16];
2096 uchar old_lm_hash[16];
2097 uchar new_nt_hash[16];
2098 uchar new_lm_hash[16];
2100 gnutls_cipher_hd_t cipher_hnd = NULL;
2101 gnutls_datum_t old_nt_key = {
2102 .data = old_nt_hash,
2103 .size = sizeof(old_nt_hash),
2105 int rc;
2107 new_nt_pswd = data_blob(NULL, 516);
2108 old_nt_hash_enc = data_blob(NULL, 16);
2110 /* Calculate the MD4 hash (NT compatible) of the
2111 * password */
2112 E_md4hash(oldpswd, old_nt_hash);
2113 E_md4hash(newpswd, new_nt_hash);
2115 /* E_deshash returns false for 'long'
2116 passwords (> 14 DOS chars).
2118 Therefore, don't send a buffer
2119 encrypted with the truncated hash
2120 (it could allow an even easier
2121 attack on the password)
2123 Likewise, obey the admin's restriction
2126 rc = gnutls_cipher_init(&cipher_hnd,
2127 GNUTLS_CIPHER_ARCFOUR_128,
2128 &old_nt_key,
2129 NULL);
2130 if (rc < 0) {
2131 DBG_ERR("gnutls_cipher_init failed: %s\n",
2132 gnutls_strerror(rc));
2133 if (rc == GNUTLS_E_UNWANTED_ALGORITHM) {
2134 DBG_ERR("Running in FIPS mode, NTLM blocked\n");
2136 return;
2139 if (lp_client_lanman_auth() &&
2140 E_deshash(newpswd, new_lm_hash) &&
2141 E_deshash(oldpswd, old_lm_hash)) {
2142 new_lm_pswd = data_blob(NULL, 516);
2143 old_lm_hash_enc = data_blob(NULL, 16);
2144 encode_pw_buffer(new_lm_pswd.data, newpswd,
2145 STR_UNICODE);
2147 rc = gnutls_cipher_encrypt(cipher_hnd,
2148 new_lm_pswd.data,
2149 516);
2150 if (rc < 0) {
2151 gnutls_cipher_deinit(cipher_hnd);
2152 return;
2154 rc = E_old_pw_hash(new_nt_hash, old_lm_hash,
2155 old_lm_hash_enc.data);
2156 if (rc != 0) {
2157 DBG_ERR("E_old_pw_hash failed: %s\n",
2158 gnutls_strerror(rc));
2159 return;
2161 } else {
2162 new_lm_pswd.data = NULL;
2163 new_lm_pswd.length = 0;
2164 old_lm_hash_enc.data = NULL;
2165 old_lm_hash_enc.length = 0;
2168 encode_pw_buffer(new_nt_pswd.data, newpswd,
2169 STR_UNICODE);
2171 rc = gnutls_cipher_encrypt(cipher_hnd,
2172 new_nt_pswd.data,
2173 516);
2174 gnutls_cipher_deinit(cipher_hnd);
2175 if (rc < 0) {
2176 return;
2178 rc = E_old_pw_hash(new_nt_hash, old_nt_hash,
2179 old_nt_hash_enc.data);
2180 if (rc != 0) {
2181 DBG_ERR("E_old_pw_hash failed: %s\n",
2182 gnutls_strerror(rc));
2183 return;
2186 ZERO_ARRAY(old_nt_hash);
2187 ZERO_ARRAY(old_lm_hash);
2188 ZERO_ARRAY(new_nt_hash);
2189 ZERO_ARRAY(new_lm_hash);
2192 if (!full_username && !username) {
2193 printf("Error: No username supplied!\n");
2194 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2195 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2196 printf("Error: No NT or LM password "
2197 "blobs supplied!\n");
2198 } else {
2199 char *error_string = NULL;
2201 if (full_username && !username) {
2202 fstring fstr_user;
2203 fstring fstr_domain;
2205 if (!parse_ntlm_auth_domain_user(full_username,
2206 fstr_user,
2207 fstr_domain)) {
2208 /* username might be 'tainted', don't
2209 * print into our new-line
2210 * deleimianted stream */
2211 printf("Error: Could not "
2212 "parse into domain and "
2213 "username\n");
2214 SAFE_FREE(username);
2215 username = smb_xstrdup(full_username);
2216 } else {
2217 SAFE_FREE(username);
2218 SAFE_FREE(domain);
2219 username = smb_xstrdup(fstr_user);
2220 domain = smb_xstrdup(fstr_domain);
2225 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2226 username, domain,
2227 new_nt_pswd,
2228 old_nt_hash_enc,
2229 new_lm_pswd,
2230 old_lm_hash_enc,
2231 &error_string))) {
2232 printf("Password-Change: No\n");
2233 printf("Password-Change-Error: %s\n.\n",
2234 error_string);
2235 } else {
2236 printf("Password-Change: Yes\n");
2239 SAFE_FREE(error_string);
2241 /* clear out the state */
2242 new_nt_pswd = data_blob_null;
2243 old_nt_hash_enc = data_blob_null;
2244 new_lm_pswd = data_blob_null;
2245 old_nt_hash_enc = data_blob_null;
2246 SAFE_FREE(full_username);
2247 SAFE_FREE(username);
2248 SAFE_FREE(domain);
2249 SAFE_FREE(newpswd);
2250 SAFE_FREE(oldpswd);
2251 printf(".\n");
2253 return;
2256 request = buf;
2258 /* Indicates a base64 encoded structure */
2259 parameter = strstr_m(request, ":: ");
2260 if (!parameter) {
2261 parameter = strstr_m(request, ": ");
2263 if (!parameter) {
2264 DEBUG(0, ("Parameter not found!\n"));
2265 printf("Error: Parameter not found!\n.\n");
2266 return;
2269 parameter[0] ='\0';
2270 parameter++;
2271 parameter[0] ='\0';
2272 parameter++;
2273 } else {
2274 parameter[0] ='\0';
2275 parameter++;
2276 parameter[0] ='\0';
2277 parameter++;
2278 parameter[0] ='\0';
2279 parameter++;
2281 base64_decode_inplace(parameter);
2284 if (strequal(request, "new-nt-password-blob")) {
2285 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2286 if (new_nt_pswd.length != 516) {
2287 printf("Error: hex decode of %s failed! "
2288 "(got %d bytes, expected 516)\n.\n",
2289 parameter,
2290 (int)new_nt_pswd.length);
2291 new_nt_pswd = data_blob_null;
2293 } else if (strequal(request, "old-nt-hash-blob")) {
2294 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2295 if (old_nt_hash_enc.length != 16) {
2296 printf("Error: hex decode of %s failed! "
2297 "(got %d bytes, expected 16)\n.\n",
2298 parameter,
2299 (int)old_nt_hash_enc.length);
2300 old_nt_hash_enc = data_blob_null;
2302 } else if (strequal(request, "new-lm-password-blob")) {
2303 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2304 if (new_lm_pswd.length != 516) {
2305 printf("Error: hex decode of %s failed! "
2306 "(got %d bytes, expected 516)\n.\n",
2307 parameter,
2308 (int)new_lm_pswd.length);
2309 new_lm_pswd = data_blob_null;
2312 else if (strequal(request, "old-lm-hash-blob")) {
2313 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2314 if (old_lm_hash_enc.length != 16)
2316 printf("Error: hex decode of %s failed! "
2317 "(got %d bytes, expected 16)\n.\n",
2318 parameter,
2319 (int)old_lm_hash_enc.length);
2320 old_lm_hash_enc = data_blob_null;
2322 } else if (strequal(request, "nt-domain")) {
2323 domain = smb_xstrdup(parameter);
2324 } else if(strequal(request, "username")) {
2325 username = smb_xstrdup(parameter);
2326 } else if(strequal(request, "full-username")) {
2327 username = smb_xstrdup(parameter);
2328 } else if(strequal(request, "new-password")) {
2329 newpswd = smb_xstrdup(parameter);
2330 } else if (strequal(request, "old-password")) {
2331 oldpswd = smb_xstrdup(parameter);
2332 } else {
2333 printf("Error: Unknown request %s\n.\n", request);
2337 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2338 struct loadparm_context *lp_ctx,
2339 struct ntlm_auth_state *state,
2340 stdio_helper_function fn, void **private2)
2342 char *buf;
2343 char tmp[INITIAL_BUFFER_SIZE+1];
2344 int length, buf_size = 0;
2345 char *c;
2347 buf = talloc_strdup(state->mem_ctx, "");
2348 if (!buf) {
2349 DEBUG(0, ("Failed to allocate input buffer.\n"));
2350 fprintf(stderr, "ERR\n");
2351 exit(1);
2354 do {
2356 /* this is not a typo - x_fgets doesn't work too well under
2357 * squid */
2358 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2359 if (ferror(stdin)) {
2360 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2361 "(%s)\n", ferror(stdin),
2362 strerror(ferror(stdin))));
2364 exit(1);
2366 exit(0);
2369 buf = talloc_strdup_append_buffer(buf, tmp);
2370 buf_size += INITIAL_BUFFER_SIZE;
2372 if (buf_size > MAX_BUFFER_SIZE) {
2373 DEBUG(2, ("Oversized message\n"));
2374 fprintf(stderr, "ERR\n");
2375 talloc_free(buf);
2376 return;
2379 c = strchr(buf, '\n');
2380 } while (c == NULL);
2382 *c = '\0';
2383 length = c-buf;
2385 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2387 if (buf[0] == '\0') {
2388 DEBUG(2, ("Invalid Request\n"));
2389 fprintf(stderr, "ERR\n");
2390 talloc_free(buf);
2391 return;
2394 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2395 talloc_free(buf);
2399 static void squid_stream(enum stdio_helper_mode stdio_mode,
2400 struct loadparm_context *lp_ctx,
2401 stdio_helper_function fn) {
2402 TALLOC_CTX *mem_ctx;
2403 struct ntlm_auth_state *state;
2405 /* initialize FDescs */
2406 setbuf(stdout, NULL);
2407 setbuf(stderr, NULL);
2409 mem_ctx = talloc_init("ntlm_auth");
2410 if (!mem_ctx) {
2411 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2412 fprintf(stderr, "ERR\n");
2413 exit(1);
2416 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2417 if (!state) {
2418 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2419 fprintf(stderr, "ERR\n");
2420 exit(1);
2423 state->mem_ctx = mem_ctx;
2424 state->helper_mode = stdio_mode;
2426 while(1) {
2427 TALLOC_CTX *frame = talloc_stackframe();
2428 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2429 TALLOC_FREE(frame);
2434 /* Authenticate a user with a challenge/response */
2436 static bool check_auth_crap(void)
2438 NTSTATUS nt_status;
2439 uint32_t flags = 0;
2440 char lm_key[8];
2441 char user_session_key[16];
2442 char *hex_lm_key;
2443 char *hex_user_session_key;
2444 char *error_string;
2445 uint8_t authoritative = 0;
2447 setbuf(stdout, NULL);
2449 if (request_lm_key)
2450 flags |= WBFLAG_PAM_LMKEY;
2452 if (request_user_session_key)
2453 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2455 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2457 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2458 opt_workstation,
2459 &opt_challenge,
2460 &opt_lm_response,
2461 &opt_nt_response,
2462 flags, 0,
2463 (unsigned char *)lm_key,
2464 (unsigned char *)user_session_key,
2465 &authoritative,
2466 &error_string, NULL);
2468 if (!NT_STATUS_IS_OK(nt_status)) {
2469 printf("%s (0x%x)\n", error_string,
2470 NT_STATUS_V(nt_status));
2471 SAFE_FREE(error_string);
2472 return False;
2475 if (request_lm_key
2476 && (!all_zero((uint8_t *)lm_key, sizeof(lm_key)))) {
2477 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2478 sizeof(lm_key));
2479 printf("LM_KEY: %s\n", hex_lm_key);
2480 TALLOC_FREE(hex_lm_key);
2482 if (request_user_session_key
2483 && (!all_zero((uint8_t *)user_session_key,
2484 sizeof(user_session_key)))) {
2485 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2486 sizeof(user_session_key));
2487 printf("NT_KEY: %s\n", hex_user_session_key);
2488 TALLOC_FREE(hex_user_session_key);
2491 return True;
2494 /* Main program */
2496 enum {
2497 OPT_USERNAME = 1000,
2498 OPT_DOMAIN,
2499 OPT_WORKSTATION,
2500 OPT_CHALLENGE,
2501 OPT_RESPONSE,
2502 OPT_LM,
2503 OPT_NT,
2504 OPT_PASSWORD,
2505 OPT_LM_KEY,
2506 OPT_USER_SESSION_KEY,
2507 OPT_DIAGNOSTICS,
2508 OPT_REQUIRE_MEMBERSHIP,
2509 OPT_USE_CACHED_CREDS,
2510 OPT_ALLOW_MSCHAPV2,
2511 OPT_PAM_WINBIND_CONF,
2512 OPT_TARGET_SERVICE,
2513 OPT_TARGET_HOSTNAME,
2514 OPT_OFFLINE_LOGON
2517 int main(int argc, const char **argv)
2519 TALLOC_CTX *frame = talloc_stackframe();
2520 int opt;
2521 const char *helper_protocol = NULL;
2522 int diagnostics = 0;
2524 const char *hex_challenge = NULL;
2525 const char *hex_lm_response = NULL;
2526 const char *hex_nt_response = NULL;
2527 struct loadparm_context *lp_ctx;
2528 poptContext pc;
2530 /* NOTE: DO NOT change this interface without considering the implications!
2531 This is an external interface, which other programs will use to interact
2532 with this helper.
2535 /* We do not use single-letter command abbreviations, because they harm future
2536 interface stability. */
2538 struct poptOption long_options[] = {
2539 POPT_AUTOHELP
2541 .longName = "helper-protocol",
2542 .shortName = 0,
2543 .argInfo = POPT_ARG_STRING,
2544 .arg = &helper_protocol,
2545 .val = OPT_DOMAIN,
2546 .descrip = "operate as a stdio-based helper",
2547 .argDescrip = "helper protocol to use"
2550 .longName = "username",
2551 .shortName = 0,
2552 .argInfo = POPT_ARG_STRING,
2553 .arg = &opt_username,
2554 .val = OPT_USERNAME,
2555 .descrip = "username"
2558 .longName = "domain",
2559 .shortName = 0,
2560 .argInfo = POPT_ARG_STRING,
2561 .arg = &opt_domain,
2562 .val = OPT_DOMAIN,
2563 .descrip = "domain name"
2566 .longName = "workstation",
2567 .shortName = 0,
2568 .argInfo = POPT_ARG_STRING,
2569 .arg = &opt_workstation,
2570 .val = OPT_WORKSTATION,
2571 .descrip = "workstation"
2574 .longName = "challenge",
2575 .shortName = 0,
2576 .argInfo = POPT_ARG_STRING,
2577 .arg = &hex_challenge,
2578 .val = OPT_CHALLENGE,
2579 .descrip = "challenge (HEX encoded)"
2582 .longName = "lm-response",
2583 .shortName = 0,
2584 .argInfo = POPT_ARG_STRING,
2585 .arg = &hex_lm_response,
2586 .val = OPT_LM,
2587 .descrip = "LM Response to the challenge (HEX encoded)"
2590 .longName = "nt-response",
2591 .shortName = 0,
2592 .argInfo = POPT_ARG_STRING,
2593 .arg = &hex_nt_response,
2594 .val = OPT_NT,
2595 .descrip = "NT or NTLMv2 Response to the challenge (HEX encoded)"
2598 .longName = "password",
2599 .shortName = 0,
2600 .argInfo = POPT_ARG_STRING,
2601 .arg = &opt_password,
2602 .val = OPT_PASSWORD,
2603 .descrip = "User's plaintext password"
2606 .longName = "request-lm-key",
2607 .shortName = 0,
2608 .argInfo = POPT_ARG_NONE,
2609 .arg = &request_lm_key,
2610 .val = OPT_LM_KEY,
2611 .descrip = "Retrieve LM session key"
2614 .longName = "request-nt-key",
2615 .shortName = 0,
2616 .argInfo = POPT_ARG_NONE,
2617 .arg = &request_user_session_key,
2618 .val = OPT_USER_SESSION_KEY,
2619 .descrip = "Retrieve User (NT) session key"
2622 .longName = "use-cached-creds",
2623 .shortName = 0,
2624 .argInfo = POPT_ARG_NONE,
2625 .arg = &use_cached_creds,
2626 .val = OPT_USE_CACHED_CREDS,
2627 .descrip = "Use cached credentials if no password is given"
2630 .longName = "allow-mschapv2",
2631 .shortName = 0,
2632 .argInfo = POPT_ARG_NONE,
2633 .arg = &opt_allow_mschapv2,
2634 .val = OPT_ALLOW_MSCHAPV2,
2635 .descrip = "Explicitly allow MSCHAPv2",
2638 .longName = "offline-logon",
2639 .shortName = 0,
2640 .argInfo = POPT_ARG_NONE,
2641 .arg = &offline_logon,
2642 .val = OPT_OFFLINE_LOGON,
2643 .descrip = "Use cached passwords when DC is offline"
2646 .longName = "diagnostics",
2647 .shortName = 0,
2648 .argInfo = POPT_ARG_NONE,
2649 .arg = &diagnostics,
2650 .val = OPT_DIAGNOSTICS,
2651 .descrip = "Perform diagnostics on the authentication chain"
2654 .longName = "require-membership-of",
2655 .shortName = 0,
2656 .argInfo = POPT_ARG_STRING,
2657 .arg = &require_membership_of,
2658 .val = OPT_REQUIRE_MEMBERSHIP,
2659 .descrip = "Require that a user be a member of this group (either name or SID) for authentication to succeed",
2662 .longName = "pam-winbind-conf",
2663 .shortName = 0,
2664 .argInfo = POPT_ARG_STRING,
2665 .arg = &opt_pam_winbind_conf,
2666 .val = OPT_PAM_WINBIND_CONF,
2667 .descrip = "Require that request must set WBFLAG_PAM_CONTACT_TRUSTDOM when krb5 auth is required",
2670 .longName = "target-service",
2671 .shortName = 0,
2672 .argInfo = POPT_ARG_STRING,
2673 .arg = &opt_target_service,
2674 .val = OPT_TARGET_SERVICE,
2675 .descrip = "Target service (eg http)",
2678 .longName = "target-hostname",
2679 .shortName = 0,
2680 .argInfo = POPT_ARG_STRING,
2681 .arg = &opt_target_hostname,
2682 .val = OPT_TARGET_HOSTNAME,
2683 .descrip = "Target hostname",
2685 POPT_COMMON_CONFIGFILE
2686 POPT_COMMON_VERSION
2687 POPT_COMMON_OPTION
2688 POPT_TABLEEND
2691 /* Samba client initialisation */
2692 smb_init_locale();
2694 setup_logging("ntlm_auth", DEBUG_STDERR);
2695 fault_setup();
2697 /* Parse options */
2699 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2701 /* Parse command line options */
2703 if (argc == 1) {
2704 poptPrintHelp(pc, stderr, 0);
2705 poptFreeContext(pc);
2706 return 1;
2709 while((opt = poptGetNextOpt(pc)) != -1) {
2710 /* Get generic config options like --configfile */
2713 poptFreeContext(pc);
2715 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2716 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2717 get_dyn_CONFIGFILE(), strerror(errno));
2718 exit(1);
2721 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2722 POPT_CONTEXT_KEEP_FIRST);
2724 while((opt = poptGetNextOpt(pc)) != -1) {
2725 switch (opt) {
2726 case OPT_CHALLENGE:
2727 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2728 if (opt_challenge.length != 8) {
2729 fprintf(stderr, "hex decode of %s failed! "
2730 "(only got %d bytes)\n",
2731 hex_challenge,
2732 (int)opt_challenge.length);
2733 exit(1);
2735 break;
2736 case OPT_LM:
2737 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2738 if (opt_lm_response.length != 24) {
2739 fprintf(stderr, "hex decode of %s failed! "
2740 "(only got %d bytes)\n",
2741 hex_lm_response,
2742 (int)opt_lm_response.length);
2743 exit(1);
2745 break;
2747 case OPT_NT:
2748 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2749 if (opt_nt_response.length < 24) {
2750 fprintf(stderr, "hex decode of %s failed! "
2751 "(only got %d bytes)\n",
2752 hex_nt_response,
2753 (int)opt_nt_response.length);
2754 exit(1);
2756 break;
2758 case OPT_REQUIRE_MEMBERSHIP:
2759 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2760 require_membership_of_sid = require_membership_of;
2762 break;
2766 if (opt_username) {
2767 char *domain = SMB_STRDUP(opt_username);
2768 char *p = strchr_m(domain, *lp_winbind_separator());
2769 if (p) {
2770 opt_username = p+1;
2771 *p = '\0';
2772 if (opt_domain && !strequal(opt_domain, domain)) {
2773 fprintf(stderr, "Domain specified in username (%s) "
2774 "doesn't match specified domain (%s)!\n\n",
2775 domain, opt_domain);
2776 poptPrintHelp(pc, stderr, 0);
2777 exit(1);
2779 opt_domain = domain;
2780 } else {
2781 SAFE_FREE(domain);
2785 /* Note: if opt_domain is "" then send no domain */
2786 if (opt_domain == NULL) {
2787 opt_domain = get_winbind_domain();
2790 if (opt_workstation == NULL) {
2791 opt_workstation = "";
2794 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2795 if (lp_ctx == NULL) {
2796 fprintf(stderr, "loadparm_init_s3() failed!\n");
2797 exit(1);
2800 if (helper_protocol) {
2801 int i;
2802 for (i=0; i<NUM_HELPER_MODES; i++) {
2803 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2804 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2805 exit(0);
2808 fprintf(stderr, "unknown helper protocol [%s]\n\n"
2809 "Valid helper protools:\n\n", helper_protocol);
2811 for (i=0; i<NUM_HELPER_MODES; i++) {
2812 fprintf(stderr, "%s\n",
2813 stdio_helper_protocols[i].name);
2816 exit(1);
2819 if (!opt_username || !*opt_username) {
2820 fprintf(stderr, "username must be specified!\n\n");
2821 poptPrintHelp(pc, stderr, 0);
2822 exit(1);
2825 if (opt_challenge.length) {
2826 if (!check_auth_crap()) {
2827 exit(1);
2829 exit(0);
2832 if (!opt_password) {
2833 char pwd[256] = {0};
2834 int rc;
2836 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2837 if (rc == 0) {
2838 opt_password = SMB_STRDUP(pwd);
2842 if (diagnostics) {
2843 if (!diagnose_ntlm_auth()) {
2844 poptFreeContext(pc);
2845 return 1;
2847 } else {
2848 fstring user;
2850 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2851 if (!check_plaintext_auth(user, opt_password, True)) {
2852 poptFreeContext(pc);
2853 return 1;
2857 /* Exit code */
2859 poptFreeContext(pc);
2860 TALLOC_FREE(frame);
2861 return 0;