s3:smbd only initialize kernel oplocks if they are enabled for a share
[Samba/vl.git] / source3 / utils / ntlm_auth.c
blobab629d2ccc65e0013d22ba629a725f0961ad222d
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 "utils/ntlm_auth.h"
31 #include "../libcli/auth/libcli_auth.h"
32 #include "../libcli/auth/spnego.h"
33 #include "auth/ntlmssp/ntlmssp.h"
34 #include "auth/gensec/gensec.h"
35 #include "auth/credentials/credentials.h"
36 #include "librpc/crypto/gse.h"
37 #include "smb_krb5.h"
38 #include <iniparser.h>
39 #include "../lib/crypto/arcfour.h"
40 #include "libads/kerberos_proto.h"
41 #include "nsswitch/winbind_client.h"
42 #include "librpc/gen_ndr/krb5pac.h"
43 #include "../lib/util/asn1.h"
44 #include "auth/common_auth.h"
45 #include "source3/include/auth.h"
46 #include "source3/auth/proto.h"
47 #include "nsswitch/libwbclient/wbclient.h"
49 #ifndef PAM_WINBIND_CONFIG_FILE
50 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
51 #endif
53 #define WINBIND_KRB5_AUTH 0x00000080
55 #undef DBGC_CLASS
56 #define DBGC_CLASS DBGC_WINBIND
58 #define INITIAL_BUFFER_SIZE 300
59 #define MAX_BUFFER_SIZE 630000
61 enum stdio_helper_mode {
62 SQUID_2_4_BASIC,
63 SQUID_2_5_BASIC,
64 SQUID_2_5_NTLMSSP,
65 NTLMSSP_CLIENT_1,
66 GSS_SPNEGO_SERVER,
67 GSS_SPNEGO_CLIENT,
68 NTLM_SERVER_1,
69 NTLM_CHANGE_PASSWORD_1,
70 NUM_HELPER_MODES
73 enum ntlm_auth_cli_state {
74 CLIENT_INITIAL = 0,
75 CLIENT_RESPONSE,
76 CLIENT_FINISHED,
77 CLIENT_ERROR
80 struct ntlm_auth_state {
81 TALLOC_CTX *mem_ctx;
82 enum stdio_helper_mode helper_mode;
83 enum ntlm_auth_cli_state cli_state;
84 struct ntlmssp_state *ntlmssp_state;
85 uint32_t neg_flags;
86 char *want_feature_list;
87 bool have_session_key;
88 DATA_BLOB session_key;
89 DATA_BLOB initial_message;
90 void *gensec_private_1;
92 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
93 struct loadparm_context *lp_ctx,
94 struct ntlm_auth_state *state, char *buf,
95 int length, void **private2);
97 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
98 struct loadparm_context *lp_ctx,
99 struct ntlm_auth_state *state,
100 stdio_helper_function fn, void **private2);
102 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
103 struct loadparm_context *lp_ctx,
104 struct ntlm_auth_state *state,
105 char *buf, int length, void **private2);
107 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
108 struct loadparm_context *lp_ctx,
109 struct ntlm_auth_state *state,
110 char *buf, int length, void **private2);
112 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
113 struct loadparm_context *lp_ctx,
114 struct ntlm_auth_state *state,
115 char *buf, int length, void **private2);
117 static void manage_gss_spnego_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_gss_spnego_client_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_ntlm_server_1_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_ntlm_change_password_1_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 const struct {
138 enum stdio_helper_mode mode;
139 const char *name;
140 stdio_helper_function fn;
141 } stdio_helper_protocols[] = {
142 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
143 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
144 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
145 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
146 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
147 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
148 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
149 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
150 { NUM_HELPER_MODES, NULL, NULL}
153 const char *opt_username;
154 const char *opt_domain;
155 const char *opt_workstation;
156 const char *opt_password;
157 static DATA_BLOB opt_challenge;
158 static DATA_BLOB opt_lm_response;
159 static DATA_BLOB opt_nt_response;
160 static int request_lm_key;
161 static int request_user_session_key;
162 static int use_cached_creds;
164 static const char *require_membership_of;
165 static const char *require_membership_of_sid;
166 static const char *opt_pam_winbind_conf;
168 const char *opt_target_service;
169 const char *opt_target_hostname;
172 /* This is a bit hairy, but the basic idea is to do a password callback
173 to the calling application. The callback comes from within gensec */
175 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
176 struct loadparm_context *lp_ctx,
177 struct ntlm_auth_state *state, char *buf, int length,
178 void **password)
180 DATA_BLOB in;
181 if (strlen(buf) < 2) {
182 DEBUG(1, ("query [%s] invalid", buf));
183 x_fprintf(x_stdout, "BH Query invalid\n");
184 return;
187 if (strlen(buf) > 3) {
188 in = base64_decode_data_blob(buf + 3);
189 } else {
190 in = data_blob(NULL, 0);
193 if (strncmp(buf, "PW ", 3) == 0) {
195 *password = talloc_strndup(NULL,
196 (const char *)in.data, in.length);
198 if (*password == NULL) {
199 DEBUG(1, ("Out of memory\n"));
200 x_fprintf(x_stdout, "BH Out of memory\n");
201 data_blob_free(&in);
202 return;
205 x_fprintf(x_stdout, "OK\n");
206 data_blob_free(&in);
207 return;
209 DEBUG(1, ("Asked for (and expected) a password\n"));
210 x_fprintf(x_stdout, "BH Expected a password\n");
211 data_blob_free(&in);
215 * Callback for password credentials. This is not async, and when
216 * GENSEC and the credentials code is made async, it will look rather
217 * different.
220 static const char *get_password(struct cli_credentials *credentials)
222 char *password = NULL;
224 /* Ask for a password */
225 x_fprintf(x_stdout, "PW\n");
226 credentials->priv_data = NULL;
228 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, NULL, manage_gensec_get_pw_request, (void **)&password);
229 talloc_steal(credentials, password);
230 return password;
234 * A limited set of features are defined with text strings as needed
235 * by ntlm_auth
238 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
240 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
241 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
242 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
244 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
245 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
246 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
248 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
249 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
250 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
254 static char winbind_separator(void)
256 struct winbindd_response response;
257 static bool got_sep;
258 static char sep;
260 if (got_sep)
261 return sep;
263 ZERO_STRUCT(response);
265 /* Send off request */
267 if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
268 NSS_STATUS_SUCCESS) {
269 d_printf("could not obtain winbind separator!\n");
270 return *lp_winbind_separator();
273 sep = response.data.info.winbind_separator;
274 got_sep = True;
276 if (!sep) {
277 d_printf("winbind separator was NULL!\n");
278 return *lp_winbind_separator();
281 return sep;
284 const char *get_winbind_domain(void)
286 struct winbindd_response response;
288 static fstring winbind_domain;
289 if (*winbind_domain) {
290 return winbind_domain;
293 ZERO_STRUCT(response);
295 /* Send off request */
297 if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
298 NSS_STATUS_SUCCESS) {
299 DEBUG(0, ("could not obtain winbind domain name!\n"));
300 return lp_workgroup();
303 fstrcpy(winbind_domain, response.data.domain_name);
305 return winbind_domain;
309 const char *get_winbind_netbios_name(void)
311 struct winbindd_response response;
313 static fstring winbind_netbios_name;
315 if (*winbind_netbios_name) {
316 return winbind_netbios_name;
319 ZERO_STRUCT(response);
321 /* Send off request */
323 if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) !=
324 NSS_STATUS_SUCCESS) {
325 DEBUG(0, ("could not obtain winbind netbios name!\n"));
326 return lp_netbios_name();
329 fstrcpy(winbind_netbios_name, response.data.netbios_name);
331 return winbind_netbios_name;
335 DATA_BLOB get_challenge(void)
337 static DATA_BLOB chal;
338 if (opt_challenge.length)
339 return opt_challenge;
341 chal = data_blob(NULL, 8);
343 generate_random_buffer(chal.data, chal.length);
344 return chal;
347 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
348 form DOMAIN/user into a domain and a user */
350 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
351 fstring user)
354 char *p = strchr(domuser,winbind_separator());
356 if (!p) {
357 return False;
360 fstrcpy(user, p+1);
361 fstrcpy(domain, domuser);
362 domain[PTR_DIFF(p, domuser)] = 0;
363 strupper_m(domain);
365 return True;
368 static bool get_require_membership_sid(void) {
369 struct winbindd_request request;
370 struct winbindd_response response;
372 if (!require_membership_of) {
373 return True;
376 if (require_membership_of_sid) {
377 return True;
380 /* Otherwise, ask winbindd for the name->sid request */
382 ZERO_STRUCT(request);
383 ZERO_STRUCT(response);
385 if (!parse_ntlm_auth_domain_user(require_membership_of,
386 request.data.name.dom_name,
387 request.data.name.name)) {
388 DEBUG(0, ("Could not parse %s into seperate domain/name parts!\n",
389 require_membership_of));
390 return False;
393 if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
394 NSS_STATUS_SUCCESS) {
395 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
396 require_membership_of));
397 return False;
400 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
402 if (require_membership_of_sid)
403 return True;
405 return False;
409 * Get some configuration from pam_winbind.conf to see if we
410 * need to contact trusted domain
413 int get_pam_winbind_config()
415 int ctrl = 0;
416 dictionary *d = NULL;
418 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
419 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
422 d = iniparser_load(discard_const_p(char, opt_pam_winbind_conf));
424 if (!d) {
425 return 0;
428 if (iniparser_getboolean(d, discard_const_p(char, "global:krb5_auth"), false)) {
429 ctrl |= WINBIND_KRB5_AUTH;
432 iniparser_freedict(d);
434 return ctrl;
437 /* Authenticate a user with a plaintext password */
439 static bool check_plaintext_auth(const char *user, const char *pass,
440 bool stdout_diagnostics)
442 struct winbindd_request request;
443 struct winbindd_response response;
444 NSS_STATUS result;
446 if (!get_require_membership_sid()) {
447 return False;
450 /* Send off request */
452 ZERO_STRUCT(request);
453 ZERO_STRUCT(response);
455 fstrcpy(request.data.auth.user, user);
456 fstrcpy(request.data.auth.pass, pass);
457 if (require_membership_of_sid) {
458 strlcpy(request.data.auth.require_membership_of_sid,
459 require_membership_of_sid,
460 sizeof(request.data.auth.require_membership_of_sid));
463 result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
465 /* Display response */
467 if (stdout_diagnostics) {
468 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
469 d_printf("Reading winbind reply failed! (0x01)\n");
472 d_printf("%s: %s (0x%x)\n",
473 response.data.auth.nt_status_string,
474 response.data.auth.error_string,
475 response.data.auth.nt_status);
476 } else {
477 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
478 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
481 DEBUG(3, ("%s: %s (0x%x)\n",
482 response.data.auth.nt_status_string,
483 response.data.auth.error_string,
484 response.data.auth.nt_status));
487 return (result == NSS_STATUS_SUCCESS);
490 /* authenticate a user with an encrypted username/password */
492 NTSTATUS contact_winbind_auth_crap(const char *username,
493 const char *domain,
494 const char *workstation,
495 const DATA_BLOB *challenge,
496 const DATA_BLOB *lm_response,
497 const DATA_BLOB *nt_response,
498 uint32 flags,
499 uint32 extra_logon_parameters,
500 uint8 lm_key[8],
501 uint8 user_session_key[16],
502 char **error_string,
503 char **unix_name)
505 NTSTATUS nt_status;
506 NSS_STATUS result;
507 struct winbindd_request request;
508 struct winbindd_response response;
510 if (!get_require_membership_sid()) {
511 return NT_STATUS_INVALID_PARAMETER;
514 ZERO_STRUCT(request);
515 ZERO_STRUCT(response);
517 request.flags = flags;
519 request.data.auth_crap.logon_parameters = extra_logon_parameters
520 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
522 if (require_membership_of_sid)
523 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
525 fstrcpy(request.data.auth_crap.user, username);
526 fstrcpy(request.data.auth_crap.domain, domain);
528 fstrcpy(request.data.auth_crap.workstation,
529 workstation);
531 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
533 if (lm_response && lm_response->length) {
534 memcpy(request.data.auth_crap.lm_resp,
535 lm_response->data,
536 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
537 request.data.auth_crap.lm_resp_len = lm_response->length;
540 if (nt_response && nt_response->length) {
541 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
542 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
543 request.extra_len = nt_response->length;
544 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
545 if (request.extra_data.data == NULL) {
546 return NT_STATUS_NO_MEMORY;
548 memcpy(request.extra_data.data, nt_response->data,
549 nt_response->length);
551 } else {
552 memcpy(request.data.auth_crap.nt_resp,
553 nt_response->data, nt_response->length);
555 request.data.auth_crap.nt_resp_len = nt_response->length;
558 result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
559 SAFE_FREE(request.extra_data.data);
561 /* Display response */
563 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
564 nt_status = NT_STATUS_UNSUCCESSFUL;
565 if (error_string)
566 *error_string = smb_xstrdup("Reading winbind reply failed!");
567 winbindd_free_response(&response);
568 return nt_status;
571 nt_status = (NT_STATUS(response.data.auth.nt_status));
572 if (!NT_STATUS_IS_OK(nt_status)) {
573 if (error_string)
574 *error_string = smb_xstrdup(response.data.auth.error_string);
575 winbindd_free_response(&response);
576 return nt_status;
579 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
580 memcpy(lm_key, response.data.auth.first_8_lm_hash,
581 sizeof(response.data.auth.first_8_lm_hash));
583 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
584 memcpy(user_session_key, response.data.auth.user_session_key,
585 sizeof(response.data.auth.user_session_key));
588 if (flags & WBFLAG_PAM_UNIX_NAME) {
589 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
590 if (!*unix_name) {
591 winbindd_free_response(&response);
592 return NT_STATUS_NO_MEMORY;
596 winbindd_free_response(&response);
597 return nt_status;
600 /* contact server to change user password using auth crap */
601 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
602 const char *domain,
603 const DATA_BLOB new_nt_pswd,
604 const DATA_BLOB old_nt_hash_enc,
605 const DATA_BLOB new_lm_pswd,
606 const DATA_BLOB old_lm_hash_enc,
607 char **error_string)
609 NTSTATUS nt_status;
610 NSS_STATUS result;
611 struct winbindd_request request;
612 struct winbindd_response response;
614 if (!get_require_membership_sid())
616 if(error_string)
617 *error_string = smb_xstrdup("Can't get membership sid.");
618 return NT_STATUS_INVALID_PARAMETER;
621 ZERO_STRUCT(request);
622 ZERO_STRUCT(response);
624 if(username != NULL)
625 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
626 if(domain != NULL)
627 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
629 if(new_nt_pswd.length)
631 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
632 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
635 if(old_nt_hash_enc.length)
637 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));
638 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
641 if(new_lm_pswd.length)
643 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
644 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
647 if(old_lm_hash_enc.length)
649 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));
650 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
653 result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
655 /* Display response */
657 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
659 nt_status = NT_STATUS_UNSUCCESSFUL;
660 if (error_string)
661 *error_string = smb_xstrdup("Reading winbind reply failed!");
662 winbindd_free_response(&response);
663 return nt_status;
666 nt_status = (NT_STATUS(response.data.auth.nt_status));
667 if (!NT_STATUS_IS_OK(nt_status))
669 if (error_string)
670 *error_string = smb_xstrdup(response.data.auth.error_string);
671 winbindd_free_response(&response);
672 return nt_status;
675 winbindd_free_response(&response);
677 return nt_status;
680 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
681 TALLOC_CTX *mem_ctx,
682 void *server_returned_info,
683 const char *original_user_name,
684 uint32_t session_info_flags,
685 struct auth_session_info **session_info_out)
687 char *unix_username = (char *)server_returned_info;
688 struct auth_session_info *session_info = talloc_zero(mem_ctx, struct auth_session_info);
689 if (!session_info) {
690 return NT_STATUS_NO_MEMORY;
693 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
694 if (!session_info->unix_info) {
695 TALLOC_FREE(session_info);
696 return NT_STATUS_NO_MEMORY;
698 session_info->unix_info->unix_name = talloc_steal(session_info->unix_info, unix_username);
700 *session_info_out = session_info;
702 return NT_STATUS_OK;
705 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
706 TALLOC_CTX *mem_ctx,
707 struct smb_krb5_context *smb_krb5_context,
708 DATA_BLOB *pac_blob,
709 const char *princ_name,
710 const struct tsocket_address *remote_address,
711 uint32_t session_info_flags,
712 struct auth_session_info **session_info)
714 TALLOC_CTX *tmp_ctx;
715 struct PAC_DATA *pac_data = NULL;
716 struct PAC_LOGON_INFO *logon_info = NULL;
717 unsigned int i;
718 char *unixuser;
719 NTSTATUS status;
720 char *domain = NULL;
721 char *realm = NULL;
722 char *user = NULL;
723 char *p;
725 tmp_ctx = talloc_new(mem_ctx);
726 if (!tmp_ctx) {
727 return NT_STATUS_NO_MEMORY;
730 if (pac_blob) {
731 #ifdef HAVE_KRB5
732 status = kerberos_decode_pac(tmp_ctx,
733 *pac_blob,
734 NULL, NULL, NULL, NULL, 0, &pac_data);
735 #else
736 status = NT_STATUS_ACCESS_DENIED;
737 #endif
738 if (!NT_STATUS_IS_OK(status)) {
739 goto done;
742 /* get logon name and logon info */
743 for (i = 0; i < pac_data->num_buffers; i++) {
744 struct PAC_BUFFER *data_buf = &pac_data->buffers[i];
746 switch (data_buf->type) {
747 case PAC_TYPE_LOGON_INFO:
748 if (!data_buf->info) {
749 break;
751 logon_info = data_buf->info->logon_info.info;
752 break;
753 default:
754 break;
757 if (!logon_info) {
758 DEBUG(1, ("Invalid PAC data, missing logon info!\n"));
759 status = NT_STATUS_NOT_FOUND;
760 goto done;
764 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
766 p = strchr_m(princ_name, '@');
767 if (!p) {
768 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
769 princ_name));
770 return NT_STATUS_LOGON_FAILURE;
773 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
774 if (!user) {
775 return NT_STATUS_NO_MEMORY;
778 realm = talloc_strdup(talloc_tos(), p + 1);
779 if (!realm) {
780 return NT_STATUS_NO_MEMORY;
783 if (!strequal(realm, lp_realm())) {
784 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
785 if (!lp_allow_trusted_domains()) {
786 return NT_STATUS_LOGON_FAILURE;
790 if (logon_info && logon_info->info3.base.logon_domain.string) {
791 domain = talloc_strdup(mem_ctx,
792 logon_info->info3.base.logon_domain.string);
793 if (!domain) {
794 return NT_STATUS_NO_MEMORY;
796 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
797 } else {
799 /* If we have winbind running, we can (and must) shorten the
800 username by using the short netbios name. Otherwise we will
801 have inconsistent user names. With Kerberos, we get the
802 fully qualified realm, with ntlmssp we get the short
803 name. And even w2k3 does use ntlmssp if you for example
804 connect to an ip address. */
806 wbcErr wbc_status;
807 struct wbcDomainInfo *info = NULL;
809 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
810 realm));
812 wbc_status = wbcDomainInfo(realm, &info);
814 if (WBC_ERROR_IS_OK(wbc_status)) {
815 domain = talloc_strdup(mem_ctx,
816 info->short_name);
817 wbcFreeMemory(info);
818 } else {
819 DEBUG(3, ("Could not find short name: %s\n",
820 wbcErrorString(wbc_status)));
821 domain = talloc_strdup(mem_ctx, realm);
823 if (!domain) {
824 return NT_STATUS_NO_MEMORY;
826 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
829 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
830 if (!unixuser) {
831 status = NT_STATUS_NO_MEMORY;
832 goto done;
835 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
837 done:
838 TALLOC_FREE(tmp_ctx);
839 return status;
845 * Return the challenge as determined by the authentication subsystem
846 * @return an 8 byte random challenge
849 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
850 uint8_t chal[8])
852 if (auth_ctx->challenge.data.length == 8) {
853 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
854 auth_ctx->challenge.set_by));
855 memcpy(chal, auth_ctx->challenge.data.data, 8);
856 return NT_STATUS_OK;
859 if (!auth_ctx->challenge.set_by) {
860 generate_random_buffer(chal, 8);
862 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
863 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
864 auth_ctx->challenge.set_by = "random";
866 auth_ctx->challenge.may_be_modified = true;
869 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
870 auth_ctx->challenge.set_by));
872 return NT_STATUS_OK;
876 * Some authentication methods 'fix' the challenge, so we may not be able to set it
878 * @return If the effective challenge used by the auth subsystem may be modified
880 static bool ntlm_auth_may_set_challenge(struct auth4_context *auth_ctx)
882 return auth_ctx->challenge.may_be_modified;
886 * NTLM2 authentication modifies the effective challenge,
887 * @param challenge The new challenge value
889 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
891 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
892 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
894 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
895 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
897 return NT_STATUS_OK;
901 * Check the password on an NTLMSSP login.
903 * Return the session keys used on the connection.
906 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
907 TALLOC_CTX *mem_ctx,
908 const struct auth_usersupplied_info *user_info,
909 void **server_returned_info,
910 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
912 static const char zeros[16] = { 0, };
913 NTSTATUS nt_status;
914 char *error_string = NULL;
915 uint8 lm_key[8];
916 uint8 user_sess_key[16];
917 char *unix_name = NULL;
919 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
920 user_info->workstation_name,
921 &auth4_context->challenge.data,
922 &user_info->password.response.lanman,
923 &user_info->password.response.nt,
924 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
926 lm_key, user_sess_key,
927 &error_string, &unix_name);
929 if (NT_STATUS_IS_OK(nt_status)) {
930 if (memcmp(lm_key, zeros, 8) != 0) {
931 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
932 memcpy(lm_session_key->data, lm_key, 8);
933 memset(lm_session_key->data+8, '\0', 8);
936 if (memcmp(user_sess_key, zeros, 16) != 0) {
937 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
939 *server_returned_info = talloc_strdup(mem_ctx,
940 unix_name);
941 } else {
942 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
943 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
944 user_info->client.domain_name, user_info->client.account_name,
945 user_info->workstation_name,
946 error_string ? error_string : "unknown error (NULL)"));
949 SAFE_FREE(error_string);
950 SAFE_FREE(unix_name);
951 return nt_status;
954 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
955 TALLOC_CTX *mem_ctx,
956 const struct auth_usersupplied_info *user_info,
957 void **server_returned_info,
958 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
960 NTSTATUS nt_status;
961 struct samr_Password lm_pw, nt_pw;
963 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
965 nt_status = ntlm_password_check(mem_ctx,
966 true, true, 0,
967 &auth4_context->challenge.data,
968 &user_info->password.response.lanman,
969 &user_info->password.response.nt,
970 user_info->client.account_name,
971 user_info->client.account_name,
972 user_info->client.domain_name,
973 &lm_pw, &nt_pw, session_key, lm_session_key);
975 if (NT_STATUS_IS_OK(nt_status)) {
976 *server_returned_info = talloc_asprintf(mem_ctx,
977 "%s%c%s", user_info->client.domain_name,
978 *lp_winbind_separator(),
979 user_info->client.account_name);
980 } else {
981 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
982 user_info->client.domain_name, user_info->client.account_name,
983 user_info->workstation_name,
984 nt_errstr(nt_status)));
986 return nt_status;
989 static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state)
991 NTSTATUS status;
992 if ( (opt_username == NULL) || (opt_domain == NULL) ) {
993 status = NT_STATUS_UNSUCCESSFUL;
994 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
995 return NT_STATUS_INVALID_PARAMETER;
998 status = ntlmssp_client_start(NULL,
999 lp_netbios_name(),
1000 lp_workgroup(),
1001 lp_client_ntlmv2_auth(),
1002 client_ntlmssp_state);
1004 if (!NT_STATUS_IS_OK(status)) {
1005 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
1006 nt_errstr(status)));
1007 TALLOC_FREE(*client_ntlmssp_state);
1008 return status;
1011 status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
1013 if (!NT_STATUS_IS_OK(status)) {
1014 DEBUG(1, ("Could not set username: %s\n",
1015 nt_errstr(status)));
1016 TALLOC_FREE(*client_ntlmssp_state);
1017 return status;
1020 status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
1022 if (!NT_STATUS_IS_OK(status)) {
1023 DEBUG(1, ("Could not set domain: %s\n",
1024 nt_errstr(status)));
1025 TALLOC_FREE(*client_ntlmssp_state);
1026 return status;
1029 if (opt_password) {
1030 status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
1032 if (!NT_STATUS_IS_OK(status)) {
1033 DEBUG(1, ("Could not set password: %s\n",
1034 nt_errstr(status)));
1035 TALLOC_FREE(*client_ntlmssp_state);
1036 return status;
1040 return NT_STATUS_OK;
1043 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1045 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1046 if (auth4_context == NULL) {
1047 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1048 return NULL;
1050 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1051 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1052 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1053 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1054 auth4_context->challenge_may_be_modified = ntlm_auth_may_set_challenge;
1055 if (local_pw) {
1056 auth4_context->check_ntlm_password = local_pw_check;
1057 } else {
1058 auth4_context->check_ntlm_password = winbind_pw_check;
1060 auth4_context->private_data = NULL;
1061 return auth4_context;
1064 static NTSTATUS ntlm_auth_start_ntlmssp_server(TALLOC_CTX *mem_ctx,
1065 struct loadparm_context *lp_ctx,
1066 struct gensec_security **gensec_security_out)
1068 struct gensec_security *gensec_security;
1069 NTSTATUS nt_status;
1071 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1072 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1074 struct gensec_settings *gensec_settings;
1075 size_t idx = 0;
1076 struct cli_credentials *server_credentials;
1078 struct auth4_context *auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1079 if (auth4_context == NULL) {
1080 TALLOC_FREE(tmp_ctx);
1081 return NT_STATUS_NO_MEMORY;
1084 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1085 if (lp_ctx == NULL) {
1086 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1087 TALLOC_FREE(tmp_ctx);
1088 return NT_STATUS_NO_MEMORY;
1092 * This should be a 'netbios domain -> DNS domain'
1093 * mapping, and can currently validly return NULL on
1094 * poorly configured systems.
1096 * This is used for the NTLMSSP server
1099 if (opt_password) {
1100 gensec_settings->server_netbios_name = lp_netbios_name();
1101 gensec_settings->server_netbios_domain = lp_workgroup();
1102 } else {
1103 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1104 gensec_settings->server_netbios_domain = get_winbind_domain();
1107 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1108 get_mydnsdomname(talloc_tos()));
1109 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1110 get_mydnsfullname());
1112 gensec_settings->backends = talloc_zero_array(gensec_settings,
1113 struct gensec_security_ops *, 4);
1115 if (gensec_settings->backends == NULL) {
1116 TALLOC_FREE(tmp_ctx);
1117 return NT_STATUS_NO_MEMORY;
1120 gensec_init();
1122 /* These need to be in priority order, krb5 before NTLMSSP */
1123 #if defined(HAVE_KRB5)
1124 gensec_settings->backends[idx++] = &gensec_gse_krb5_security_ops;
1125 #endif
1127 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1129 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL,
1130 GENSEC_OID_SPNEGO);
1133 * This is anonymous for now, because we just use it
1134 * to set the kerberos state at the moment
1136 server_credentials = cli_credentials_init_anon(tmp_ctx);
1137 if (!server_credentials) {
1138 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1139 return NT_STATUS_NO_MEMORY;
1142 cli_credentials_set_conf(server_credentials, lp_ctx);
1144 if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1145 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1146 } else {
1147 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1150 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1151 auth4_context, &gensec_security);
1153 if (!NT_STATUS_IS_OK(nt_status)) {
1154 TALLOC_FREE(tmp_ctx);
1155 return nt_status;
1158 gensec_set_credentials(gensec_security, server_credentials);
1160 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
1161 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
1163 talloc_unlink(tmp_ctx, lp_ctx);
1164 talloc_unlink(tmp_ctx, server_credentials);
1165 talloc_unlink(tmp_ctx, gensec_settings);
1166 talloc_unlink(tmp_ctx, auth4_context);
1168 nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP);
1169 if (!NT_STATUS_IS_OK(nt_status)) {
1170 TALLOC_FREE(tmp_ctx);
1171 return nt_status;
1174 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1175 TALLOC_FREE(tmp_ctx);
1176 return NT_STATUS_OK;
1179 /*******************************************************************
1180 Used by firefox to drive NTLM auth to IIS servers.
1181 *******************************************************************/
1183 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
1184 DATA_BLOB *reply)
1186 struct winbindd_request wb_request;
1187 struct winbindd_response wb_response;
1188 int ctrl = 0;
1189 NSS_STATUS result;
1191 /* get winbindd to do the ntlmssp step on our behalf */
1192 ZERO_STRUCT(wb_request);
1193 ZERO_STRUCT(wb_response);
1196 * This is tricky here. If we set krb5_auth in pam_winbind.conf
1197 * creds for users in trusted domain will be stored the winbindd
1198 * child of the trusted domain. If we ask the primary domain for
1199 * ntlm_ccache_auth, it will fail. So, we have to ask the trusted
1200 * domain's child for ccache_ntlm_auth. that is to say, we have to
1201 * set WBFLAG_PAM_CONTACT_TRUSTDOM in request.flags.
1203 ctrl = get_pam_winbind_config();
1205 if (ctrl & WINBIND_KRB5_AUTH) {
1206 wb_request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1209 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
1210 "%s%c%s", opt_domain, winbind_separator(), opt_username);
1211 wb_request.data.ccache_ntlm_auth.uid = geteuid();
1212 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
1213 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
1214 wb_request.extra_len = initial_msg.length + challenge_msg.length;
1216 if (wb_request.extra_len > 0) {
1217 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
1218 if (wb_request.extra_data.data == NULL) {
1219 return NT_STATUS_NO_MEMORY;
1222 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
1223 memcpy(wb_request.extra_data.data + initial_msg.length,
1224 challenge_msg.data, challenge_msg.length);
1227 result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
1228 SAFE_FREE(wb_request.extra_data.data);
1230 if (result != NSS_STATUS_SUCCESS) {
1231 winbindd_free_response(&wb_response);
1232 return NT_STATUS_UNSUCCESSFUL;
1235 if (reply) {
1236 *reply = data_blob(wb_response.extra_data.data,
1237 wb_response.data.ccache_ntlm_auth.auth_blob_len);
1238 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
1239 reply->data == NULL) {
1240 winbindd_free_response(&wb_response);
1241 return NT_STATUS_NO_MEMORY;
1245 winbindd_free_response(&wb_response);
1246 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1249 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1250 struct loadparm_context *lp_ctx,
1251 struct ntlm_auth_state *state,
1252 char *buf, int length, void **private2)
1254 DATA_BLOB request, reply;
1255 NTSTATUS nt_status;
1257 if (!opt_username || !*opt_username) {
1258 x_fprintf(x_stderr, "username must be specified!\n\n");
1259 exit(1);
1262 if (strlen(buf) < 2) {
1263 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1264 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1265 return;
1268 if (strlen(buf) > 3) {
1269 if(strncmp(buf, "SF ", 3) == 0) {
1270 DEBUG(10, ("Looking for flags to negotiate\n"));
1271 talloc_free(state->want_feature_list);
1272 state->want_feature_list = talloc_strdup(state->mem_ctx,
1273 buf+3);
1274 x_fprintf(x_stdout, "OK\n");
1275 return;
1277 request = base64_decode_data_blob(buf + 3);
1278 } else {
1279 request = data_blob_null;
1282 if (strncmp(buf, "PW ", 3) == 0) {
1283 /* We asked for a password and obviously got it :-) */
1285 opt_password = SMB_STRNDUP((const char *)request.data,
1286 request.length);
1288 if (opt_password == NULL) {
1289 DEBUG(1, ("Out of memory\n"));
1290 x_fprintf(x_stdout, "BH Out of memory\n");
1291 data_blob_free(&request);
1292 return;
1295 x_fprintf(x_stdout, "OK\n");
1296 data_blob_free(&request);
1297 return;
1300 if (!state->ntlmssp_state && use_cached_creds) {
1301 /* check whether cached credentials are usable. */
1302 DATA_BLOB empty_blob = data_blob_null;
1304 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
1305 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1306 /* failed to use cached creds */
1307 use_cached_creds = False;
1311 if (opt_password == NULL && !use_cached_creds) {
1312 /* Request a password from the calling process. After
1313 sending it, the calling process should retry asking for the
1314 negotiate. */
1316 DEBUG(10, ("Requesting password\n"));
1317 x_fprintf(x_stdout, "PW\n");
1318 return;
1321 if (strncmp(buf, "YR", 2) == 0) {
1322 TALLOC_FREE(state->ntlmssp_state);
1323 state->cli_state = CLIENT_INITIAL;
1324 } else if (strncmp(buf, "TT", 2) == 0) {
1325 /* No special preprocessing required */
1326 } else if (strncmp(buf, "GF", 2) == 0) {
1327 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
1329 if(state->cli_state == CLIENT_FINISHED) {
1330 x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
1332 else {
1333 x_fprintf(x_stdout, "BH\n");
1336 data_blob_free(&request);
1337 return;
1338 } else if (strncmp(buf, "GK", 2) == 0 ) {
1339 DEBUG(10, ("Requested session key\n"));
1341 if(state->cli_state == CLIENT_FINISHED) {
1342 char *key64 = base64_encode_data_blob(state->mem_ctx,
1343 state->session_key);
1344 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
1345 TALLOC_FREE(key64);
1347 else {
1348 x_fprintf(x_stdout, "BH\n");
1351 data_blob_free(&request);
1352 return;
1353 } else {
1354 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1355 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1356 return;
1359 if (!state->ntlmssp_state) {
1360 nt_status = ntlm_auth_start_ntlmssp_client(
1361 &state->ntlmssp_state);
1362 if (!NT_STATUS_IS_OK(nt_status)) {
1363 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1364 return;
1366 ntlmssp_want_feature_list(state->ntlmssp_state,
1367 state->want_feature_list);
1368 state->initial_message = data_blob_null;
1371 DEBUG(10, ("got NTLMSSP packet:\n"));
1372 dump_data(10, request.data, request.length);
1374 if (use_cached_creds && !opt_password &&
1375 (state->cli_state == CLIENT_RESPONSE)) {
1376 nt_status = do_ccache_ntlm_auth(state->initial_message, request,
1377 &reply);
1378 } else {
1379 nt_status = ntlmssp_update(state->ntlmssp_state, request,
1380 &reply);
1383 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1384 char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
1385 reply);
1386 if (state->cli_state == CLIENT_INITIAL) {
1387 x_fprintf(x_stdout, "YR %s\n", reply_base64);
1388 state->initial_message = reply;
1389 state->cli_state = CLIENT_RESPONSE;
1390 } else {
1391 x_fprintf(x_stdout, "KK %s\n", reply_base64);
1392 data_blob_free(&reply);
1394 TALLOC_FREE(reply_base64);
1395 DEBUG(10, ("NTLMSSP challenge\n"));
1396 } else if (NT_STATUS_IS_OK(nt_status)) {
1397 char *reply_base64 = base64_encode_data_blob(talloc_tos(),
1398 reply);
1399 x_fprintf(x_stdout, "AF %s\n", reply_base64);
1400 TALLOC_FREE(reply_base64);
1402 if(state->have_session_key)
1403 data_blob_free(&state->session_key);
1405 state->session_key = data_blob(
1406 state->ntlmssp_state->session_key.data,
1407 state->ntlmssp_state->session_key.length);
1408 state->neg_flags = state->ntlmssp_state->neg_flags;
1409 state->have_session_key = true;
1411 DEBUG(10, ("NTLMSSP OK!\n"));
1412 state->cli_state = CLIENT_FINISHED;
1413 TALLOC_FREE(state->ntlmssp_state);
1414 } else {
1415 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1416 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
1417 state->cli_state = CLIENT_ERROR;
1418 TALLOC_FREE(state->ntlmssp_state);
1421 data_blob_free(&request);
1424 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1425 struct loadparm_context *lp_ctx,
1426 struct ntlm_auth_state *state,
1427 char *buf, int length, void **private2)
1429 char *user, *pass;
1430 user=buf;
1432 pass=(char *)memchr(buf,' ',length);
1433 if (!pass) {
1434 DEBUG(2, ("Password not found. Denying access\n"));
1435 x_fprintf(x_stdout, "ERR\n");
1436 return;
1438 *pass='\0';
1439 pass++;
1441 if (state->helper_mode == SQUID_2_5_BASIC) {
1442 rfc1738_unescape(user);
1443 rfc1738_unescape(pass);
1446 if (check_plaintext_auth(user, pass, False)) {
1447 x_fprintf(x_stdout, "OK\n");
1448 } else {
1449 x_fprintf(x_stdout, "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 if (*private1) {
1478 state = (struct gensec_ntlm_state *)*private1;
1479 } else {
1480 state = talloc_zero(NULL, struct gensec_ntlm_state);
1481 if (!state) {
1482 x_fprintf(x_stdout, "BH No Memory\n");
1483 exit(1);
1485 *private1 = state;
1486 if (opt_password) {
1487 state->set_password = opt_password;
1491 if (strlen(buf) < 2) {
1492 DEBUG(1, ("query [%s] invalid", buf));
1493 x_fprintf(x_stdout, "BH Query invalid\n");
1494 return;
1497 if (strlen(buf) > 3) {
1498 if(strncmp(buf, "SF ", 3) == 0) {
1499 DEBUG(10, ("Setting flags to negotiate\n"));
1500 talloc_free(want_feature_list);
1501 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1502 x_fprintf(x_stdout, "OK\n");
1503 return;
1505 in = base64_decode_data_blob(buf + 3);
1506 } else {
1507 in = data_blob(NULL, 0);
1510 if (strncmp(buf, "YR", 2) == 0) {
1511 if (state->gensec_state) {
1512 talloc_free(state->gensec_state);
1513 state->gensec_state = NULL;
1515 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1516 /* Just return BH, like ntlm_auth from Samba 3 does. */
1517 x_fprintf(x_stdout, "BH Command expected\n");
1518 data_blob_free(&in);
1519 return;
1520 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1521 (strncmp(buf, "KK ", 3) != 0) &&
1522 (strncmp(buf, "AF ", 3) != 0) &&
1523 (strncmp(buf, "NA ", 3) != 0) &&
1524 (strncmp(buf, "UG", 2) != 0) &&
1525 (strncmp(buf, "PW ", 3) != 0) &&
1526 (strncmp(buf, "GK", 2) != 0) &&
1527 (strncmp(buf, "GF", 2) != 0)) {
1528 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1529 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1530 data_blob_free(&in);
1531 return;
1534 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1536 /* setup gensec */
1537 if (!(state->gensec_state)) {
1538 switch (stdio_helper_mode) {
1539 case GSS_SPNEGO_CLIENT:
1540 case NTLMSSP_CLIENT_1:
1541 /* setup the client side */
1543 nt_status = gensec_client_start(NULL, &state->gensec_state,
1544 lpcfg_gensec_settings(NULL, lp_ctx));
1545 if (!NT_STATUS_IS_OK(nt_status)) {
1546 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1547 talloc_free(mem_ctx);
1548 return;
1551 creds = cli_credentials_init(state->gensec_state);
1552 cli_credentials_set_conf(creds, lp_ctx);
1553 if (opt_username) {
1554 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1556 if (opt_domain) {
1557 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1559 if (state->set_password) {
1560 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1561 } else {
1562 cli_credentials_set_password_callback(creds, get_password);
1564 if (opt_workstation) {
1565 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1568 gensec_set_credentials(state->gensec_state, creds);
1570 break;
1571 case GSS_SPNEGO_SERVER:
1572 case SQUID_2_5_NTLMSSP:
1574 nt_status = ntlm_auth_start_ntlmssp_server(state, lp_ctx,
1575 &state->gensec_state);
1576 if (!NT_STATUS_IS_OK(nt_status)) {
1577 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1578 talloc_free(mem_ctx);
1579 return;
1581 break;
1583 default:
1584 talloc_free(mem_ctx);
1585 abort();
1588 gensec_want_feature_list(state->gensec_state, want_feature_list);
1590 switch (stdio_helper_mode) {
1591 case GSS_SPNEGO_CLIENT:
1592 case GSS_SPNEGO_SERVER:
1593 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1594 if (!in.length) {
1595 first = true;
1597 break;
1598 case NTLMSSP_CLIENT_1:
1599 if (!in.length) {
1600 first = true;
1602 /* fall through */
1603 case SQUID_2_5_NTLMSSP:
1604 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1605 break;
1606 default:
1607 talloc_free(mem_ctx);
1608 abort();
1611 if (!NT_STATUS_IS_OK(nt_status)) {
1612 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1613 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1614 talloc_free(mem_ctx);
1615 return;
1620 /* update */
1622 if (strncmp(buf, "PW ", 3) == 0) {
1623 state->set_password = talloc_strndup(state,
1624 (const char *)in.data,
1625 in.length);
1627 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1628 state->set_password,
1629 CRED_SPECIFIED);
1630 x_fprintf(x_stdout, "OK\n");
1631 data_blob_free(&in);
1632 talloc_free(mem_ctx);
1633 return;
1636 if (strncmp(buf, "GK", 2) == 0) {
1637 char *base64_key;
1638 DEBUG(10, ("Requested session key\n"));
1639 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1640 if(!NT_STATUS_IS_OK(nt_status)) {
1641 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1642 x_fprintf(x_stdout, "BH No session key\n");
1643 talloc_free(mem_ctx);
1644 return;
1645 } else {
1646 base64_key = base64_encode_data_blob(state, session_key);
1647 x_fprintf(x_stdout, "GK %s\n", base64_key);
1648 talloc_free(base64_key);
1650 talloc_free(mem_ctx);
1651 return;
1654 if (stdio_helper_mode == SQUID_2_5_NTLMSSP && strncmp(buf, "GF", 2) == 0) {
1655 uint32_t neg_flags;
1657 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1659 DEBUG(10, ("Requested negotiated feature flags\n"));
1660 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1661 return;
1664 nt_status = gensec_update(state->gensec_state, mem_ctx, NULL, in, &out);
1666 /* don't leak 'bad password'/'no such user' info to the network client */
1667 nt_status = nt_status_squash(nt_status);
1669 if (out.length) {
1670 out_base64 = base64_encode_data_blob(mem_ctx, out);
1671 } else {
1672 out_base64 = NULL;
1675 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1676 reply_arg = "*";
1677 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1678 reply_code = "YR";
1679 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1680 reply_code = "KK";
1681 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1682 reply_code = "TT";
1683 } else {
1684 abort();
1688 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1689 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1690 reply_arg = nt_errstr(nt_status);
1691 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1692 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1693 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1694 reply_arg = nt_errstr(nt_status);
1695 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1696 } else if (!NT_STATUS_IS_OK(nt_status)) {
1697 reply_code = "NA";
1698 reply_arg = nt_errstr(nt_status);
1699 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1700 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1701 struct auth_session_info *session_info;
1703 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1704 if (!NT_STATUS_IS_OK(nt_status)) {
1705 reply_code = "BH Failed to retrive session info";
1706 reply_arg = nt_errstr(nt_status);
1707 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1708 } else {
1710 reply_code = "AF";
1711 reply_arg = session_info->unix_info->unix_name;
1712 talloc_free(session_info);
1714 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1715 reply_code = "AF";
1716 reply_arg = out_base64;
1717 } else {
1718 abort();
1721 switch (stdio_helper_mode) {
1722 case GSS_SPNEGO_SERVER:
1723 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1724 out_base64 ? out_base64 : "*",
1725 reply_arg ? reply_arg : "*");
1726 break;
1727 default:
1728 if (out_base64) {
1729 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1730 } else if (reply_arg) {
1731 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1732 } else {
1733 x_fprintf(x_stdout, "%s\n", reply_code);
1737 talloc_free(mem_ctx);
1738 return;
1741 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1742 struct loadparm_context *lp_ctx,
1743 struct ntlm_auth_state *state,
1744 char *buf, int length, void **private2)
1746 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1747 return;
1750 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1751 struct loadparm_context *lp_ctx,
1752 struct ntlm_auth_state *state,
1753 char *buf, int length, void **private2)
1755 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1756 return;
1759 static struct ntlmssp_state *client_ntlmssp_state = NULL;
1761 static bool manage_client_ntlmssp_init(struct spnego_data spnego)
1763 NTSTATUS status;
1764 DATA_BLOB null_blob = data_blob_null;
1765 DATA_BLOB to_server;
1766 char *to_server_base64;
1767 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1768 TALLOC_CTX *ctx = talloc_tos();
1770 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1772 if (client_ntlmssp_state != NULL) {
1773 DEBUG(1, ("Request for initial SPNEGO request where "
1774 "we already have a state\n"));
1775 return False;
1778 if (!client_ntlmssp_state) {
1779 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1780 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1781 return False;
1786 if (opt_password == NULL) {
1788 /* Request a password from the calling process. After
1789 sending it, the calling process should retry with
1790 the negTokenInit. */
1792 DEBUG(10, ("Requesting password\n"));
1793 x_fprintf(x_stdout, "PW\n");
1794 return True;
1797 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1798 spnego.negTokenInit.mechTypes = my_mechs;
1799 spnego.negTokenInit.reqFlags = data_blob_null;
1800 spnego.negTokenInit.reqFlagsPadding = 0;
1801 spnego.negTokenInit.mechListMIC = null_blob;
1803 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1804 &spnego.negTokenInit.mechToken);
1806 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1807 NT_STATUS_IS_OK(status)) ) {
1808 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1809 nt_errstr(status)));
1810 TALLOC_FREE(client_ntlmssp_state);
1811 return False;
1814 spnego_write_data(ctx, &to_server, &spnego);
1815 data_blob_free(&spnego.negTokenInit.mechToken);
1817 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1818 data_blob_free(&to_server);
1819 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1820 TALLOC_FREE(to_server_base64);
1821 return True;
1824 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
1826 NTSTATUS status;
1827 DATA_BLOB null_blob = data_blob_null;
1828 DATA_BLOB request;
1829 DATA_BLOB to_server;
1830 char *to_server_base64;
1831 TALLOC_CTX *ctx = talloc_tos();
1833 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1835 if (client_ntlmssp_state == NULL) {
1836 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1837 x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
1838 return;
1841 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1842 x_fprintf(x_stdout, "NA\n");
1843 TALLOC_FREE(client_ntlmssp_state);
1844 return;
1847 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1848 x_fprintf(x_stdout, "AF\n");
1849 TALLOC_FREE(client_ntlmssp_state);
1850 return;
1853 status = ntlmssp_update(client_ntlmssp_state,
1854 spnego.negTokenTarg.responseToken,
1855 &request);
1857 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
1858 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED or OK from "
1859 "ntlmssp_client_update, got: %s\n",
1860 nt_errstr(status)));
1861 x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
1862 "ntlmssp_client_update\n");
1863 data_blob_free(&request);
1864 TALLOC_FREE(client_ntlmssp_state);
1865 return;
1868 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1869 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1870 spnego.negTokenTarg.supportedMech = (const char *)OID_NTLMSSP;
1871 spnego.negTokenTarg.responseToken = request;
1872 spnego.negTokenTarg.mechListMIC = null_blob;
1874 spnego_write_data(ctx, &to_server, &spnego);
1875 data_blob_free(&request);
1877 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1878 data_blob_free(&to_server);
1879 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1880 TALLOC_FREE(to_server_base64);
1881 return;
1884 #ifdef HAVE_KRB5
1886 static bool manage_client_krb5_init(struct spnego_data spnego)
1888 char *principal;
1889 DATA_BLOB tkt, tkt_wrapped, to_server;
1890 DATA_BLOB session_key_krb5 = data_blob_null;
1891 struct spnego_data reply;
1892 char *reply_base64;
1893 int retval;
1895 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1896 ssize_t len;
1897 TALLOC_CTX *ctx = talloc_tos();
1899 principal = spnego.negTokenInit.targetPrincipal;
1901 /* We may not be allowed to use the server-supplied SPNEGO principal, or it may not have been supplied to us
1903 if (!lp_client_use_spnego_principal() || strequal(principal, ADS_IGNORE_PRINCIPAL)) {
1904 principal = NULL;
1907 if (principal == NULL &&
1908 opt_target_service && opt_target_hostname && !is_ipaddress(opt_target_hostname)) {
1909 DEBUG(3,("manage_client_krb5_init: using target "
1910 "hostname not SPNEGO principal\n"));
1912 principal = kerberos_get_principal_from_service_hostname(talloc_tos(),
1913 opt_target_service,
1914 opt_target_hostname);
1916 if (!principal) {
1917 return false;
1920 DEBUG(3,("manage_client_krb5_init: guessed "
1921 "server principal=%s\n",
1922 principal ? principal : "<null>"));
1925 if (principal == NULL) {
1926 DEBUG(3,("manage_client_krb5_init: could not guess server principal\n"));
1927 return false;
1930 retval = cli_krb5_get_ticket(ctx, principal, 0,
1931 &tkt, &session_key_krb5,
1932 0, NULL, NULL, NULL);
1933 if (retval) {
1934 char *user = NULL;
1936 /* Let's try to first get the TGT, for that we need a
1937 password. */
1939 if (opt_password == NULL) {
1940 DEBUG(10, ("Requesting password\n"));
1941 x_fprintf(x_stdout, "PW\n");
1942 return True;
1945 user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
1946 if (!user) {
1947 return false;
1950 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1951 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1952 return False;
1955 retval = cli_krb5_get_ticket(ctx, principal, 0,
1956 &tkt, &session_key_krb5,
1957 0, NULL, NULL, NULL);
1958 if (retval) {
1959 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1960 return False;
1965 /* wrap that up in a nice GSS-API wrapping */
1966 tkt_wrapped = spnego_gen_krb5_wrap(ctx, tkt, TOK_ID_KRB_AP_REQ);
1968 data_blob_free(&session_key_krb5);
1970 ZERO_STRUCT(reply);
1972 reply.type = SPNEGO_NEG_TOKEN_INIT;
1973 reply.negTokenInit.mechTypes = my_mechs;
1974 reply.negTokenInit.reqFlags = data_blob_null;
1975 reply.negTokenInit.reqFlagsPadding = 0;
1976 reply.negTokenInit.mechToken = tkt_wrapped;
1977 reply.negTokenInit.mechListMIC = data_blob_null;
1979 len = spnego_write_data(ctx, &to_server, &reply);
1980 data_blob_free(&tkt);
1982 if (len == -1) {
1983 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1984 return False;
1987 reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1988 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1990 TALLOC_FREE(reply_base64);
1991 data_blob_free(&to_server);
1992 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1993 return True;
1996 static void manage_client_krb5_targ(struct spnego_data spnego)
1998 switch (spnego.negTokenTarg.negResult) {
1999 case SPNEGO_ACCEPT_INCOMPLETE:
2000 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
2001 x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
2002 "ACCEPT_INCOMPLETE\n");
2003 break;
2004 case SPNEGO_ACCEPT_COMPLETED:
2005 DEBUG(10, ("Accept completed\n"));
2006 x_fprintf(x_stdout, "AF\n");
2007 break;
2008 case SPNEGO_REJECT:
2009 DEBUG(10, ("Rejected\n"));
2010 x_fprintf(x_stdout, "NA\n");
2011 break;
2012 default:
2013 DEBUG(1, ("Got an invalid negTokenTarg\n"));
2014 x_fprintf(x_stdout, "AF\n");
2018 #endif
2020 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
2021 struct loadparm_context *lp_ctx,
2022 struct ntlm_auth_state *state,
2023 char *buf, int length, void **private2)
2025 DATA_BLOB request;
2026 struct spnego_data spnego;
2027 ssize_t len;
2028 TALLOC_CTX *ctx = talloc_tos();
2030 if (!opt_username || !*opt_username) {
2031 x_fprintf(x_stderr, "username must be specified!\n\n");
2032 exit(1);
2035 if (strlen(buf) <= 3) {
2036 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
2037 x_fprintf(x_stdout, "BH SPNEGO query too short\n");
2038 return;
2041 request = base64_decode_data_blob(buf+3);
2043 if (strncmp(buf, "PW ", 3) == 0) {
2045 /* We asked for a password and obviously got it :-) */
2047 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
2049 if (opt_password == NULL) {
2050 DEBUG(1, ("Out of memory\n"));
2051 x_fprintf(x_stdout, "BH Out of memory\n");
2052 data_blob_free(&request);
2053 return;
2056 x_fprintf(x_stdout, "OK\n");
2057 data_blob_free(&request);
2058 return;
2061 if ( (strncmp(buf, "TT ", 3) != 0) &&
2062 (strncmp(buf, "AF ", 3) != 0) &&
2063 (strncmp(buf, "NA ", 3) != 0) ) {
2064 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
2065 x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
2066 data_blob_free(&request);
2067 return;
2070 /* So we got a server challenge to generate a SPNEGO
2071 client-to-server request... */
2073 len = spnego_read_data(ctx, request, &spnego);
2074 data_blob_free(&request);
2076 if (len == -1) {
2077 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
2078 x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
2079 return;
2082 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
2084 /* The server offers a list of mechanisms */
2086 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
2088 while (*mechType != NULL) {
2090 #ifdef HAVE_KRB5
2091 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
2092 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
2093 if (manage_client_krb5_init(spnego))
2094 goto out;
2096 #endif
2098 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
2099 if (manage_client_ntlmssp_init(spnego))
2100 goto out;
2103 mechType++;
2106 DEBUG(1, ("Server offered no compatible mechanism\n"));
2107 x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
2108 return;
2111 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
2113 if (spnego.negTokenTarg.supportedMech == NULL) {
2114 /* On accept/reject Windows does not send the
2115 mechanism anymore. Handle that here and
2116 shut down the mechanisms. */
2118 switch (spnego.negTokenTarg.negResult) {
2119 case SPNEGO_ACCEPT_COMPLETED:
2120 x_fprintf(x_stdout, "AF\n");
2121 break;
2122 case SPNEGO_REJECT:
2123 x_fprintf(x_stdout, "NA\n");
2124 break;
2125 default:
2126 DEBUG(1, ("Got a negTokenTarg with no mech and an "
2127 "unknown negResult: %d\n",
2128 spnego.negTokenTarg.negResult));
2129 x_fprintf(x_stdout, "BH Got a negTokenTarg with"
2130 " no mech and an unknown "
2131 "negResult\n");
2134 TALLOC_FREE(client_ntlmssp_state);
2135 goto out;
2138 if (strcmp(spnego.negTokenTarg.supportedMech,
2139 OID_NTLMSSP) == 0) {
2140 manage_client_ntlmssp_targ(spnego);
2141 goto out;
2144 #if HAVE_KRB5
2145 if (strcmp(spnego.negTokenTarg.supportedMech,
2146 OID_KERBEROS5_OLD) == 0) {
2147 manage_client_krb5_targ(spnego);
2148 goto out;
2150 #endif
2154 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
2155 x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
2156 return;
2158 out:
2159 spnego_free_data(&spnego);
2160 return;
2163 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
2164 struct loadparm_context *lp_ctx,
2165 struct ntlm_auth_state *state,
2166 char *buf, int length, void **private2)
2168 char *request, *parameter;
2169 static DATA_BLOB challenge;
2170 static DATA_BLOB lm_response;
2171 static DATA_BLOB nt_response;
2172 static char *full_username;
2173 static char *username;
2174 static char *domain;
2175 static char *plaintext_password;
2176 static bool ntlm_server_1_user_session_key;
2177 static bool ntlm_server_1_lm_session_key;
2179 if (strequal(buf, ".")) {
2180 if (!full_username && !username) {
2181 x_fprintf(x_stdout, "Error: No username supplied!\n");
2182 } else if (plaintext_password) {
2183 /* handle this request as plaintext */
2184 if (!full_username) {
2185 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
2186 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
2187 return;
2190 if (check_plaintext_auth(full_username, plaintext_password, False)) {
2191 x_fprintf(x_stdout, "Authenticated: Yes\n");
2192 } else {
2193 x_fprintf(x_stdout, "Authenticated: No\n");
2195 } else if (!lm_response.data && !nt_response.data) {
2196 x_fprintf(x_stdout, "Error: No password supplied!\n");
2197 } else if (!challenge.data) {
2198 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
2199 } else {
2200 char *error_string = NULL;
2201 uchar lm_key[8];
2202 uchar user_session_key[16];
2203 uint32 flags = 0;
2205 if (full_username && !username) {
2206 fstring fstr_user;
2207 fstring fstr_domain;
2209 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
2210 /* username might be 'tainted', don't print into our new-line deleimianted stream */
2211 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
2213 SAFE_FREE(username);
2214 SAFE_FREE(domain);
2215 username = smb_xstrdup(fstr_user);
2216 domain = smb_xstrdup(fstr_domain);
2219 if (!domain) {
2220 domain = smb_xstrdup(get_winbind_domain());
2223 if (ntlm_server_1_lm_session_key)
2224 flags |= WBFLAG_PAM_LMKEY;
2226 if (ntlm_server_1_user_session_key)
2227 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2229 if (!NT_STATUS_IS_OK(
2230 contact_winbind_auth_crap(username,
2231 domain,
2232 lp_netbios_name(),
2233 &challenge,
2234 &lm_response,
2235 &nt_response,
2236 flags, 0,
2237 lm_key,
2238 user_session_key,
2239 &error_string,
2240 NULL))) {
2242 x_fprintf(x_stdout, "Authenticated: No\n");
2243 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
2244 } else {
2245 static char zeros[16];
2246 char *hex_lm_key;
2247 char *hex_user_session_key;
2249 x_fprintf(x_stdout, "Authenticated: Yes\n");
2251 if (ntlm_server_1_lm_session_key
2252 && (memcmp(zeros, lm_key,
2253 sizeof(lm_key)) != 0)) {
2254 hex_lm_key = hex_encode_talloc(NULL,
2255 (const unsigned char *)lm_key,
2256 sizeof(lm_key));
2257 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
2258 TALLOC_FREE(hex_lm_key);
2261 if (ntlm_server_1_user_session_key
2262 && (memcmp(zeros, user_session_key,
2263 sizeof(user_session_key)) != 0)) {
2264 hex_user_session_key = hex_encode_talloc(NULL,
2265 (const unsigned char *)user_session_key,
2266 sizeof(user_session_key));
2267 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
2268 TALLOC_FREE(hex_user_session_key);
2271 SAFE_FREE(error_string);
2273 /* clear out the state */
2274 challenge = data_blob_null;
2275 nt_response = data_blob_null;
2276 lm_response = data_blob_null;
2277 SAFE_FREE(full_username);
2278 SAFE_FREE(username);
2279 SAFE_FREE(domain);
2280 SAFE_FREE(plaintext_password);
2281 ntlm_server_1_user_session_key = False;
2282 ntlm_server_1_lm_session_key = False;
2283 x_fprintf(x_stdout, ".\n");
2285 return;
2288 request = buf;
2290 /* Indicates a base64 encoded structure */
2291 parameter = strstr_m(request, ":: ");
2292 if (!parameter) {
2293 parameter = strstr_m(request, ": ");
2295 if (!parameter) {
2296 DEBUG(0, ("Parameter not found!\n"));
2297 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2298 return;
2301 parameter[0] ='\0';
2302 parameter++;
2303 parameter[0] ='\0';
2304 parameter++;
2306 } else {
2307 parameter[0] ='\0';
2308 parameter++;
2309 parameter[0] ='\0';
2310 parameter++;
2311 parameter[0] ='\0';
2312 parameter++;
2314 base64_decode_inplace(parameter);
2317 if (strequal(request, "LANMAN-Challenge")) {
2318 challenge = strhex_to_data_blob(NULL, parameter);
2319 if (challenge.length != 8) {
2320 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
2321 parameter,
2322 (int)challenge.length);
2323 challenge = data_blob_null;
2325 } else if (strequal(request, "NT-Response")) {
2326 nt_response = strhex_to_data_blob(NULL, parameter);
2327 if (nt_response.length < 24) {
2328 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
2329 parameter,
2330 (int)nt_response.length);
2331 nt_response = data_blob_null;
2333 } else if (strequal(request, "LANMAN-Response")) {
2334 lm_response = strhex_to_data_blob(NULL, parameter);
2335 if (lm_response.length != 24) {
2336 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
2337 parameter,
2338 (int)lm_response.length);
2339 lm_response = data_blob_null;
2341 } else if (strequal(request, "Password")) {
2342 plaintext_password = smb_xstrdup(parameter);
2343 } else if (strequal(request, "NT-Domain")) {
2344 domain = smb_xstrdup(parameter);
2345 } else if (strequal(request, "Username")) {
2346 username = smb_xstrdup(parameter);
2347 } else if (strequal(request, "Full-Username")) {
2348 full_username = smb_xstrdup(parameter);
2349 } else if (strequal(request, "Request-User-Session-Key")) {
2350 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2351 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2352 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2353 } else {
2354 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2358 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2359 struct loadparm_context *lp_ctx,
2360 struct ntlm_auth_state *state,
2361 char *buf, int length, void **private2)
2363 char *request, *parameter;
2364 static DATA_BLOB new_nt_pswd;
2365 static DATA_BLOB old_nt_hash_enc;
2366 static DATA_BLOB new_lm_pswd;
2367 static DATA_BLOB old_lm_hash_enc;
2368 static char *full_username = NULL;
2369 static char *username = NULL;
2370 static char *domain = NULL;
2371 static char *newpswd = NULL;
2372 static char *oldpswd = NULL;
2374 if (strequal(buf, ".")) {
2375 if(newpswd && oldpswd) {
2376 uchar old_nt_hash[16];
2377 uchar old_lm_hash[16];
2378 uchar new_nt_hash[16];
2379 uchar new_lm_hash[16];
2381 new_nt_pswd = data_blob(NULL, 516);
2382 old_nt_hash_enc = data_blob(NULL, 16);
2384 /* Calculate the MD4 hash (NT compatible) of the
2385 * password */
2386 E_md4hash(oldpswd, old_nt_hash);
2387 E_md4hash(newpswd, new_nt_hash);
2389 /* E_deshash returns false for 'long'
2390 passwords (> 14 DOS chars).
2392 Therefore, don't send a buffer
2393 encrypted with the truncated hash
2394 (it could allow an even easier
2395 attack on the password)
2397 Likewise, obey the admin's restriction
2400 if (lp_client_lanman_auth() &&
2401 E_deshash(newpswd, new_lm_hash) &&
2402 E_deshash(oldpswd, old_lm_hash)) {
2403 new_lm_pswd = data_blob(NULL, 516);
2404 old_lm_hash_enc = data_blob(NULL, 16);
2405 encode_pw_buffer(new_lm_pswd.data, newpswd,
2406 STR_UNICODE);
2408 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
2409 E_old_pw_hash(new_nt_hash, old_lm_hash,
2410 old_lm_hash_enc.data);
2411 } else {
2412 new_lm_pswd.data = NULL;
2413 new_lm_pswd.length = 0;
2414 old_lm_hash_enc.data = NULL;
2415 old_lm_hash_enc.length = 0;
2418 encode_pw_buffer(new_nt_pswd.data, newpswd,
2419 STR_UNICODE);
2421 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
2422 E_old_pw_hash(new_nt_hash, old_nt_hash,
2423 old_nt_hash_enc.data);
2426 if (!full_username && !username) {
2427 x_fprintf(x_stdout, "Error: No username supplied!\n");
2428 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2429 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2430 x_fprintf(x_stdout, "Error: No NT or LM password "
2431 "blobs supplied!\n");
2432 } else {
2433 char *error_string = NULL;
2435 if (full_username && !username) {
2436 fstring fstr_user;
2437 fstring fstr_domain;
2439 if (!parse_ntlm_auth_domain_user(full_username,
2440 fstr_user,
2441 fstr_domain)) {
2442 /* username might be 'tainted', don't
2443 * print into our new-line
2444 * deleimianted stream */
2445 x_fprintf(x_stdout, "Error: Could not "
2446 "parse into domain and "
2447 "username\n");
2448 SAFE_FREE(username);
2449 username = smb_xstrdup(full_username);
2450 } else {
2451 SAFE_FREE(username);
2452 SAFE_FREE(domain);
2453 username = smb_xstrdup(fstr_user);
2454 domain = smb_xstrdup(fstr_domain);
2459 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2460 username, domain,
2461 new_nt_pswd,
2462 old_nt_hash_enc,
2463 new_lm_pswd,
2464 old_lm_hash_enc,
2465 &error_string))) {
2466 x_fprintf(x_stdout, "Password-Change: No\n");
2467 x_fprintf(x_stdout, "Password-Change-Error: "
2468 "%s\n.\n", error_string);
2469 } else {
2470 x_fprintf(x_stdout, "Password-Change: Yes\n");
2473 SAFE_FREE(error_string);
2475 /* clear out the state */
2476 new_nt_pswd = data_blob_null;
2477 old_nt_hash_enc = data_blob_null;
2478 new_lm_pswd = data_blob_null;
2479 old_nt_hash_enc = data_blob_null;
2480 SAFE_FREE(full_username);
2481 SAFE_FREE(username);
2482 SAFE_FREE(domain);
2483 SAFE_FREE(newpswd);
2484 SAFE_FREE(oldpswd);
2485 x_fprintf(x_stdout, ".\n");
2487 return;
2490 request = buf;
2492 /* Indicates a base64 encoded structure */
2493 parameter = strstr_m(request, ":: ");
2494 if (!parameter) {
2495 parameter = strstr_m(request, ": ");
2497 if (!parameter) {
2498 DEBUG(0, ("Parameter not found!\n"));
2499 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2500 return;
2503 parameter[0] ='\0';
2504 parameter++;
2505 parameter[0] ='\0';
2506 parameter++;
2507 } else {
2508 parameter[0] ='\0';
2509 parameter++;
2510 parameter[0] ='\0';
2511 parameter++;
2512 parameter[0] ='\0';
2513 parameter++;
2515 base64_decode_inplace(parameter);
2518 if (strequal(request, "new-nt-password-blob")) {
2519 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2520 if (new_nt_pswd.length != 516) {
2521 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2522 "(got %d bytes, expected 516)\n.\n",
2523 parameter,
2524 (int)new_nt_pswd.length);
2525 new_nt_pswd = data_blob_null;
2527 } else if (strequal(request, "old-nt-hash-blob")) {
2528 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2529 if (old_nt_hash_enc.length != 16) {
2530 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2531 "(got %d bytes, expected 16)\n.\n",
2532 parameter,
2533 (int)old_nt_hash_enc.length);
2534 old_nt_hash_enc = data_blob_null;
2536 } else if (strequal(request, "new-lm-password-blob")) {
2537 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2538 if (new_lm_pswd.length != 516) {
2539 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2540 "(got %d bytes, expected 516)\n.\n",
2541 parameter,
2542 (int)new_lm_pswd.length);
2543 new_lm_pswd = data_blob_null;
2546 else if (strequal(request, "old-lm-hash-blob")) {
2547 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2548 if (old_lm_hash_enc.length != 16)
2550 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2551 "(got %d bytes, expected 16)\n.\n",
2552 parameter,
2553 (int)old_lm_hash_enc.length);
2554 old_lm_hash_enc = data_blob_null;
2556 } else if (strequal(request, "nt-domain")) {
2557 domain = smb_xstrdup(parameter);
2558 } else if(strequal(request, "username")) {
2559 username = smb_xstrdup(parameter);
2560 } else if(strequal(request, "full-username")) {
2561 username = smb_xstrdup(parameter);
2562 } else if(strequal(request, "new-password")) {
2563 newpswd = smb_xstrdup(parameter);
2564 } else if (strequal(request, "old-password")) {
2565 oldpswd = smb_xstrdup(parameter);
2566 } else {
2567 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2571 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2572 struct loadparm_context *lp_ctx,
2573 struct ntlm_auth_state *state,
2574 stdio_helper_function fn, void **private2)
2576 char *buf;
2577 char tmp[INITIAL_BUFFER_SIZE+1];
2578 int length, buf_size = 0;
2579 char *c;
2581 buf = talloc_strdup(state->mem_ctx, "");
2582 if (!buf) {
2583 DEBUG(0, ("Failed to allocate input buffer.\n"));
2584 x_fprintf(x_stderr, "ERR\n");
2585 exit(1);
2588 do {
2590 /* this is not a typo - x_fgets doesn't work too well under
2591 * squid */
2592 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2593 if (ferror(stdin)) {
2594 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2595 "(%s)\n", ferror(stdin),
2596 strerror(ferror(stdin))));
2598 exit(1);
2600 exit(0);
2603 buf = talloc_strdup_append_buffer(buf, tmp);
2604 buf_size += INITIAL_BUFFER_SIZE;
2606 if (buf_size > MAX_BUFFER_SIZE) {
2607 DEBUG(2, ("Oversized message\n"));
2608 x_fprintf(x_stderr, "ERR\n");
2609 talloc_free(buf);
2610 return;
2613 c = strchr(buf, '\n');
2614 } while (c == NULL);
2616 *c = '\0';
2617 length = c-buf;
2619 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2621 if (buf[0] == '\0') {
2622 DEBUG(2, ("Invalid Request\n"));
2623 x_fprintf(x_stderr, "ERR\n");
2624 talloc_free(buf);
2625 return;
2628 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2629 talloc_free(buf);
2633 static void squid_stream(enum stdio_helper_mode stdio_mode,
2634 struct loadparm_context *lp_ctx,
2635 stdio_helper_function fn) {
2636 TALLOC_CTX *mem_ctx;
2637 struct ntlm_auth_state *state;
2639 /* initialize FDescs */
2640 x_setbuf(x_stdout, NULL);
2641 x_setbuf(x_stderr, NULL);
2643 mem_ctx = talloc_init("ntlm_auth");
2644 if (!mem_ctx) {
2645 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2646 x_fprintf(x_stderr, "ERR\n");
2647 exit(1);
2650 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2651 if (!state) {
2652 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2653 x_fprintf(x_stderr, "ERR\n");
2654 exit(1);
2657 state->mem_ctx = mem_ctx;
2658 state->helper_mode = stdio_mode;
2660 while(1) {
2661 TALLOC_CTX *frame = talloc_stackframe();
2662 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2663 TALLOC_FREE(frame);
2668 /* Authenticate a user with a challenge/response */
2670 static bool check_auth_crap(void)
2672 NTSTATUS nt_status;
2673 uint32 flags = 0;
2674 char lm_key[8];
2675 char user_session_key[16];
2676 char *hex_lm_key;
2677 char *hex_user_session_key;
2678 char *error_string;
2679 static uint8 zeros[16];
2681 x_setbuf(x_stdout, NULL);
2683 if (request_lm_key)
2684 flags |= WBFLAG_PAM_LMKEY;
2686 if (request_user_session_key)
2687 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2689 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2691 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2692 opt_workstation,
2693 &opt_challenge,
2694 &opt_lm_response,
2695 &opt_nt_response,
2696 flags, 0,
2697 (unsigned char *)lm_key,
2698 (unsigned char *)user_session_key,
2699 &error_string, NULL);
2701 if (!NT_STATUS_IS_OK(nt_status)) {
2702 x_fprintf(x_stdout, "%s (0x%x)\n",
2703 error_string,
2704 NT_STATUS_V(nt_status));
2705 SAFE_FREE(error_string);
2706 return False;
2709 if (request_lm_key
2710 && (memcmp(zeros, lm_key,
2711 sizeof(lm_key)) != 0)) {
2712 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2713 sizeof(lm_key));
2714 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2715 TALLOC_FREE(hex_lm_key);
2717 if (request_user_session_key
2718 && (memcmp(zeros, user_session_key,
2719 sizeof(user_session_key)) != 0)) {
2720 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2721 sizeof(user_session_key));
2722 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2723 TALLOC_FREE(hex_user_session_key);
2726 return True;
2729 /* Main program */
2731 enum {
2732 OPT_USERNAME = 1000,
2733 OPT_DOMAIN,
2734 OPT_WORKSTATION,
2735 OPT_CHALLENGE,
2736 OPT_RESPONSE,
2737 OPT_LM,
2738 OPT_NT,
2739 OPT_PASSWORD,
2740 OPT_LM_KEY,
2741 OPT_USER_SESSION_KEY,
2742 OPT_DIAGNOSTICS,
2743 OPT_REQUIRE_MEMBERSHIP,
2744 OPT_USE_CACHED_CREDS,
2745 OPT_PAM_WINBIND_CONF,
2746 OPT_TARGET_SERVICE,
2747 OPT_TARGET_HOSTNAME
2750 int main(int argc, const char **argv)
2752 TALLOC_CTX *frame = talloc_stackframe();
2753 int opt;
2754 static const char *helper_protocol;
2755 static int diagnostics;
2757 static const char *hex_challenge;
2758 static const char *hex_lm_response;
2759 static const char *hex_nt_response;
2760 struct loadparm_context *lp_ctx;
2761 poptContext pc;
2763 /* NOTE: DO NOT change this interface without considering the implications!
2764 This is an external interface, which other programs will use to interact
2765 with this helper.
2768 /* We do not use single-letter command abbreviations, because they harm future
2769 interface stability. */
2771 struct poptOption long_options[] = {
2772 POPT_AUTOHELP
2773 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2774 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2775 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2776 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2777 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2778 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2779 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2780 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2781 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2782 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2783 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2784 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2785 OPT_DIAGNOSTICS,
2786 "Perform diagnostics on the authentication chain"},
2787 { "require-membership-of", 0, POPT_ARG_STRING, &require_membership_of, OPT_REQUIRE_MEMBERSHIP, "Require that a user be a member of this group (either name or SID) for authentication to succeed" },
2788 { "pam-winbind-conf", 0, POPT_ARG_STRING, &opt_pam_winbind_conf, OPT_PAM_WINBIND_CONF, "Require that request must set WBFLAG_PAM_CONTACT_TRUSTDOM when krb5 auth is required" },
2789 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2790 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2791 POPT_COMMON_CONFIGFILE
2792 POPT_COMMON_VERSION
2793 POPT_TABLEEND
2796 /* Samba client initialisation */
2797 load_case_tables();
2799 setup_logging("ntlm_auth", DEBUG_STDERR);
2801 /* Parse options */
2803 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2805 /* Parse command line options */
2807 if (argc == 1) {
2808 poptPrintHelp(pc, stderr, 0);
2809 return 1;
2812 while((opt = poptGetNextOpt(pc)) != -1) {
2813 /* Get generic config options like --configfile */
2816 poptFreeContext(pc);
2818 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2819 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2820 get_dyn_CONFIGFILE(), strerror(errno));
2821 exit(1);
2824 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2825 POPT_CONTEXT_KEEP_FIRST);
2827 while((opt = poptGetNextOpt(pc)) != -1) {
2828 switch (opt) {
2829 case OPT_CHALLENGE:
2830 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2831 if (opt_challenge.length != 8) {
2832 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2833 hex_challenge,
2834 (int)opt_challenge.length);
2835 exit(1);
2837 break;
2838 case OPT_LM:
2839 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2840 if (opt_lm_response.length != 24) {
2841 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2842 hex_lm_response,
2843 (int)opt_lm_response.length);
2844 exit(1);
2846 break;
2848 case OPT_NT:
2849 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2850 if (opt_nt_response.length < 24) {
2851 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2852 hex_nt_response,
2853 (int)opt_nt_response.length);
2854 exit(1);
2856 break;
2858 case OPT_REQUIRE_MEMBERSHIP:
2859 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2860 require_membership_of_sid = require_membership_of;
2862 break;
2866 if (opt_username) {
2867 char *domain = SMB_STRDUP(opt_username);
2868 char *p = strchr_m(domain, *lp_winbind_separator());
2869 if (p) {
2870 opt_username = p+1;
2871 *p = '\0';
2872 if (opt_domain && !strequal(opt_domain, domain)) {
2873 x_fprintf(x_stderr, "Domain specified in username (%s) "
2874 "doesn't match specified domain (%s)!\n\n",
2875 domain, opt_domain);
2876 poptPrintHelp(pc, stderr, 0);
2877 exit(1);
2879 opt_domain = domain;
2880 } else {
2881 SAFE_FREE(domain);
2885 /* Note: if opt_domain is "" then send no domain */
2886 if (opt_domain == NULL) {
2887 opt_domain = get_winbind_domain();
2890 if (opt_workstation == NULL) {
2891 opt_workstation = "";
2894 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_context());
2895 if (lp_ctx == NULL) {
2896 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2897 exit(1);
2900 if (helper_protocol) {
2901 int i;
2902 for (i=0; i<NUM_HELPER_MODES; i++) {
2903 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2904 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2905 exit(0);
2908 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2910 for (i=0; i<NUM_HELPER_MODES; i++) {
2911 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2914 exit(1);
2917 if (!opt_username || !*opt_username) {
2918 x_fprintf(x_stderr, "username must be specified!\n\n");
2919 poptPrintHelp(pc, stderr, 0);
2920 exit(1);
2923 if (opt_challenge.length) {
2924 if (!check_auth_crap()) {
2925 exit(1);
2927 exit(0);
2930 if (!opt_password) {
2931 opt_password = getpass("password: ");
2934 if (diagnostics) {
2935 if (!diagnose_ntlm_auth()) {
2936 return 1;
2938 } else {
2939 fstring user;
2941 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2942 if (!check_plaintext_auth(user, opt_password, True)) {
2943 return 1;
2947 /* Exit code */
2949 poptFreeContext(pc);
2950 TALLOC_FREE(frame);
2951 return 0;