tests/krb5: Don't create PAC request or options manually in fast_tests
[Samba.git] / source3 / utils / ntlm_auth.c
blobf887d6814d0d54bbda9a7f6fad79f1a593c6cf69
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind status program.
6 Copyright (C) Tim Potter 2000-2003
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
8 Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000
9 Copyright (C) Robert O'Callahan 2006 (added cached credential code).
10 Copyright (C) Kai Blin <kai@samba.org> 2008
11 Copyright (C) Simo Sorce 2010
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 3 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "includes.h"
28 #include "lib/param/param.h"
29 #include "lib/cmdline/cmdline.h"
30 #include "libcli/security/security.h"
31 #include "utils/ntlm_auth.h"
32 #include "../libcli/auth/libcli_auth.h"
33 #include "auth/ntlmssp/ntlmssp.h"
34 #include "auth/gensec/gensec.h"
35 #include "auth/gensec/gensec_internal.h"
36 #include "auth/credentials/credentials.h"
37 #include "librpc/crypto/gse.h"
38 #include "smb_krb5.h"
39 #include "lib/util/tiniparser.h"
40 #include "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"
51 #include "lib/util/string_wrappers.h"
53 #include <gnutls/gnutls.h>
54 #include <gnutls/crypto.h>
56 #ifdef HAVE_KRB5
57 #include "auth/kerberos/pac_utils.h"
58 #endif
60 #ifndef PAM_WINBIND_CONFIG_FILE
61 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
62 #endif
64 #define WINBIND_KRB5_AUTH 0x00000080
66 #undef DBGC_CLASS
67 #define DBGC_CLASS DBGC_WINBIND
69 #define INITIAL_BUFFER_SIZE 300
70 #define MAX_BUFFER_SIZE 630000
72 enum stdio_helper_mode {
73 SQUID_2_4_BASIC,
74 SQUID_2_5_BASIC,
75 SQUID_2_5_NTLMSSP,
76 NTLMSSP_CLIENT_1,
77 GSS_SPNEGO_SERVER,
78 GSS_SPNEGO_CLIENT,
79 NTLM_SERVER_1,
80 NTLM_CHANGE_PASSWORD_1,
81 NUM_HELPER_MODES
84 enum ntlm_auth_cli_state {
85 CLIENT_INITIAL = 0,
86 CLIENT_RESPONSE,
87 CLIENT_FINISHED,
88 CLIENT_ERROR
91 struct ntlm_auth_state {
92 TALLOC_CTX *mem_ctx;
93 enum stdio_helper_mode helper_mode;
94 enum ntlm_auth_cli_state cli_state;
95 struct ntlmssp_state *ntlmssp_state;
96 uint32_t neg_flags;
97 char *want_feature_list;
98 bool have_session_key;
99 DATA_BLOB session_key;
100 DATA_BLOB initial_message;
101 void *gensec_private_1;
103 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
104 struct loadparm_context *lp_ctx,
105 struct ntlm_auth_state *state, char *buf,
106 int length, void **private2);
108 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
109 struct loadparm_context *lp_ctx,
110 char *buf, int length, void **private1);
112 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
113 struct loadparm_context *lp_ctx,
114 struct ntlm_auth_state *state,
115 stdio_helper_function fn, void **private2);
117 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
118 struct loadparm_context *lp_ctx,
119 struct ntlm_auth_state *state,
120 char *buf, int length, void **private2);
122 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
123 struct loadparm_context *lp_ctx,
124 struct ntlm_auth_state *state,
125 char *buf, int length, void **private2);
127 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
128 struct loadparm_context *lp_ctx,
129 struct ntlm_auth_state *state,
130 char *buf, int length, void **private2);
132 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
133 struct loadparm_context *lp_ctx,
134 struct ntlm_auth_state *state,
135 char *buf, int length, void **private2);
137 static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
138 struct loadparm_context *lp_ctx,
139 struct ntlm_auth_state *state,
140 char *buf, int length, void **private2);
142 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
143 struct loadparm_context *lp_ctx,
144 struct ntlm_auth_state *state,
145 char *buf, int length, void **private2);
147 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
148 struct loadparm_context *lp_ctx,
149 struct ntlm_auth_state *state,
150 char *buf, int length, void **private2);
152 static const struct {
153 enum stdio_helper_mode mode;
154 const char *name;
155 stdio_helper_function fn;
156 } stdio_helper_protocols[] = {
157 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
158 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
159 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
160 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
161 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
162 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
163 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
164 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
165 { NUM_HELPER_MODES, NULL, NULL}
168 const char *opt_username;
169 const char *opt_domain;
170 const char *opt_workstation;
171 const char *opt_password;
172 static DATA_BLOB opt_challenge;
173 static DATA_BLOB opt_lm_response;
174 static DATA_BLOB opt_nt_response;
175 static int request_lm_key;
176 static int request_user_session_key;
177 static int use_cached_creds;
178 static int offline_logon;
179 static int opt_allow_mschapv2;
181 static const char *require_membership_of;
182 static const char *require_membership_of_sid;
183 static const char *opt_pam_winbind_conf;
185 const char *opt_target_service;
186 const char *opt_target_hostname;
189 /* This is a bit hairy, but the basic idea is to do a password callback
190 to the calling application. The callback comes from within gensec */
192 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
193 struct loadparm_context *lp_ctx,
194 struct ntlm_auth_state *state, char *buf, int length,
195 void **password)
197 DATA_BLOB in;
198 if (strlen(buf) < 2) {
199 DEBUG(1, ("query [%s] invalid", buf));
200 printf("BH Query invalid\n");
201 return;
204 if (strlen(buf) > 3) {
205 in = base64_decode_data_blob(buf + 3);
206 } else {
207 in = data_blob(NULL, 0);
210 if (strncmp(buf, "PW ", 3) == 0) {
212 *password = talloc_strndup(NULL,
213 (const char *)in.data, in.length);
215 if (*password == NULL) {
216 DEBUG(1, ("Out of memory\n"));
217 printf("BH Out of memory\n");
218 data_blob_free(&in);
219 return;
222 printf("OK\n");
223 data_blob_free(&in);
224 return;
226 DEBUG(1, ("Asked for (and expected) a password\n"));
227 printf("BH Expected a password\n");
228 data_blob_free(&in);
232 * Callback for password credentials. This is not async, and when
233 * GENSEC and the credentials code is made async, it will look rather
234 * different.
237 static const char *get_password(struct cli_credentials *credentials)
239 TALLOC_CTX *frame = talloc_stackframe();
240 char *password = NULL;
241 struct ntlm_auth_state *state;
243 state = talloc_zero(frame, struct ntlm_auth_state);
244 if (state == NULL) {
245 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
246 fprintf(stderr, "ERR\n");
247 exit(1);
250 state->mem_ctx = state;
252 /* Ask for a password */
253 printf("PW\n");
255 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, state, manage_gensec_get_pw_request, (void **)&password);
256 talloc_steal(credentials, password);
257 TALLOC_FREE(frame);
258 return password;
262 * A limited set of features are defined with text strings as needed
263 * by ntlm_auth
266 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
268 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
269 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
270 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
272 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
273 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
274 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
276 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
277 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
278 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
280 if (in_list("NTLMSSP_FEATURE_CCACHE", feature_list, true)) {
281 DEBUG(10, ("want GENSEC_FEATURE_NTLM_CCACHE\n"));
282 gensec_want_feature(state, GENSEC_FEATURE_NTLM_CCACHE);
286 static char winbind_separator(void)
288 struct wbcInterfaceDetails *details;
289 wbcErr ret;
290 static bool got_sep;
291 static char sep;
293 if (got_sep)
294 return sep;
296 ret = wbcInterfaceDetails(&details);
297 if (!WBC_ERROR_IS_OK(ret)) {
298 d_fprintf(stderr, "could not obtain winbind separator!\n");
299 return *lp_winbind_separator();
302 sep = details->winbind_separator;
304 wbcFreeMemory(details);
306 got_sep = True;
308 if (!sep) {
309 d_fprintf(stderr, "winbind separator was NULL!\n");
310 return *lp_winbind_separator();
313 return sep;
316 const char *get_winbind_domain(void)
318 struct wbcInterfaceDetails *details;
319 wbcErr ret;
321 static fstring winbind_domain;
322 if (*winbind_domain) {
323 return winbind_domain;
326 /* Send off request */
328 ret = wbcInterfaceDetails(&details);
329 if (!WBC_ERROR_IS_OK(ret)) {
330 DEBUG(1, ("could not obtain winbind domain name!\n"));
331 return lp_workgroup();
334 fstrcpy(winbind_domain, details->netbios_domain);
336 wbcFreeMemory(details);
338 return winbind_domain;
342 const char *get_winbind_netbios_name(void)
344 struct wbcInterfaceDetails *details;
345 wbcErr ret;
347 static fstring winbind_netbios_name;
349 if (*winbind_netbios_name) {
350 return winbind_netbios_name;
353 /* Send off request */
355 ret = wbcInterfaceDetails(&details);
356 if (!WBC_ERROR_IS_OK(ret)) {
357 DEBUG(1, ("could not obtain winbind netbios name!\n"));
358 return lp_netbios_name();
361 fstrcpy(winbind_netbios_name, details->netbios_name);
363 wbcFreeMemory(details);
365 return winbind_netbios_name;
369 DATA_BLOB get_challenge(void)
371 static DATA_BLOB chal;
372 if (opt_challenge.length)
373 return opt_challenge;
375 chal = data_blob(NULL, 8);
377 generate_random_buffer(chal.data, chal.length);
378 return chal;
381 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
382 form DOMAIN/user into a domain and a user */
384 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
385 fstring user)
388 char *p = strchr(domuser,winbind_separator());
390 if (!p) {
391 return False;
394 fstrcpy(user, p+1);
395 fstrcpy(domain, domuser);
396 domain[PTR_DIFF(p, domuser)] = 0;
397 return strupper_m(domain);
400 static bool get_require_membership_sid(void) {
401 fstring domain, name, sidbuf;
402 struct wbcDomainSid sid;
403 enum wbcSidType type;
404 wbcErr ret;
406 if (!require_membership_of) {
407 return True;
410 if (require_membership_of_sid) {
411 return True;
414 /* Otherwise, ask winbindd for the name->sid request */
416 if (!parse_ntlm_auth_domain_user(require_membership_of,
417 domain, name)) {
418 DEBUG(0, ("Could not parse %s into separate domain/name parts!\n",
419 require_membership_of));
420 return False;
423 ret = wbcLookupName(domain, name, &sid, &type);
424 if (!WBC_ERROR_IS_OK(ret)) {
425 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
426 require_membership_of));
427 return False;
430 wbcSidToStringBuf(&sid, sidbuf, sizeof(sidbuf));
432 require_membership_of_sid = SMB_STRDUP(sidbuf);
434 if (require_membership_of_sid)
435 return True;
437 return False;
441 * Get some configuration from pam_winbind.conf to see if we
442 * need to contact trusted domain
445 int get_pam_winbind_config()
447 int ctrl = 0;
448 struct tiniparser_dictionary *d = NULL;
450 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
451 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
454 d = tiniparser_load(opt_pam_winbind_conf);
456 if (!d) {
457 return 0;
460 if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
461 ctrl |= WINBIND_KRB5_AUTH;
464 tiniparser_freedict(d);
466 return ctrl;
469 /* Authenticate a user with a plaintext password */
471 static bool check_plaintext_auth(const char *user, const char *pass,
472 bool stdout_diagnostics)
474 struct winbindd_request request;
475 struct winbindd_response response;
476 NSS_STATUS result;
478 if (!get_require_membership_sid()) {
479 return False;
482 /* Send off request */
484 ZERO_STRUCT(request);
485 ZERO_STRUCT(response);
487 fstrcpy(request.data.auth.user, user);
488 fstrcpy(request.data.auth.pass, pass);
489 if (require_membership_of_sid) {
490 strlcpy(request.data.auth.require_membership_of_sid,
491 require_membership_of_sid,
492 sizeof(request.data.auth.require_membership_of_sid));
495 if (offline_logon) {
496 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
499 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH, &request, &response);
501 /* Display response */
503 if (stdout_diagnostics) {
504 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
505 d_fprintf(stderr, "Reading winbind reply failed! (0x01)\n");
508 d_printf("%s: %s (0x%x)\n",
509 response.data.auth.nt_status_string,
510 response.data.auth.error_string,
511 response.data.auth.nt_status);
512 } else {
513 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
514 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
517 DEBUG(3, ("%s: %s (0x%x)\n",
518 response.data.auth.nt_status_string,
519 response.data.auth.error_string,
520 response.data.auth.nt_status));
523 return (result == NSS_STATUS_SUCCESS);
526 /* authenticate a user with an encrypted username/password */
528 NTSTATUS contact_winbind_auth_crap(const char *username,
529 const char *domain,
530 const char *workstation,
531 const DATA_BLOB *challenge,
532 const DATA_BLOB *lm_response,
533 const DATA_BLOB *nt_response,
534 uint32_t flags,
535 uint32_t extra_logon_parameters,
536 uint8_t lm_key[8],
537 uint8_t user_session_key[16],
538 uint8_t *pauthoritative,
539 char **error_string,
540 char **unix_name)
542 NTSTATUS nt_status;
543 NSS_STATUS result;
544 struct winbindd_request request;
545 struct winbindd_response response;
547 *pauthoritative = 1;
549 if (!get_require_membership_sid()) {
550 return NT_STATUS_INVALID_PARAMETER;
553 ZERO_STRUCT(request);
554 ZERO_STRUCT(response);
556 request.flags = flags;
558 request.data.auth_crap.logon_parameters = extra_logon_parameters
559 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
561 if (opt_allow_mschapv2) {
562 request.data.auth_crap.logon_parameters |= MSV1_0_ALLOW_MSVCHAPV2;
565 if (require_membership_of_sid)
566 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
568 fstrcpy(request.data.auth_crap.user, username);
569 fstrcpy(request.data.auth_crap.domain, domain);
571 fstrcpy(request.data.auth_crap.workstation,
572 workstation);
574 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
576 if (lm_response && lm_response->length) {
577 memcpy(request.data.auth_crap.lm_resp,
578 lm_response->data,
579 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
580 request.data.auth_crap.lm_resp_len = lm_response->length;
583 if (nt_response && nt_response->length) {
584 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
585 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
586 request.extra_len = nt_response->length;
587 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
588 if (request.extra_data.data == NULL) {
589 return NT_STATUS_NO_MEMORY;
591 memcpy(request.extra_data.data, nt_response->data,
592 nt_response->length);
594 } else {
595 memcpy(request.data.auth_crap.nt_resp,
596 nt_response->data, nt_response->length);
598 request.data.auth_crap.nt_resp_len = nt_response->length;
601 result = winbindd_priv_request_response(
602 NULL,
603 WINBINDD_PAM_AUTH_CRAP,
604 &request,
605 &response);
606 SAFE_FREE(request.extra_data.data);
608 /* Display response */
610 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
611 nt_status = NT_STATUS_UNSUCCESSFUL;
612 if (error_string)
613 *error_string = smb_xstrdup("Reading winbind reply failed!");
614 winbindd_free_response(&response);
615 return nt_status;
618 nt_status = (NT_STATUS(response.data.auth.nt_status));
619 if (!NT_STATUS_IS_OK(nt_status)) {
620 if (error_string)
621 *error_string = smb_xstrdup(response.data.auth.error_string);
622 *pauthoritative = response.data.auth.authoritative;
623 winbindd_free_response(&response);
624 return nt_status;
627 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
628 memcpy(lm_key, response.data.auth.first_8_lm_hash,
629 sizeof(response.data.auth.first_8_lm_hash));
631 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
632 memcpy(user_session_key, response.data.auth.user_session_key,
633 sizeof(response.data.auth.user_session_key));
636 if (flags & WBFLAG_PAM_UNIX_NAME) {
637 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
638 if (!*unix_name) {
639 winbindd_free_response(&response);
640 return NT_STATUS_NO_MEMORY;
644 winbindd_free_response(&response);
645 return nt_status;
648 /* contact server to change user password using auth crap */
649 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
650 const char *domain,
651 const DATA_BLOB new_nt_pswd,
652 const DATA_BLOB old_nt_hash_enc,
653 const DATA_BLOB new_lm_pswd,
654 const DATA_BLOB old_lm_hash_enc,
655 char **error_string)
657 NTSTATUS nt_status;
658 NSS_STATUS result;
659 struct winbindd_request request;
660 struct winbindd_response response;
662 if (!get_require_membership_sid())
664 if(error_string)
665 *error_string = smb_xstrdup("Can't get membership sid.");
666 return NT_STATUS_INVALID_PARAMETER;
669 ZERO_STRUCT(request);
670 ZERO_STRUCT(response);
672 if(username != NULL)
673 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
674 if(domain != NULL)
675 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
677 if(new_nt_pswd.length)
679 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
680 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
683 if(old_nt_hash_enc.length)
685 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));
686 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
689 if(new_lm_pswd.length)
691 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
692 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
695 if(old_lm_hash_enc.length)
697 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));
698 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
701 result = winbindd_request_response(NULL, WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
703 /* Display response */
705 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
707 nt_status = NT_STATUS_UNSUCCESSFUL;
708 if (error_string)
709 *error_string = smb_xstrdup("Reading winbind reply failed!");
710 winbindd_free_response(&response);
711 return nt_status;
714 nt_status = (NT_STATUS(response.data.auth.nt_status));
715 if (!NT_STATUS_IS_OK(nt_status))
717 if (error_string)
718 *error_string = smb_xstrdup(response.data.auth.error_string);
719 winbindd_free_response(&response);
720 return nt_status;
723 winbindd_free_response(&response);
725 return nt_status;
728 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
729 TALLOC_CTX *mem_ctx,
730 void *server_returned_info,
731 const char *original_user_name,
732 uint32_t session_info_flags,
733 struct auth_session_info **session_info_out)
735 const char *unix_username = (const char *)server_returned_info;
736 struct dom_sid *sids = NULL;
737 struct auth_session_info *session_info = NULL;
739 session_info = talloc_zero(mem_ctx, struct auth_session_info);
740 if (session_info == NULL) {
741 return NT_STATUS_NO_MEMORY;
744 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
745 if (session_info->unix_info == NULL) {
746 TALLOC_FREE(session_info);
747 return NT_STATUS_NO_MEMORY;
749 session_info->unix_info->unix_name = talloc_strdup(session_info->unix_info,
750 unix_username);
751 if (session_info->unix_info->unix_name == NULL) {
752 TALLOC_FREE(session_info);
753 return NT_STATUS_NO_MEMORY;
756 session_info->security_token = talloc_zero(session_info, struct security_token);
757 if (session_info->security_token == NULL) {
758 TALLOC_FREE(session_info);
759 return NT_STATUS_NO_MEMORY;
762 sids = talloc_zero_array(session_info->security_token,
763 struct dom_sid, 3);
764 if (sids == NULL) {
765 TALLOC_FREE(session_info);
766 return NT_STATUS_NO_MEMORY;
768 sid_copy(&sids[0], &global_sid_World);
769 sid_copy(&sids[1], &global_sid_Network);
770 sid_copy(&sids[2], &global_sid_Authenticated_Users);
772 session_info->security_token->num_sids = talloc_array_length(sids);
773 session_info->security_token->sids = sids;
775 *session_info_out = session_info;
777 return NT_STATUS_OK;
780 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
781 TALLOC_CTX *mem_ctx,
782 struct smb_krb5_context *smb_krb5_context,
783 DATA_BLOB *pac_blob,
784 const char *princ_name,
785 const struct tsocket_address *remote_address,
786 uint32_t session_info_flags,
787 struct auth_session_info **session_info)
789 TALLOC_CTX *tmp_ctx;
790 struct PAC_LOGON_INFO *logon_info = NULL;
791 char *unixuser;
792 NTSTATUS status;
793 char *domain = NULL;
794 char *realm = NULL;
795 char *user = NULL;
796 char *p;
798 tmp_ctx = talloc_new(mem_ctx);
799 if (!tmp_ctx) {
800 return NT_STATUS_NO_MEMORY;
803 if (pac_blob) {
804 #ifdef HAVE_KRB5
805 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
806 NULL, NULL, 0, &logon_info);
807 #else
808 status = NT_STATUS_ACCESS_DENIED;
809 #endif
810 if (!NT_STATUS_IS_OK(status)) {
811 goto done;
815 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
817 p = strchr_m(princ_name, '@');
818 if (!p) {
819 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
820 princ_name));
821 return NT_STATUS_LOGON_FAILURE;
824 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
825 if (!user) {
826 return NT_STATUS_NO_MEMORY;
829 realm = talloc_strdup(talloc_tos(), p + 1);
830 if (!realm) {
831 return NT_STATUS_NO_MEMORY;
834 if (!strequal(realm, lp_realm())) {
835 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
836 if (!lp_allow_trusted_domains()) {
837 return NT_STATUS_LOGON_FAILURE;
841 if (logon_info && logon_info->info3.base.logon_domain.string) {
842 domain = talloc_strdup(mem_ctx,
843 logon_info->info3.base.logon_domain.string);
844 if (!domain) {
845 return NT_STATUS_NO_MEMORY;
847 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
848 } else {
850 /* If we have winbind running, we can (and must) shorten the
851 username by using the short netbios name. Otherwise we will
852 have inconsistent user names. With Kerberos, we get the
853 fully qualified realm, with ntlmssp we get the short
854 name. And even w2k3 does use ntlmssp if you for example
855 connect to an ip address. */
857 wbcErr wbc_status;
858 struct wbcDomainInfo *info = NULL;
860 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
861 realm));
863 wbc_status = wbcDomainInfo(realm, &info);
865 if (WBC_ERROR_IS_OK(wbc_status)) {
866 domain = talloc_strdup(mem_ctx,
867 info->short_name);
868 wbcFreeMemory(info);
869 } else {
870 DEBUG(3, ("Could not find short name: %s\n",
871 wbcErrorString(wbc_status)));
872 domain = talloc_strdup(mem_ctx, realm);
874 if (!domain) {
875 return NT_STATUS_NO_MEMORY;
877 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
880 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
881 if (!unixuser) {
882 status = NT_STATUS_NO_MEMORY;
883 goto done;
886 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
888 done:
889 TALLOC_FREE(tmp_ctx);
890 return status;
896 * Return the challenge as determined by the authentication subsystem
897 * @return an 8 byte random challenge
900 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
901 uint8_t chal[8])
903 if (auth_ctx->challenge.data.length == 8) {
904 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
905 auth_ctx->challenge.set_by));
906 memcpy(chal, auth_ctx->challenge.data.data, 8);
907 return NT_STATUS_OK;
910 if (!auth_ctx->challenge.set_by) {
911 generate_random_buffer(chal, 8);
913 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
914 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
915 auth_ctx->challenge.set_by = "random";
918 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
919 auth_ctx->challenge.set_by));
921 return NT_STATUS_OK;
925 * NTLM2 authentication modifies the effective challenge,
926 * @param challenge The new challenge value
928 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
930 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
931 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
933 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
934 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
936 return NT_STATUS_OK;
940 * Check the password on an NTLMSSP login.
942 * Return the session keys used on the connection.
945 struct winbind_pw_check_state {
946 uint8_t authoritative;
947 void *server_info;
948 DATA_BLOB nt_session_key;
949 DATA_BLOB lm_session_key;
952 static struct tevent_req *winbind_pw_check_send(
953 TALLOC_CTX *mem_ctx,
954 struct tevent_context *ev,
955 struct auth4_context *auth4_context,
956 const struct auth_usersupplied_info *user_info)
958 struct tevent_req *req = NULL;
959 struct winbind_pw_check_state *state = NULL;
960 NTSTATUS nt_status;
961 char *error_string = NULL;
962 uint8_t lm_key[8];
963 uint8_t user_sess_key[16];
964 char *unix_name = NULL;
966 req = tevent_req_create(
967 mem_ctx, &state, struct winbind_pw_check_state);
968 if (req == NULL) {
969 return NULL;
972 nt_status = contact_winbind_auth_crap(
973 user_info->client.account_name,
974 user_info->client.domain_name,
975 user_info->workstation_name,
976 &auth4_context->challenge.data,
977 &user_info->password.response.lanman,
978 &user_info->password.response.nt,
979 WBFLAG_PAM_LMKEY |
980 WBFLAG_PAM_USER_SESSION_KEY |
981 WBFLAG_PAM_UNIX_NAME,
983 lm_key, user_sess_key,
984 &state->authoritative,
985 &error_string,
986 &unix_name);
988 if (tevent_req_nterror(req, nt_status)) {
989 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
990 DBG_ERR("Login for user [%s]\\[%s]@[%s] failed due "
991 "to [%s]\n",
992 user_info->client.domain_name,
993 user_info->client.account_name,
994 user_info->workstation_name,
995 error_string ?
996 error_string :
997 "unknown error (NULL)");
998 } else {
999 DBG_NOTICE("Login for user [%s]\\[%s]@[%s] failed due "
1000 "to [%s]\n",
1001 user_info->client.domain_name,
1002 user_info->client.account_name,
1003 user_info->workstation_name,
1004 error_string ?
1005 error_string :
1006 "unknown error (NULL)");
1008 goto done;
1011 if (!all_zero(lm_key, 8)) {
1012 state->lm_session_key = data_blob_talloc(state, NULL, 16);
1013 if (tevent_req_nomem(state->lm_session_key.data, req)) {
1014 goto done;
1016 memcpy(state->lm_session_key.data, lm_key, 8);
1017 memset(state->lm_session_key.data+8, '\0', 8);
1019 if (!all_zero(user_sess_key, 16)) {
1020 state->nt_session_key = data_blob_talloc(
1021 state, user_sess_key, 16);
1022 if (tevent_req_nomem(state->nt_session_key.data, req)) {
1023 goto done;
1026 state->server_info = talloc_strdup(state, unix_name);
1027 if (tevent_req_nomem(state->server_info, req)) {
1028 goto done;
1030 tevent_req_done(req);
1032 done:
1033 SAFE_FREE(error_string);
1034 SAFE_FREE(unix_name);
1035 return tevent_req_post(req, ev);
1038 static NTSTATUS winbind_pw_check_recv(struct tevent_req *req,
1039 TALLOC_CTX *mem_ctx,
1040 uint8_t *pauthoritative,
1041 void **server_returned_info,
1042 DATA_BLOB *nt_session_key,
1043 DATA_BLOB *lm_session_key)
1045 struct winbind_pw_check_state *state = tevent_req_data(
1046 req, struct winbind_pw_check_state);
1047 NTSTATUS status;
1049 if (pauthoritative != NULL) {
1050 *pauthoritative = state->authoritative;
1053 if (tevent_req_is_nterror(req, &status)) {
1054 return status;
1057 if (server_returned_info != NULL) {
1058 *server_returned_info = talloc_move(
1059 mem_ctx, &state->server_info);
1061 if (nt_session_key != NULL) {
1062 *nt_session_key = (DATA_BLOB) {
1063 .data = talloc_move(
1064 mem_ctx, &state->nt_session_key.data),
1065 .length = state->nt_session_key.length,
1068 if (lm_session_key != NULL) {
1069 *lm_session_key = (DATA_BLOB) {
1070 .data = talloc_move(
1071 mem_ctx, &state->lm_session_key.data),
1072 .length = state->lm_session_key.length,
1076 return NT_STATUS_OK;
1079 struct local_pw_check_state {
1080 uint8_t authoritative;
1081 void *server_info;
1082 DATA_BLOB nt_session_key;
1083 DATA_BLOB lm_session_key;
1086 static struct tevent_req *local_pw_check_send(
1087 TALLOC_CTX *mem_ctx,
1088 struct tevent_context *ev,
1089 struct auth4_context *auth4_context,
1090 const struct auth_usersupplied_info *user_info)
1092 struct tevent_req *req = NULL;
1093 struct local_pw_check_state *state = NULL;
1094 struct samr_Password lm_pw, nt_pw;
1095 NTSTATUS nt_status;
1097 req = tevent_req_create(
1098 mem_ctx, &state, struct local_pw_check_state);
1099 if (req == NULL) {
1100 return NULL;
1102 state->authoritative = 1;
1104 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1106 nt_status = ntlm_password_check(
1107 state,
1108 true,
1109 NTLM_AUTH_ON,
1111 &auth4_context->challenge.data,
1112 &user_info->password.response.lanman,
1113 &user_info->password.response.nt,
1114 user_info->client.account_name,
1115 user_info->client.account_name,
1116 user_info->client.domain_name,
1117 &lm_pw,
1118 &nt_pw,
1119 &state->nt_session_key,
1120 &state->lm_session_key);
1122 if (tevent_req_nterror(req, nt_status)) {
1123 DBG_NOTICE("Login for user [%s]\\[%s]@[%s] failed due to "
1124 "[%s]\n",
1125 user_info->client.domain_name,
1126 user_info->client.account_name,
1127 user_info->workstation_name,
1128 nt_errstr(nt_status));
1129 return tevent_req_post(req, ev);
1132 state->server_info = talloc_asprintf(
1133 state,
1134 "%s%c%s",
1135 user_info->client.domain_name,
1136 *lp_winbind_separator(),
1137 user_info->client.account_name);
1138 if (tevent_req_nomem(state->server_info, req)) {
1139 return tevent_req_post(req, ev);
1142 tevent_req_done(req);
1143 return tevent_req_post(req, ev);
1146 static NTSTATUS local_pw_check_recv(struct tevent_req *req,
1147 TALLOC_CTX *mem_ctx,
1148 uint8_t *pauthoritative,
1149 void **server_returned_info,
1150 DATA_BLOB *nt_session_key,
1151 DATA_BLOB *lm_session_key)
1153 struct local_pw_check_state *state = tevent_req_data(
1154 req, struct local_pw_check_state);
1155 NTSTATUS status;
1157 if (pauthoritative != NULL) {
1158 *pauthoritative = state->authoritative;
1161 if (tevent_req_is_nterror(req, &status)) {
1162 return status;
1165 if (server_returned_info != NULL) {
1166 *server_returned_info = talloc_move(
1167 mem_ctx, &state->server_info);
1169 if (nt_session_key != NULL) {
1170 *nt_session_key = (DATA_BLOB) {
1171 .data = talloc_move(
1172 mem_ctx, &state->nt_session_key.data),
1173 .length = state->nt_session_key.length,
1176 if (lm_session_key != NULL) {
1177 *lm_session_key = (DATA_BLOB) {
1178 .data = talloc_move(
1179 mem_ctx, &state->lm_session_key.data),
1180 .length = state->lm_session_key.length,
1184 return NT_STATUS_OK;
1187 static NTSTATUS ntlm_auth_prepare_gensec_client(TALLOC_CTX *mem_ctx,
1188 struct loadparm_context *lp_ctx,
1189 struct gensec_security **gensec_security_out)
1191 struct gensec_security *gensec_security = NULL;
1192 NTSTATUS nt_status;
1193 TALLOC_CTX *tmp_ctx;
1194 const struct gensec_security_ops **backends = NULL;
1195 struct gensec_settings *gensec_settings = NULL;
1196 size_t idx = 0;
1198 tmp_ctx = talloc_new(mem_ctx);
1199 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1201 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1202 if (gensec_settings == NULL) {
1203 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1204 TALLOC_FREE(tmp_ctx);
1205 return NT_STATUS_NO_MEMORY;
1208 backends = talloc_zero_array(gensec_settings,
1209 const struct gensec_security_ops *, 4);
1210 if (backends == NULL) {
1211 TALLOC_FREE(tmp_ctx);
1212 return NT_STATUS_NO_MEMORY;
1214 gensec_settings->backends = backends;
1216 gensec_init();
1218 /* These need to be in priority order, krb5 before NTLMSSP */
1219 #if defined(HAVE_KRB5)
1220 backends[idx++] = &gensec_gse_krb5_security_ops;
1221 #endif
1223 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1225 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1227 nt_status = gensec_client_start(NULL, &gensec_security,
1228 gensec_settings);
1229 if (!NT_STATUS_IS_OK(nt_status)) {
1230 TALLOC_FREE(tmp_ctx);
1231 return nt_status;
1234 talloc_unlink(tmp_ctx, gensec_settings);
1236 if (opt_target_service != NULL) {
1237 nt_status = gensec_set_target_service(gensec_security,
1238 opt_target_service);
1239 if (!NT_STATUS_IS_OK(nt_status)) {
1240 TALLOC_FREE(tmp_ctx);
1241 return nt_status;
1245 if (opt_target_hostname != NULL) {
1246 nt_status = gensec_set_target_hostname(gensec_security,
1247 opt_target_hostname);
1248 if (!NT_STATUS_IS_OK(nt_status)) {
1249 TALLOC_FREE(tmp_ctx);
1250 return nt_status;
1254 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1255 TALLOC_FREE(tmp_ctx);
1256 return NT_STATUS_OK;
1259 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1261 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1262 if (auth4_context == NULL) {
1263 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1264 return NULL;
1266 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1267 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1268 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1269 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1270 if (local_pw) {
1271 auth4_context->check_ntlm_password_send = local_pw_check_send;
1272 auth4_context->check_ntlm_password_recv = local_pw_check_recv;
1273 } else {
1274 auth4_context->check_ntlm_password_send =
1275 winbind_pw_check_send;
1276 auth4_context->check_ntlm_password_recv =
1277 winbind_pw_check_recv;
1279 auth4_context->private_data = NULL;
1280 return auth4_context;
1283 static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx,
1284 struct loadparm_context *lp_ctx,
1285 struct gensec_security **gensec_security_out)
1287 struct gensec_security *gensec_security;
1288 NTSTATUS nt_status;
1290 TALLOC_CTX *tmp_ctx;
1291 const struct gensec_security_ops **backends;
1292 struct gensec_settings *gensec_settings;
1293 size_t idx = 0;
1294 struct cli_credentials *server_credentials;
1296 struct auth4_context *auth4_context;
1298 tmp_ctx = talloc_new(mem_ctx);
1299 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1301 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1302 if (auth4_context == NULL) {
1303 TALLOC_FREE(tmp_ctx);
1304 return NT_STATUS_NO_MEMORY;
1307 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1308 if (lp_ctx == NULL) {
1309 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1310 TALLOC_FREE(tmp_ctx);
1311 return NT_STATUS_NO_MEMORY;
1315 * This should be a 'netbios domain -> DNS domain'
1316 * mapping, and can currently validly return NULL on
1317 * poorly configured systems.
1319 * This is used for the NTLMSSP server
1322 if (opt_password) {
1323 gensec_settings->server_netbios_name = lp_netbios_name();
1324 gensec_settings->server_netbios_domain = lp_workgroup();
1325 } else {
1326 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1327 gensec_settings->server_netbios_domain = get_winbind_domain();
1330 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1331 get_mydnsdomname(talloc_tos()));
1332 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1333 get_mydnsfullname());
1335 backends = talloc_zero_array(gensec_settings,
1336 const struct gensec_security_ops *, 4);
1338 if (backends == NULL) {
1339 TALLOC_FREE(tmp_ctx);
1340 return NT_STATUS_NO_MEMORY;
1342 gensec_settings->backends = backends;
1344 gensec_init();
1346 /* These need to be in priority order, krb5 before NTLMSSP */
1347 #if defined(HAVE_KRB5)
1348 backends[idx++] = &gensec_gse_krb5_security_ops;
1349 #endif
1351 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1353 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1356 * This is anonymous for now, because we just use it
1357 * to set the kerberos state at the moment
1359 server_credentials = cli_credentials_init_anon(tmp_ctx);
1360 if (!server_credentials) {
1361 DBG_ERR("Failed to init server credentials\n");
1362 return NT_STATUS_NO_MEMORY;
1365 cli_credentials_set_conf(server_credentials, lp_ctx);
1367 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1368 cli_credentials_set_kerberos_state(server_credentials,
1369 CRED_USE_KERBEROS_DESIRED,
1370 CRED_SPECIFIED);
1371 } else {
1372 cli_credentials_set_kerberos_state(server_credentials,
1373 CRED_USE_KERBEROS_DISABLED,
1374 CRED_SPECIFIED);
1377 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1378 auth4_context, &gensec_security);
1380 if (!NT_STATUS_IS_OK(nt_status)) {
1381 TALLOC_FREE(tmp_ctx);
1382 return nt_status;
1385 gensec_set_credentials(gensec_security, server_credentials);
1388 * TODO: Allow the caller to pass their own description here
1389 * via a command-line option
1391 nt_status = gensec_set_target_service_description(gensec_security,
1392 "ntlm_auth");
1393 if (!NT_STATUS_IS_OK(nt_status)) {
1394 TALLOC_FREE(tmp_ctx);
1395 return nt_status;
1398 talloc_unlink(tmp_ctx, lp_ctx);
1399 talloc_unlink(tmp_ctx, server_credentials);
1400 talloc_unlink(tmp_ctx, gensec_settings);
1401 talloc_unlink(tmp_ctx, auth4_context);
1403 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1404 TALLOC_FREE(tmp_ctx);
1405 return NT_STATUS_OK;
1408 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1409 struct loadparm_context *lp_ctx,
1410 struct ntlm_auth_state *state,
1411 char *buf, int length, void **private2)
1413 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1414 return;
1417 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1418 struct loadparm_context *lp_ctx,
1419 struct ntlm_auth_state *state,
1420 char *buf, int length, void **private2)
1422 char *user, *pass;
1423 user=buf;
1425 pass=(char *)memchr(buf,' ',length);
1426 if (!pass) {
1427 DEBUG(2, ("Password not found. Denying access\n"));
1428 printf("ERR\n");
1429 return;
1431 *pass='\0';
1432 pass++;
1434 if (state->helper_mode == SQUID_2_5_BASIC) {
1435 char *end = rfc1738_unescape(user);
1436 if (end == NULL || (end - user) != strlen(user)) {
1437 DEBUG(2, ("Badly rfc1738 encoded username: %s; "
1438 "denying access\n", user));
1439 printf("ERR\n");
1440 return;
1442 end = rfc1738_unescape(pass);
1443 if (end == NULL || (end - pass) != strlen(pass)) {
1444 DEBUG(2, ("Badly encoded password for %s; "
1445 "denying access\n", user));
1446 printf("ERR\n");
1447 return;
1451 if (check_plaintext_auth(user, pass, False)) {
1452 printf("OK\n");
1453 } else {
1454 printf("ERR\n");
1458 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1459 struct loadparm_context *lp_ctx,
1460 char *buf, int length, void **private1)
1462 DATA_BLOB in;
1463 DATA_BLOB out = data_blob(NULL, 0);
1464 char *out_base64 = NULL;
1465 const char *reply_arg = NULL;
1466 struct gensec_ntlm_state {
1467 struct gensec_security *gensec_state;
1468 const char *set_password;
1470 struct gensec_ntlm_state *state;
1472 NTSTATUS nt_status;
1473 bool first = false;
1474 const char *reply_code;
1475 struct cli_credentials *creds;
1477 static char *want_feature_list = NULL;
1478 static DATA_BLOB session_key;
1480 TALLOC_CTX *mem_ctx;
1482 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1483 if (mem_ctx == NULL) {
1484 printf("BH No Memory\n");
1485 exit(1);
1488 if (*private1) {
1489 state = talloc_get_type(*private1, struct gensec_ntlm_state);
1490 if (state == NULL) {
1491 DBG_WARNING("*private1 is of type %s\n",
1492 talloc_get_name(*private1));
1493 printf("BH *private1 is of type %s\n",
1494 talloc_get_name(*private1));
1495 exit(1);
1497 } else {
1498 state = talloc_zero(NULL, struct gensec_ntlm_state);
1499 if (!state) {
1500 printf("BH No Memory\n");
1501 exit(1);
1503 *private1 = state;
1504 if (opt_password) {
1505 state->set_password = opt_password;
1509 if (strlen(buf) < 2) {
1510 DEBUG(1, ("query [%s] invalid", buf));
1511 printf("BH Query invalid\n");
1512 talloc_free(mem_ctx);
1513 return;
1516 if (strlen(buf) > 3) {
1517 if(strncmp(buf, "SF ", 3) == 0) {
1518 DEBUG(10, ("Setting flags to negotiate\n"));
1519 talloc_free(want_feature_list);
1520 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1521 printf("OK\n");
1522 talloc_free(mem_ctx);
1523 return;
1525 in = base64_decode_data_blob_talloc(mem_ctx, buf + 3);
1526 } else {
1527 in = data_blob(NULL, 0);
1530 if (strncmp(buf, "YR", 2) == 0) {
1531 if (state->gensec_state) {
1532 talloc_free(state->gensec_state);
1533 state->gensec_state = NULL;
1535 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1536 /* Just return BH, like ntlm_auth from Samba 3 does. */
1537 printf("BH Command expected\n");
1538 talloc_free(mem_ctx);
1539 return;
1540 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1541 (strncmp(buf, "KK ", 3) != 0) &&
1542 (strncmp(buf, "AF ", 3) != 0) &&
1543 (strncmp(buf, "NA ", 3) != 0) &&
1544 (strncmp(buf, "UG", 2) != 0) &&
1545 (strncmp(buf, "PW ", 3) != 0) &&
1546 (strncmp(buf, "GK", 2) != 0) &&
1547 (strncmp(buf, "GF", 2) != 0)) {
1548 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1549 printf("BH SPNEGO request invalid prefix\n");
1550 talloc_free(mem_ctx);
1551 return;
1554 /* setup gensec */
1555 if (!(state->gensec_state)) {
1556 switch (stdio_helper_mode) {
1557 case GSS_SPNEGO_CLIENT:
1559 * cached credentials are only supported by
1560 * NTLMSSP_CLIENT_1 for now.
1562 use_cached_creds = false;
1563 FALL_THROUGH;
1564 case NTLMSSP_CLIENT_1:
1565 /* setup the client side */
1567 if (state->set_password != NULL) {
1568 use_cached_creds = false;
1571 if (use_cached_creds) {
1572 struct wbcCredentialCacheParams params;
1573 struct wbcCredentialCacheInfo *info = NULL;
1574 struct wbcAuthErrorInfo *error = NULL;
1575 wbcErr wbc_status;
1577 params.account_name = opt_username;
1578 params.domain_name = opt_domain;
1579 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1580 params.num_blobs = 0;
1581 params.blobs = NULL;
1583 wbc_status = wbcCredentialCache(&params, &info,
1584 &error);
1585 wbcFreeMemory(error);
1586 if (!WBC_ERROR_IS_OK(wbc_status)) {
1587 use_cached_creds = false;
1589 wbcFreeMemory(info);
1592 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1593 &state->gensec_state);
1594 if (!NT_STATUS_IS_OK(nt_status)) {
1595 printf("BH GENSEC mech failed to start: %s\n",
1596 nt_errstr(nt_status));
1597 talloc_free(mem_ctx);
1598 return;
1601 creds = cli_credentials_init(state->gensec_state);
1602 cli_credentials_set_conf(creds, lp_ctx);
1603 if (opt_username) {
1604 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1606 if (opt_domain) {
1607 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1609 if (use_cached_creds) {
1610 gensec_want_feature(state->gensec_state,
1611 GENSEC_FEATURE_NTLM_CCACHE);
1612 } else if (state->set_password) {
1613 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1614 } else {
1615 cli_credentials_set_password_callback(creds, get_password);
1617 if (opt_workstation) {
1618 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1621 gensec_set_credentials(state->gensec_state, creds);
1623 break;
1624 case GSS_SPNEGO_SERVER:
1625 case SQUID_2_5_NTLMSSP:
1627 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1628 &state->gensec_state);
1629 if (!NT_STATUS_IS_OK(nt_status)) {
1630 printf("BH GENSEC mech failed to start: %s\n",
1631 nt_errstr(nt_status));
1632 talloc_free(mem_ctx);
1633 return;
1635 break;
1637 default:
1638 talloc_free(mem_ctx);
1639 abort();
1642 gensec_want_feature_list(state->gensec_state, want_feature_list);
1644 /* Session info is not complete, do not pass to auth log */
1645 gensec_want_feature(state->gensec_state, GENSEC_FEATURE_NO_AUTHZ_LOG);
1647 switch (stdio_helper_mode) {
1648 case GSS_SPNEGO_CLIENT:
1649 case GSS_SPNEGO_SERVER:
1650 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1651 if (!in.length) {
1652 first = true;
1654 break;
1655 case NTLMSSP_CLIENT_1:
1656 if (!in.length) {
1657 first = true;
1659 FALL_THROUGH;
1660 case SQUID_2_5_NTLMSSP:
1661 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1662 break;
1663 default:
1664 talloc_free(mem_ctx);
1665 abort();
1668 if (!NT_STATUS_IS_OK(nt_status)) {
1669 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1670 printf("BH GENSEC mech failed to start\n");
1671 talloc_free(mem_ctx);
1672 return;
1677 /* update */
1679 if (strncmp(buf, "PW ", 3) == 0) {
1680 state->set_password = talloc_strndup(state,
1681 (const char *)in.data,
1682 in.length);
1684 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1685 state->set_password,
1686 CRED_SPECIFIED);
1687 printf("OK\n");
1688 talloc_free(mem_ctx);
1689 return;
1692 if (strncmp(buf, "GK", 2) == 0) {
1693 char *base64_key;
1694 DEBUG(10, ("Requested session key\n"));
1695 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1696 if(!NT_STATUS_IS_OK(nt_status)) {
1697 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1698 printf("BH No session key\n");
1699 talloc_free(mem_ctx);
1700 return;
1701 } else {
1702 base64_key = base64_encode_data_blob(state, session_key);
1703 SMB_ASSERT(base64_key != NULL);
1704 printf("GK %s\n", base64_key);
1705 talloc_free(base64_key);
1707 talloc_free(mem_ctx);
1708 return;
1711 if (strncmp(buf, "GF", 2) == 0) {
1712 uint32_t neg_flags;
1714 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1716 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1717 if (neg_flags == 0) {
1718 printf("BH\n");
1719 talloc_free(mem_ctx);
1720 return;
1723 printf("GF 0x%08x\n", neg_flags);
1724 talloc_free(mem_ctx);
1725 return;
1728 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1730 /* don't leak 'bad password'/'no such user' info to the network client */
1731 nt_status = nt_status_squash(nt_status);
1733 if (out.length) {
1734 out_base64 = base64_encode_data_blob(mem_ctx, out);
1735 SMB_ASSERT(out_base64 != NULL);
1736 } else {
1737 out_base64 = NULL;
1740 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1741 reply_arg = "*";
1742 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1743 reply_code = "YR";
1744 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1745 reply_code = "KK";
1746 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1747 reply_code = "TT";
1748 } else {
1749 abort();
1753 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1754 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1755 reply_arg = nt_errstr(nt_status);
1756 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1757 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1758 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1759 reply_arg = nt_errstr(nt_status);
1760 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1761 } else if (!NT_STATUS_IS_OK(nt_status)) {
1762 reply_code = "NA";
1763 reply_arg = nt_errstr(nt_status);
1764 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1765 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1766 struct auth_session_info *session_info;
1768 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1769 if (!NT_STATUS_IS_OK(nt_status)) {
1770 reply_code = "BH Failed to retrieve session info";
1771 reply_arg = nt_errstr(nt_status);
1772 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1773 } else {
1775 reply_code = "AF";
1776 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1777 if (reply_arg == NULL) {
1778 reply_code = "BH out of memory";
1779 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1781 talloc_free(session_info);
1783 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1784 reply_code = "AF";
1785 reply_arg = out_base64;
1786 } else {
1787 abort();
1790 switch (stdio_helper_mode) {
1791 case GSS_SPNEGO_SERVER:
1792 printf("%s %s %s\n", reply_code,
1793 out_base64 ? out_base64 : "*",
1794 reply_arg ? reply_arg : "*");
1795 break;
1796 default:
1797 if (out_base64) {
1798 printf("%s %s\n", reply_code, out_base64);
1799 } else if (reply_arg) {
1800 printf("%s %s\n", reply_code, reply_arg);
1801 } else {
1802 printf("%s\n", reply_code);
1806 talloc_free(mem_ctx);
1807 return;
1810 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1811 struct loadparm_context *lp_ctx,
1812 struct ntlm_auth_state *state,
1813 char *buf, int length, void **private2)
1815 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1816 return;
1819 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1820 struct loadparm_context *lp_ctx,
1821 struct ntlm_auth_state *state,
1822 char *buf, int length, void **private2)
1824 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1825 return;
1828 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1829 struct loadparm_context *lp_ctx,
1830 struct ntlm_auth_state *state,
1831 char *buf, int length, void **private2)
1833 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1834 return;
1837 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1838 struct loadparm_context *lp_ctx,
1839 struct ntlm_auth_state *state,
1840 char *buf, int length, void **private2)
1842 char *request, *parameter;
1843 static DATA_BLOB challenge;
1844 static DATA_BLOB lm_response;
1845 static DATA_BLOB nt_response;
1846 static char *full_username;
1847 static char *username;
1848 static char *domain;
1849 static char *plaintext_password;
1850 static bool ntlm_server_1_user_session_key;
1851 static bool ntlm_server_1_lm_session_key;
1853 if (strequal(buf, ".")) {
1854 if (!full_username && !username) {
1855 printf("Error: No username supplied!\n");
1856 } else if (plaintext_password) {
1857 /* handle this request as plaintext */
1858 if (!full_username) {
1859 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1860 printf("Error: Out of memory in "
1861 "asprintf!\n.\n");
1862 return;
1865 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1866 printf("Authenticated: Yes\n");
1867 } else {
1868 printf("Authenticated: No\n");
1870 } else if (!lm_response.data && !nt_response.data) {
1871 printf("Error: No password supplied!\n");
1872 } else if (!challenge.data) {
1873 printf("Error: No lanman-challenge supplied!\n");
1874 } else {
1875 char *error_string = NULL;
1876 uchar lm_key[8];
1877 uchar user_session_key[16];
1878 uint32_t flags = 0;
1879 NTSTATUS nt_status;
1880 if (full_username && !username) {
1881 fstring fstr_user;
1882 fstring fstr_domain;
1884 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1885 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1886 printf("Error: Could not parse into "
1887 "domain and username\n");
1889 SAFE_FREE(username);
1890 SAFE_FREE(domain);
1891 username = smb_xstrdup(fstr_user);
1892 domain = smb_xstrdup(fstr_domain);
1895 if (opt_password) {
1896 DATA_BLOB nt_session_key, lm_session_key;
1897 struct samr_Password lm_pw, nt_pw;
1898 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1899 ZERO_STRUCT(user_session_key);
1900 ZERO_STRUCT(lm_key);
1902 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1903 nt_status = ntlm_password_check(mem_ctx,
1904 true,
1905 NTLM_AUTH_ON,
1907 &challenge,
1908 &lm_response,
1909 &nt_response,
1910 username,
1911 username,
1912 domain,
1913 &lm_pw, &nt_pw,
1914 &nt_session_key,
1915 &lm_session_key);
1916 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1917 if (ntlm_server_1_user_session_key) {
1918 if (nt_session_key.length == sizeof(user_session_key)) {
1919 memcpy(user_session_key,
1920 nt_session_key.data,
1921 sizeof(user_session_key));
1924 if (ntlm_server_1_lm_session_key) {
1925 if (lm_session_key.length == sizeof(lm_key)) {
1926 memcpy(lm_key,
1927 lm_session_key.data,
1928 sizeof(lm_key));
1931 TALLOC_FREE(mem_ctx);
1933 } else {
1934 uint8_t authoritative = 0;
1936 if (!domain) {
1937 domain = smb_xstrdup(get_winbind_domain());
1940 if (ntlm_server_1_lm_session_key)
1941 flags |= WBFLAG_PAM_LMKEY;
1943 if (ntlm_server_1_user_session_key)
1944 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1946 nt_status = contact_winbind_auth_crap(username,
1947 domain,
1948 lp_netbios_name(),
1949 &challenge,
1950 &lm_response,
1951 &nt_response,
1952 flags, 0,
1953 lm_key,
1954 user_session_key,
1955 &authoritative,
1956 &error_string,
1957 NULL);
1960 if (!NT_STATUS_IS_OK(nt_status)) {
1961 printf("Authenticated: No\n");
1962 printf("Authentication-Error: %s\n.\n",
1963 error_string);
1964 } else {
1965 char *hex_lm_key;
1966 char *hex_user_session_key;
1968 printf("Authenticated: Yes\n");
1970 if (ntlm_server_1_lm_session_key
1971 && (!all_zero(lm_key,
1972 sizeof(lm_key)))) {
1973 hex_lm_key = hex_encode_talloc(NULL,
1974 (const unsigned char *)lm_key,
1975 sizeof(lm_key));
1976 printf("LANMAN-Session-Key: %s\n",
1977 hex_lm_key);
1978 TALLOC_FREE(hex_lm_key);
1981 if (ntlm_server_1_user_session_key
1982 && (!all_zero(user_session_key,
1983 sizeof(user_session_key)))) {
1984 hex_user_session_key = hex_encode_talloc(NULL,
1985 (const unsigned char *)user_session_key,
1986 sizeof(user_session_key));
1987 printf("User-Session-Key: %s\n",
1988 hex_user_session_key);
1989 TALLOC_FREE(hex_user_session_key);
1992 SAFE_FREE(error_string);
1994 /* clear out the state */
1995 challenge = data_blob_null;
1996 nt_response = data_blob_null;
1997 lm_response = data_blob_null;
1998 SAFE_FREE(full_username);
1999 SAFE_FREE(username);
2000 SAFE_FREE(domain);
2001 SAFE_FREE(plaintext_password);
2002 ntlm_server_1_user_session_key = False;
2003 ntlm_server_1_lm_session_key = False;
2004 printf(".\n");
2006 return;
2009 request = buf;
2011 /* Indicates a base64 encoded structure */
2012 parameter = strstr_m(request, ":: ");
2013 if (!parameter) {
2014 parameter = strstr_m(request, ": ");
2016 if (!parameter) {
2017 DEBUG(0, ("Parameter not found!\n"));
2018 printf("Error: Parameter not found!\n.\n");
2019 return;
2022 parameter[0] ='\0';
2023 parameter++;
2024 parameter[0] ='\0';
2025 parameter++;
2027 } else {
2028 parameter[0] ='\0';
2029 parameter++;
2030 parameter[0] ='\0';
2031 parameter++;
2032 parameter[0] ='\0';
2033 parameter++;
2035 base64_decode_inplace(parameter);
2038 if (strequal(request, "LANMAN-Challenge")) {
2039 challenge = strhex_to_data_blob(NULL, parameter);
2040 if (challenge.length != 8) {
2041 printf("Error: hex decode of %s failed! "
2042 "(got %d bytes, expected 8)\n.\n",
2043 parameter,
2044 (int)challenge.length);
2045 challenge = data_blob_null;
2047 } else if (strequal(request, "NT-Response")) {
2048 nt_response = strhex_to_data_blob(NULL, parameter);
2049 if (nt_response.length < 24) {
2050 printf("Error: hex decode of %s failed! "
2051 "(only got %d bytes, needed at least 24)\n.\n",
2052 parameter,
2053 (int)nt_response.length);
2054 nt_response = data_blob_null;
2056 } else if (strequal(request, "LANMAN-Response")) {
2057 lm_response = strhex_to_data_blob(NULL, parameter);
2058 if (lm_response.length != 24) {
2059 printf("Error: hex decode of %s failed! "
2060 "(got %d bytes, expected 24)\n.\n",
2061 parameter,
2062 (int)lm_response.length);
2063 lm_response = data_blob_null;
2065 } else if (strequal(request, "Password")) {
2066 plaintext_password = smb_xstrdup(parameter);
2067 } else if (strequal(request, "NT-Domain")) {
2068 domain = smb_xstrdup(parameter);
2069 } else if (strequal(request, "Username")) {
2070 username = smb_xstrdup(parameter);
2071 } else if (strequal(request, "Full-Username")) {
2072 full_username = smb_xstrdup(parameter);
2073 } else if (strequal(request, "Request-User-Session-Key")) {
2074 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2075 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2076 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2077 } else {
2078 printf("Error: Unknown request %s\n.\n", request);
2082 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2083 struct loadparm_context *lp_ctx,
2084 struct ntlm_auth_state *state,
2085 char *buf, int length, void **private2)
2087 char *request, *parameter;
2088 static DATA_BLOB new_nt_pswd;
2089 static DATA_BLOB old_nt_hash_enc;
2090 static DATA_BLOB new_lm_pswd;
2091 static DATA_BLOB old_lm_hash_enc;
2092 static char *full_username = NULL;
2093 static char *username = NULL;
2094 static char *domain = NULL;
2095 static char *newpswd = NULL;
2096 static char *oldpswd = NULL;
2098 if (strequal(buf, ".")) {
2099 if(newpswd && oldpswd) {
2100 uchar old_nt_hash[16];
2101 uchar old_lm_hash[16];
2102 uchar new_nt_hash[16];
2103 uchar new_lm_hash[16];
2105 gnutls_cipher_hd_t cipher_hnd = NULL;
2106 gnutls_datum_t old_nt_key = {
2107 .data = old_nt_hash,
2108 .size = sizeof(old_nt_hash),
2110 int rc;
2112 new_nt_pswd = data_blob(NULL, 516);
2113 old_nt_hash_enc = data_blob(NULL, 16);
2115 /* Calculate the MD4 hash (NT compatible) of the
2116 * password */
2117 E_md4hash(oldpswd, old_nt_hash);
2118 E_md4hash(newpswd, new_nt_hash);
2120 /* E_deshash returns false for 'long'
2121 passwords (> 14 DOS chars).
2123 Therefore, don't send a buffer
2124 encrypted with the truncated hash
2125 (it could allow an even easier
2126 attack on the password)
2128 Likewise, obey the admin's restriction
2131 rc = gnutls_cipher_init(&cipher_hnd,
2132 GNUTLS_CIPHER_ARCFOUR_128,
2133 &old_nt_key,
2134 NULL);
2135 if (rc < 0) {
2136 DBG_ERR("gnutls_cipher_init failed: %s\n",
2137 gnutls_strerror(rc));
2138 if (rc == GNUTLS_E_UNWANTED_ALGORITHM) {
2139 DBG_ERR("Running in FIPS mode, NTLM blocked\n");
2141 return;
2144 if (lp_client_lanman_auth() &&
2145 E_deshash(newpswd, new_lm_hash) &&
2146 E_deshash(oldpswd, old_lm_hash)) {
2147 new_lm_pswd = data_blob(NULL, 516);
2148 old_lm_hash_enc = data_blob(NULL, 16);
2149 encode_pw_buffer(new_lm_pswd.data, newpswd,
2150 STR_UNICODE);
2152 rc = gnutls_cipher_encrypt(cipher_hnd,
2153 new_lm_pswd.data,
2154 516);
2155 if (rc < 0) {
2156 gnutls_cipher_deinit(cipher_hnd);
2157 return;
2159 rc = E_old_pw_hash(new_nt_hash, old_lm_hash,
2160 old_lm_hash_enc.data);
2161 if (rc != 0) {
2162 DBG_ERR("E_old_pw_hash failed: %s\n",
2163 gnutls_strerror(rc));
2164 return;
2166 } else {
2167 new_lm_pswd.data = NULL;
2168 new_lm_pswd.length = 0;
2169 old_lm_hash_enc.data = NULL;
2170 old_lm_hash_enc.length = 0;
2173 encode_pw_buffer(new_nt_pswd.data, newpswd,
2174 STR_UNICODE);
2176 rc = gnutls_cipher_encrypt(cipher_hnd,
2177 new_nt_pswd.data,
2178 516);
2179 gnutls_cipher_deinit(cipher_hnd);
2180 if (rc < 0) {
2181 return;
2183 rc = E_old_pw_hash(new_nt_hash, old_nt_hash,
2184 old_nt_hash_enc.data);
2185 if (rc != 0) {
2186 DBG_ERR("E_old_pw_hash failed: %s\n",
2187 gnutls_strerror(rc));
2188 return;
2191 ZERO_ARRAY(old_nt_hash);
2192 ZERO_ARRAY(old_lm_hash);
2193 ZERO_ARRAY(new_nt_hash);
2194 ZERO_ARRAY(new_lm_hash);
2197 if (!full_username && !username) {
2198 printf("Error: No username supplied!\n");
2199 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2200 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2201 printf("Error: No NT or LM password "
2202 "blobs supplied!\n");
2203 } else {
2204 char *error_string = NULL;
2206 if (full_username && !username) {
2207 fstring fstr_user;
2208 fstring fstr_domain;
2210 if (!parse_ntlm_auth_domain_user(full_username,
2211 fstr_user,
2212 fstr_domain)) {
2213 /* username might be 'tainted', don't
2214 * print into our new-line
2215 * deleimianted stream */
2216 printf("Error: Could not "
2217 "parse into domain and "
2218 "username\n");
2219 SAFE_FREE(username);
2220 username = smb_xstrdup(full_username);
2221 } else {
2222 SAFE_FREE(username);
2223 SAFE_FREE(domain);
2224 username = smb_xstrdup(fstr_user);
2225 domain = smb_xstrdup(fstr_domain);
2230 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2231 username, domain,
2232 new_nt_pswd,
2233 old_nt_hash_enc,
2234 new_lm_pswd,
2235 old_lm_hash_enc,
2236 &error_string))) {
2237 printf("Password-Change: No\n");
2238 printf("Password-Change-Error: %s\n.\n",
2239 error_string);
2240 } else {
2241 printf("Password-Change: Yes\n");
2244 SAFE_FREE(error_string);
2246 /* clear out the state */
2247 new_nt_pswd = data_blob_null;
2248 old_nt_hash_enc = data_blob_null;
2249 new_lm_pswd = data_blob_null;
2250 old_nt_hash_enc = data_blob_null;
2251 SAFE_FREE(full_username);
2252 SAFE_FREE(username);
2253 SAFE_FREE(domain);
2254 SAFE_FREE(newpswd);
2255 SAFE_FREE(oldpswd);
2256 printf(".\n");
2258 return;
2261 request = buf;
2263 /* Indicates a base64 encoded structure */
2264 parameter = strstr_m(request, ":: ");
2265 if (!parameter) {
2266 parameter = strstr_m(request, ": ");
2268 if (!parameter) {
2269 DEBUG(0, ("Parameter not found!\n"));
2270 printf("Error: Parameter not found!\n.\n");
2271 return;
2274 parameter[0] ='\0';
2275 parameter++;
2276 parameter[0] ='\0';
2277 parameter++;
2278 } else {
2279 parameter[0] ='\0';
2280 parameter++;
2281 parameter[0] ='\0';
2282 parameter++;
2283 parameter[0] ='\0';
2284 parameter++;
2286 base64_decode_inplace(parameter);
2289 if (strequal(request, "new-nt-password-blob")) {
2290 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2291 if (new_nt_pswd.length != 516) {
2292 printf("Error: hex decode of %s failed! "
2293 "(got %d bytes, expected 516)\n.\n",
2294 parameter,
2295 (int)new_nt_pswd.length);
2296 new_nt_pswd = data_blob_null;
2298 } else if (strequal(request, "old-nt-hash-blob")) {
2299 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2300 if (old_nt_hash_enc.length != 16) {
2301 printf("Error: hex decode of %s failed! "
2302 "(got %d bytes, expected 16)\n.\n",
2303 parameter,
2304 (int)old_nt_hash_enc.length);
2305 old_nt_hash_enc = data_blob_null;
2307 } else if (strequal(request, "new-lm-password-blob")) {
2308 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2309 if (new_lm_pswd.length != 516) {
2310 printf("Error: hex decode of %s failed! "
2311 "(got %d bytes, expected 516)\n.\n",
2312 parameter,
2313 (int)new_lm_pswd.length);
2314 new_lm_pswd = data_blob_null;
2317 else if (strequal(request, "old-lm-hash-blob")) {
2318 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2319 if (old_lm_hash_enc.length != 16)
2321 printf("Error: hex decode of %s failed! "
2322 "(got %d bytes, expected 16)\n.\n",
2323 parameter,
2324 (int)old_lm_hash_enc.length);
2325 old_lm_hash_enc = data_blob_null;
2327 } else if (strequal(request, "nt-domain")) {
2328 domain = smb_xstrdup(parameter);
2329 } else if(strequal(request, "username")) {
2330 username = smb_xstrdup(parameter);
2331 } else if(strequal(request, "full-username")) {
2332 username = smb_xstrdup(parameter);
2333 } else if(strequal(request, "new-password")) {
2334 newpswd = smb_xstrdup(parameter);
2335 } else if (strequal(request, "old-password")) {
2336 oldpswd = smb_xstrdup(parameter);
2337 } else {
2338 printf("Error: Unknown request %s\n.\n", request);
2342 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2343 struct loadparm_context *lp_ctx,
2344 struct ntlm_auth_state *state,
2345 stdio_helper_function fn, void **private2)
2347 char *buf;
2348 char tmp[INITIAL_BUFFER_SIZE+1];
2349 int length, buf_size = 0;
2350 char *c;
2352 buf = talloc_strdup(state->mem_ctx, "");
2353 if (!buf) {
2354 DEBUG(0, ("Failed to allocate input buffer.\n"));
2355 fprintf(stderr, "ERR\n");
2356 exit(1);
2359 do {
2361 /* this is not a typo - x_fgets doesn't work too well under
2362 * squid */
2363 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2364 if (ferror(stdin)) {
2365 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2366 "(%s)\n", ferror(stdin),
2367 strerror(ferror(stdin))));
2369 exit(1);
2371 exit(0);
2374 buf = talloc_strdup_append_buffer(buf, tmp);
2375 buf_size += INITIAL_BUFFER_SIZE;
2377 if (buf_size > MAX_BUFFER_SIZE) {
2378 DEBUG(2, ("Oversized message\n"));
2379 fprintf(stderr, "ERR\n");
2380 talloc_free(buf);
2381 return;
2384 c = strchr(buf, '\n');
2385 } while (c == NULL);
2387 *c = '\0';
2388 length = c-buf;
2390 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2392 if (buf[0] == '\0') {
2393 DEBUG(2, ("Invalid Request\n"));
2394 fprintf(stderr, "ERR\n");
2395 talloc_free(buf);
2396 return;
2399 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2400 talloc_free(buf);
2404 static void squid_stream(enum stdio_helper_mode stdio_mode,
2405 struct loadparm_context *lp_ctx,
2406 stdio_helper_function fn) {
2407 TALLOC_CTX *mem_ctx;
2408 struct ntlm_auth_state *state;
2410 /* initialize FDescs */
2411 setbuf(stdout, NULL);
2412 setbuf(stderr, NULL);
2414 mem_ctx = talloc_init("ntlm_auth");
2415 if (!mem_ctx) {
2416 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2417 fprintf(stderr, "ERR\n");
2418 exit(1);
2421 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2422 if (!state) {
2423 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2424 fprintf(stderr, "ERR\n");
2425 exit(1);
2428 state->mem_ctx = mem_ctx;
2429 state->helper_mode = stdio_mode;
2431 while(1) {
2432 TALLOC_CTX *frame = talloc_stackframe();
2433 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2434 TALLOC_FREE(frame);
2439 /* Authenticate a user with a challenge/response */
2441 static bool check_auth_crap(void)
2443 NTSTATUS nt_status;
2444 uint32_t flags = 0;
2445 char lm_key[8];
2446 char user_session_key[16];
2447 char *hex_lm_key;
2448 char *hex_user_session_key;
2449 char *error_string;
2450 uint8_t authoritative = 0;
2452 setbuf(stdout, NULL);
2454 if (request_lm_key)
2455 flags |= WBFLAG_PAM_LMKEY;
2457 if (request_user_session_key)
2458 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2460 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2462 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2463 opt_workstation,
2464 &opt_challenge,
2465 &opt_lm_response,
2466 &opt_nt_response,
2467 flags, 0,
2468 (unsigned char *)lm_key,
2469 (unsigned char *)user_session_key,
2470 &authoritative,
2471 &error_string, NULL);
2473 if (!NT_STATUS_IS_OK(nt_status)) {
2474 printf("%s (0x%x)\n", error_string,
2475 NT_STATUS_V(nt_status));
2476 SAFE_FREE(error_string);
2477 return False;
2480 if (request_lm_key
2481 && (!all_zero((uint8_t *)lm_key, sizeof(lm_key)))) {
2482 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2483 sizeof(lm_key));
2484 printf("LM_KEY: %s\n", hex_lm_key);
2485 TALLOC_FREE(hex_lm_key);
2487 if (request_user_session_key
2488 && (!all_zero((uint8_t *)user_session_key,
2489 sizeof(user_session_key)))) {
2490 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2491 sizeof(user_session_key));
2492 printf("NT_KEY: %s\n", hex_user_session_key);
2493 TALLOC_FREE(hex_user_session_key);
2496 return True;
2499 /* Main program */
2501 enum {
2502 OPT_USERNAME = 1000,
2503 OPT_DOMAIN,
2504 OPT_WORKSTATION,
2505 OPT_CHALLENGE,
2506 OPT_RESPONSE,
2507 OPT_LM,
2508 OPT_NT,
2509 OPT_PASSWORD,
2510 OPT_LM_KEY,
2511 OPT_USER_SESSION_KEY,
2512 OPT_DIAGNOSTICS,
2513 OPT_REQUIRE_MEMBERSHIP,
2514 OPT_USE_CACHED_CREDS,
2515 OPT_ALLOW_MSCHAPV2,
2516 OPT_PAM_WINBIND_CONF,
2517 OPT_TARGET_SERVICE,
2518 OPT_TARGET_HOSTNAME,
2519 OPT_OFFLINE_LOGON
2522 int main(int argc, const char **argv)
2524 TALLOC_CTX *frame = talloc_stackframe();
2525 int opt;
2526 const char *helper_protocol = NULL;
2527 int diagnostics = 0;
2529 const char *hex_challenge = NULL;
2530 const char *hex_lm_response = NULL;
2531 const char *hex_nt_response = NULL;
2532 struct loadparm_context *lp_ctx;
2533 poptContext pc;
2534 bool ok;
2536 /* NOTE: DO NOT change this interface without considering the implications!
2537 This is an external interface, which other programs will use to interact
2538 with this helper.
2541 /* We do not use single-letter command abbreviations, because they harm future
2542 interface stability. */
2544 struct poptOption long_options[] = {
2545 POPT_AUTOHELP
2547 .longName = "helper-protocol",
2548 .shortName = 0,
2549 .argInfo = POPT_ARG_STRING,
2550 .arg = &helper_protocol,
2551 .val = OPT_DOMAIN,
2552 .descrip = "operate as a stdio-based helper",
2553 .argDescrip = "helper protocol to use"
2556 .longName = "username",
2557 .shortName = 0,
2558 .argInfo = POPT_ARG_STRING,
2559 .arg = &opt_username,
2560 .val = OPT_USERNAME,
2561 .descrip = "username"
2564 .longName = "domain",
2565 .shortName = 0,
2566 .argInfo = POPT_ARG_STRING,
2567 .arg = &opt_domain,
2568 .val = OPT_DOMAIN,
2569 .descrip = "domain name"
2572 .longName = "workstation",
2573 .shortName = 0,
2574 .argInfo = POPT_ARG_STRING,
2575 .arg = &opt_workstation,
2576 .val = OPT_WORKSTATION,
2577 .descrip = "workstation"
2580 .longName = "challenge",
2581 .shortName = 0,
2582 .argInfo = POPT_ARG_STRING,
2583 .arg = &hex_challenge,
2584 .val = OPT_CHALLENGE,
2585 .descrip = "challenge (HEX encoded)"
2588 .longName = "lm-response",
2589 .shortName = 0,
2590 .argInfo = POPT_ARG_STRING,
2591 .arg = &hex_lm_response,
2592 .val = OPT_LM,
2593 .descrip = "LM Response to the challenge (HEX encoded)"
2596 .longName = "nt-response",
2597 .shortName = 0,
2598 .argInfo = POPT_ARG_STRING,
2599 .arg = &hex_nt_response,
2600 .val = OPT_NT,
2601 .descrip = "NT or NTLMv2 Response to the challenge (HEX encoded)"
2604 .longName = "password",
2605 .shortName = 0,
2606 .argInfo = POPT_ARG_STRING,
2607 .arg = &opt_password,
2608 .val = OPT_PASSWORD,
2609 .descrip = "User's plaintext password"
2612 .longName = "request-lm-key",
2613 .shortName = 0,
2614 .argInfo = POPT_ARG_NONE,
2615 .arg = &request_lm_key,
2616 .val = OPT_LM_KEY,
2617 .descrip = "Retrieve LM session key"
2620 .longName = "request-nt-key",
2621 .shortName = 0,
2622 .argInfo = POPT_ARG_NONE,
2623 .arg = &request_user_session_key,
2624 .val = OPT_USER_SESSION_KEY,
2625 .descrip = "Retrieve User (NT) session key"
2628 .longName = "use-cached-creds",
2629 .shortName = 0,
2630 .argInfo = POPT_ARG_NONE,
2631 .arg = &use_cached_creds,
2632 .val = OPT_USE_CACHED_CREDS,
2633 .descrip = "Use cached credentials if no password is given"
2636 .longName = "allow-mschapv2",
2637 .shortName = 0,
2638 .argInfo = POPT_ARG_NONE,
2639 .arg = &opt_allow_mschapv2,
2640 .val = OPT_ALLOW_MSCHAPV2,
2641 .descrip = "Explicitly allow MSCHAPv2",
2644 .longName = "offline-logon",
2645 .shortName = 0,
2646 .argInfo = POPT_ARG_NONE,
2647 .arg = &offline_logon,
2648 .val = OPT_OFFLINE_LOGON,
2649 .descrip = "Use cached passwords when DC is offline"
2652 .longName = "diagnostics",
2653 .shortName = 0,
2654 .argInfo = POPT_ARG_NONE,
2655 .arg = &diagnostics,
2656 .val = OPT_DIAGNOSTICS,
2657 .descrip = "Perform diagnostics on the authentication chain"
2660 .longName = "require-membership-of",
2661 .shortName = 0,
2662 .argInfo = POPT_ARG_STRING,
2663 .arg = &require_membership_of,
2664 .val = OPT_REQUIRE_MEMBERSHIP,
2665 .descrip = "Require that a user be a member of this group (either name or SID) for authentication to succeed",
2668 .longName = "pam-winbind-conf",
2669 .shortName = 0,
2670 .argInfo = POPT_ARG_STRING,
2671 .arg = &opt_pam_winbind_conf,
2672 .val = OPT_PAM_WINBIND_CONF,
2673 .descrip = "Require that request must set WBFLAG_PAM_CONTACT_TRUSTDOM when krb5 auth is required",
2676 .longName = "target-service",
2677 .shortName = 0,
2678 .argInfo = POPT_ARG_STRING,
2679 .arg = &opt_target_service,
2680 .val = OPT_TARGET_SERVICE,
2681 .descrip = "Target service (eg http)",
2684 .longName = "target-hostname",
2685 .shortName = 0,
2686 .argInfo = POPT_ARG_STRING,
2687 .arg = &opt_target_hostname,
2688 .val = OPT_TARGET_HOSTNAME,
2689 .descrip = "Target hostname",
2691 POPT_COMMON_DEBUG_ONLY
2692 POPT_COMMON_CONFIG_ONLY
2693 POPT_COMMON_OPTION_ONLY
2694 POPT_COMMON_VERSION
2695 POPT_TABLEEND
2698 /* Samba client initialisation */
2699 smb_init_locale();
2701 ok = samba_cmdline_init(frame,
2702 SAMBA_CMDLINE_CONFIG_CLIENT,
2703 false /* require_smbconf */);
2704 if (!ok) {
2705 DBG_ERR("Failed to init cmdline parser!\n");
2706 TALLOC_FREE(frame);
2707 exit(1);
2710 pc = samba_popt_get_context(getprogname(),
2711 argc,
2712 argv,
2713 long_options,
2714 POPT_CONTEXT_KEEP_FIRST);
2715 if (pc == NULL) {
2716 DBG_ERR("Failed to setup popt context!\n");
2717 TALLOC_FREE(frame);
2718 exit(1);
2721 while((opt = poptGetNextOpt(pc)) != -1) {
2722 switch (opt) {
2723 case OPT_CHALLENGE:
2724 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2725 if (opt_challenge.length != 8) {
2726 fprintf(stderr, "hex decode of %s failed! "
2727 "(only got %d bytes)\n",
2728 hex_challenge,
2729 (int)opt_challenge.length);
2730 exit(1);
2732 break;
2733 case OPT_LM:
2734 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2735 if (opt_lm_response.length != 24) {
2736 fprintf(stderr, "hex decode of %s failed! "
2737 "(only got %d bytes)\n",
2738 hex_lm_response,
2739 (int)opt_lm_response.length);
2740 exit(1);
2742 break;
2744 case OPT_NT:
2745 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2746 if (opt_nt_response.length < 24) {
2747 fprintf(stderr, "hex decode of %s failed! "
2748 "(only got %d bytes)\n",
2749 hex_nt_response,
2750 (int)opt_nt_response.length);
2751 exit(1);
2753 break;
2755 case OPT_REQUIRE_MEMBERSHIP:
2756 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2757 require_membership_of_sid = require_membership_of;
2759 break;
2761 case POPT_ERROR_BADOPT:
2762 fprintf(stderr, "\nInvalid option %s: %s\n\n",
2763 poptBadOption(pc, 0), poptStrerror(opt));
2764 poptPrintUsage(pc, stderr, 0);
2765 exit(1);
2769 if (opt_username) {
2770 char *domain = SMB_STRDUP(opt_username);
2771 char *p = strchr_m(domain, *lp_winbind_separator());
2772 if (p) {
2773 opt_username = p+1;
2774 *p = '\0';
2775 if (opt_domain && !strequal(opt_domain, domain)) {
2776 fprintf(stderr, "Domain specified in username (%s) "
2777 "doesn't match specified domain (%s)!\n\n",
2778 domain, opt_domain);
2779 poptPrintHelp(pc, stderr, 0);
2780 exit(1);
2782 opt_domain = domain;
2783 } else {
2784 SAFE_FREE(domain);
2788 /* Note: if opt_domain is "" then send no domain */
2789 if (opt_domain == NULL) {
2790 opt_domain = get_winbind_domain();
2793 if (opt_workstation == NULL) {
2794 opt_workstation = "";
2797 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2798 if (lp_ctx == NULL) {
2799 fprintf(stderr, "loadparm_init_s3() failed!\n");
2800 exit(1);
2803 if (helper_protocol) {
2804 int i;
2805 for (i=0; i<NUM_HELPER_MODES; i++) {
2806 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2807 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2808 exit(0);
2811 fprintf(stderr, "unknown helper protocol [%s]\n\n"
2812 "Valid helper protools:\n\n", helper_protocol);
2814 for (i=0; i<NUM_HELPER_MODES; i++) {
2815 fprintf(stderr, "%s\n",
2816 stdio_helper_protocols[i].name);
2819 exit(1);
2822 if (!opt_username || !*opt_username) {
2823 fprintf(stderr, "username must be specified!\n\n");
2824 poptPrintHelp(pc, stderr, 0);
2825 exit(1);
2828 if (opt_challenge.length) {
2829 if (!check_auth_crap()) {
2830 exit(1);
2832 exit(0);
2835 if (!opt_password) {
2836 char pwd[256] = {0};
2837 int rc;
2839 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2840 if (rc == 0) {
2841 opt_password = SMB_STRDUP(pwd);
2845 if (diagnostics) {
2846 if (!diagnose_ntlm_auth()) {
2847 poptFreeContext(pc);
2848 return 1;
2850 } else {
2851 fstring user;
2853 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2854 if (!check_plaintext_auth(user, opt_password, True)) {
2855 poptFreeContext(pc);
2856 return 1;
2860 /* Exit code */
2862 poptFreeContext(pc);
2863 TALLOC_FREE(frame);
2864 return 0;