auth: Common function for retrieving PAC_LOGIN_INFO from PAC
[Samba/gebeck_regimport.git] / source3 / utils / ntlm_auth.c
blobafb51e9356b1debbd2afb62c76ce53d7739945f9
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 #if HAVE_KRB5
50 #include "auth/kerberos/pac_utils.h"
51 #endif
53 #ifndef PAM_WINBIND_CONFIG_FILE
54 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
55 #endif
57 #define WINBIND_KRB5_AUTH 0x00000080
59 #undef DBGC_CLASS
60 #define DBGC_CLASS DBGC_WINBIND
62 #define INITIAL_BUFFER_SIZE 300
63 #define MAX_BUFFER_SIZE 630000
65 enum stdio_helper_mode {
66 SQUID_2_4_BASIC,
67 SQUID_2_5_BASIC,
68 SQUID_2_5_NTLMSSP,
69 NTLMSSP_CLIENT_1,
70 GSS_SPNEGO_SERVER,
71 GSS_SPNEGO_CLIENT,
72 NTLM_SERVER_1,
73 NTLM_CHANGE_PASSWORD_1,
74 NUM_HELPER_MODES
77 enum ntlm_auth_cli_state {
78 CLIENT_INITIAL = 0,
79 CLIENT_RESPONSE,
80 CLIENT_FINISHED,
81 CLIENT_ERROR
84 struct ntlm_auth_state {
85 TALLOC_CTX *mem_ctx;
86 enum stdio_helper_mode helper_mode;
87 enum ntlm_auth_cli_state cli_state;
88 struct ntlmssp_state *ntlmssp_state;
89 uint32_t neg_flags;
90 char *want_feature_list;
91 bool have_session_key;
92 DATA_BLOB session_key;
93 DATA_BLOB initial_message;
94 void *gensec_private_1;
96 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
97 struct loadparm_context *lp_ctx,
98 struct ntlm_auth_state *state, char *buf,
99 int length, void **private2);
101 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
102 struct loadparm_context *lp_ctx,
103 struct ntlm_auth_state *state,
104 stdio_helper_function fn, void **private2);
106 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
107 struct loadparm_context *lp_ctx,
108 struct ntlm_auth_state *state,
109 char *buf, int length, void **private2);
111 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
112 struct loadparm_context *lp_ctx,
113 struct ntlm_auth_state *state,
114 char *buf, int length, void **private2);
116 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
117 struct loadparm_context *lp_ctx,
118 struct ntlm_auth_state *state,
119 char *buf, int length, void **private2);
121 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
122 struct loadparm_context *lp_ctx,
123 struct ntlm_auth_state *state,
124 char *buf, int length, void **private2);
126 static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
127 struct loadparm_context *lp_ctx,
128 struct ntlm_auth_state *state,
129 char *buf, int length, void **private2);
131 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
132 struct loadparm_context *lp_ctx,
133 struct ntlm_auth_state *state,
134 char *buf, int length, void **private2);
136 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
137 struct loadparm_context *lp_ctx,
138 struct ntlm_auth_state *state,
139 char *buf, int length, void **private2);
141 static const struct {
142 enum stdio_helper_mode mode;
143 const char *name;
144 stdio_helper_function fn;
145 } stdio_helper_protocols[] = {
146 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
147 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
148 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
149 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
150 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
151 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
152 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
153 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
154 { NUM_HELPER_MODES, NULL, NULL}
157 const char *opt_username;
158 const char *opt_domain;
159 const char *opt_workstation;
160 const char *opt_password;
161 static DATA_BLOB opt_challenge;
162 static DATA_BLOB opt_lm_response;
163 static DATA_BLOB opt_nt_response;
164 static int request_lm_key;
165 static int request_user_session_key;
166 static int use_cached_creds;
168 static const char *require_membership_of;
169 static const char *require_membership_of_sid;
170 static const char *opt_pam_winbind_conf;
172 const char *opt_target_service;
173 const char *opt_target_hostname;
176 /* This is a bit hairy, but the basic idea is to do a password callback
177 to the calling application. The callback comes from within gensec */
179 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
180 struct loadparm_context *lp_ctx,
181 struct ntlm_auth_state *state, char *buf, int length,
182 void **password)
184 DATA_BLOB in;
185 if (strlen(buf) < 2) {
186 DEBUG(1, ("query [%s] invalid", buf));
187 x_fprintf(x_stdout, "BH Query invalid\n");
188 return;
191 if (strlen(buf) > 3) {
192 in = base64_decode_data_blob(buf + 3);
193 } else {
194 in = data_blob(NULL, 0);
197 if (strncmp(buf, "PW ", 3) == 0) {
199 *password = talloc_strndup(NULL,
200 (const char *)in.data, in.length);
202 if (*password == NULL) {
203 DEBUG(1, ("Out of memory\n"));
204 x_fprintf(x_stdout, "BH Out of memory\n");
205 data_blob_free(&in);
206 return;
209 x_fprintf(x_stdout, "OK\n");
210 data_blob_free(&in);
211 return;
213 DEBUG(1, ("Asked for (and expected) a password\n"));
214 x_fprintf(x_stdout, "BH Expected a password\n");
215 data_blob_free(&in);
219 * Callback for password credentials. This is not async, and when
220 * GENSEC and the credentials code is made async, it will look rather
221 * different.
224 static const char *get_password(struct cli_credentials *credentials)
226 char *password = NULL;
228 /* Ask for a password */
229 x_fprintf(x_stdout, "PW\n");
230 credentials->priv_data = NULL;
232 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, NULL, manage_gensec_get_pw_request, (void **)&password);
233 talloc_steal(credentials, password);
234 return password;
238 * A limited set of features are defined with text strings as needed
239 * by ntlm_auth
242 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
244 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
245 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
246 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
248 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
249 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
250 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
252 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
253 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
254 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
258 static char winbind_separator(void)
260 struct winbindd_response response;
261 static bool got_sep;
262 static char sep;
264 if (got_sep)
265 return sep;
267 ZERO_STRUCT(response);
269 /* Send off request */
271 if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
272 NSS_STATUS_SUCCESS) {
273 d_printf("could not obtain winbind separator!\n");
274 return *lp_winbind_separator();
277 sep = response.data.info.winbind_separator;
278 got_sep = True;
280 if (!sep) {
281 d_printf("winbind separator was NULL!\n");
282 return *lp_winbind_separator();
285 return sep;
288 const char *get_winbind_domain(void)
290 struct winbindd_response response;
292 static fstring winbind_domain;
293 if (*winbind_domain) {
294 return winbind_domain;
297 ZERO_STRUCT(response);
299 /* Send off request */
301 if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
302 NSS_STATUS_SUCCESS) {
303 DEBUG(0, ("could not obtain winbind domain name!\n"));
304 return lp_workgroup();
307 fstrcpy(winbind_domain, response.data.domain_name);
309 return winbind_domain;
313 const char *get_winbind_netbios_name(void)
315 struct winbindd_response response;
317 static fstring winbind_netbios_name;
319 if (*winbind_netbios_name) {
320 return winbind_netbios_name;
323 ZERO_STRUCT(response);
325 /* Send off request */
327 if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) !=
328 NSS_STATUS_SUCCESS) {
329 DEBUG(0, ("could not obtain winbind netbios name!\n"));
330 return lp_netbios_name();
333 fstrcpy(winbind_netbios_name, response.data.netbios_name);
335 return winbind_netbios_name;
339 DATA_BLOB get_challenge(void)
341 static DATA_BLOB chal;
342 if (opt_challenge.length)
343 return opt_challenge;
345 chal = data_blob(NULL, 8);
347 generate_random_buffer(chal.data, chal.length);
348 return chal;
351 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
352 form DOMAIN/user into a domain and a user */
354 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
355 fstring user)
358 char *p = strchr(domuser,winbind_separator());
360 if (!p) {
361 return False;
364 fstrcpy(user, p+1);
365 fstrcpy(domain, domuser);
366 domain[PTR_DIFF(p, domuser)] = 0;
367 strupper_m(domain);
369 return True;
372 static bool get_require_membership_sid(void) {
373 struct winbindd_request request;
374 struct winbindd_response response;
376 if (!require_membership_of) {
377 return True;
380 if (require_membership_of_sid) {
381 return True;
384 /* Otherwise, ask winbindd for the name->sid request */
386 ZERO_STRUCT(request);
387 ZERO_STRUCT(response);
389 if (!parse_ntlm_auth_domain_user(require_membership_of,
390 request.data.name.dom_name,
391 request.data.name.name)) {
392 DEBUG(0, ("Could not parse %s into seperate domain/name parts!\n",
393 require_membership_of));
394 return False;
397 if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
398 NSS_STATUS_SUCCESS) {
399 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
400 require_membership_of));
401 return False;
404 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
406 if (require_membership_of_sid)
407 return True;
409 return False;
413 * Get some configuration from pam_winbind.conf to see if we
414 * need to contact trusted domain
417 int get_pam_winbind_config()
419 int ctrl = 0;
420 dictionary *d = NULL;
422 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
423 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
426 d = iniparser_load(discard_const_p(char, opt_pam_winbind_conf));
428 if (!d) {
429 return 0;
432 if (iniparser_getboolean(d, discard_const_p(char, "global:krb5_auth"), false)) {
433 ctrl |= WINBIND_KRB5_AUTH;
436 iniparser_freedict(d);
438 return ctrl;
441 /* Authenticate a user with a plaintext password */
443 static bool check_plaintext_auth(const char *user, const char *pass,
444 bool stdout_diagnostics)
446 struct winbindd_request request;
447 struct winbindd_response response;
448 NSS_STATUS result;
450 if (!get_require_membership_sid()) {
451 return False;
454 /* Send off request */
456 ZERO_STRUCT(request);
457 ZERO_STRUCT(response);
459 fstrcpy(request.data.auth.user, user);
460 fstrcpy(request.data.auth.pass, pass);
461 if (require_membership_of_sid) {
462 strlcpy(request.data.auth.require_membership_of_sid,
463 require_membership_of_sid,
464 sizeof(request.data.auth.require_membership_of_sid));
467 result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
469 /* Display response */
471 if (stdout_diagnostics) {
472 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
473 d_printf("Reading winbind reply failed! (0x01)\n");
476 d_printf("%s: %s (0x%x)\n",
477 response.data.auth.nt_status_string,
478 response.data.auth.error_string,
479 response.data.auth.nt_status);
480 } else {
481 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
482 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
485 DEBUG(3, ("%s: %s (0x%x)\n",
486 response.data.auth.nt_status_string,
487 response.data.auth.error_string,
488 response.data.auth.nt_status));
491 return (result == NSS_STATUS_SUCCESS);
494 /* authenticate a user with an encrypted username/password */
496 NTSTATUS contact_winbind_auth_crap(const char *username,
497 const char *domain,
498 const char *workstation,
499 const DATA_BLOB *challenge,
500 const DATA_BLOB *lm_response,
501 const DATA_BLOB *nt_response,
502 uint32 flags,
503 uint32 extra_logon_parameters,
504 uint8 lm_key[8],
505 uint8 user_session_key[16],
506 char **error_string,
507 char **unix_name)
509 NTSTATUS nt_status;
510 NSS_STATUS result;
511 struct winbindd_request request;
512 struct winbindd_response response;
514 if (!get_require_membership_sid()) {
515 return NT_STATUS_INVALID_PARAMETER;
518 ZERO_STRUCT(request);
519 ZERO_STRUCT(response);
521 request.flags = flags;
523 request.data.auth_crap.logon_parameters = extra_logon_parameters
524 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
526 if (require_membership_of_sid)
527 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
529 fstrcpy(request.data.auth_crap.user, username);
530 fstrcpy(request.data.auth_crap.domain, domain);
532 fstrcpy(request.data.auth_crap.workstation,
533 workstation);
535 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
537 if (lm_response && lm_response->length) {
538 memcpy(request.data.auth_crap.lm_resp,
539 lm_response->data,
540 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
541 request.data.auth_crap.lm_resp_len = lm_response->length;
544 if (nt_response && nt_response->length) {
545 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
546 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
547 request.extra_len = nt_response->length;
548 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
549 if (request.extra_data.data == NULL) {
550 return NT_STATUS_NO_MEMORY;
552 memcpy(request.extra_data.data, nt_response->data,
553 nt_response->length);
555 } else {
556 memcpy(request.data.auth_crap.nt_resp,
557 nt_response->data, nt_response->length);
559 request.data.auth_crap.nt_resp_len = nt_response->length;
562 result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
563 SAFE_FREE(request.extra_data.data);
565 /* Display response */
567 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
568 nt_status = NT_STATUS_UNSUCCESSFUL;
569 if (error_string)
570 *error_string = smb_xstrdup("Reading winbind reply failed!");
571 winbindd_free_response(&response);
572 return nt_status;
575 nt_status = (NT_STATUS(response.data.auth.nt_status));
576 if (!NT_STATUS_IS_OK(nt_status)) {
577 if (error_string)
578 *error_string = smb_xstrdup(response.data.auth.error_string);
579 winbindd_free_response(&response);
580 return nt_status;
583 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
584 memcpy(lm_key, response.data.auth.first_8_lm_hash,
585 sizeof(response.data.auth.first_8_lm_hash));
587 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
588 memcpy(user_session_key, response.data.auth.user_session_key,
589 sizeof(response.data.auth.user_session_key));
592 if (flags & WBFLAG_PAM_UNIX_NAME) {
593 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
594 if (!*unix_name) {
595 winbindd_free_response(&response);
596 return NT_STATUS_NO_MEMORY;
600 winbindd_free_response(&response);
601 return nt_status;
604 /* contact server to change user password using auth crap */
605 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
606 const char *domain,
607 const DATA_BLOB new_nt_pswd,
608 const DATA_BLOB old_nt_hash_enc,
609 const DATA_BLOB new_lm_pswd,
610 const DATA_BLOB old_lm_hash_enc,
611 char **error_string)
613 NTSTATUS nt_status;
614 NSS_STATUS result;
615 struct winbindd_request request;
616 struct winbindd_response response;
618 if (!get_require_membership_sid())
620 if(error_string)
621 *error_string = smb_xstrdup("Can't get membership sid.");
622 return NT_STATUS_INVALID_PARAMETER;
625 ZERO_STRUCT(request);
626 ZERO_STRUCT(response);
628 if(username != NULL)
629 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
630 if(domain != NULL)
631 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
633 if(new_nt_pswd.length)
635 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
636 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
639 if(old_nt_hash_enc.length)
641 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));
642 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
645 if(new_lm_pswd.length)
647 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
648 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
651 if(old_lm_hash_enc.length)
653 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));
654 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
657 result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
659 /* Display response */
661 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
663 nt_status = NT_STATUS_UNSUCCESSFUL;
664 if (error_string)
665 *error_string = smb_xstrdup("Reading winbind reply failed!");
666 winbindd_free_response(&response);
667 return nt_status;
670 nt_status = (NT_STATUS(response.data.auth.nt_status));
671 if (!NT_STATUS_IS_OK(nt_status))
673 if (error_string)
674 *error_string = smb_xstrdup(response.data.auth.error_string);
675 winbindd_free_response(&response);
676 return nt_status;
679 winbindd_free_response(&response);
681 return nt_status;
684 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
685 TALLOC_CTX *mem_ctx,
686 void *server_returned_info,
687 const char *original_user_name,
688 uint32_t session_info_flags,
689 struct auth_session_info **session_info_out)
691 char *unix_username = (char *)server_returned_info;
692 struct auth_session_info *session_info = talloc_zero(mem_ctx, struct auth_session_info);
693 if (!session_info) {
694 return NT_STATUS_NO_MEMORY;
697 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
698 if (!session_info->unix_info) {
699 TALLOC_FREE(session_info);
700 return NT_STATUS_NO_MEMORY;
702 session_info->unix_info->unix_name = talloc_steal(session_info->unix_info, unix_username);
704 *session_info_out = session_info;
706 return NT_STATUS_OK;
709 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
710 TALLOC_CTX *mem_ctx,
711 struct smb_krb5_context *smb_krb5_context,
712 DATA_BLOB *pac_blob,
713 const char *princ_name,
714 const struct tsocket_address *remote_address,
715 uint32_t session_info_flags,
716 struct auth_session_info **session_info)
718 TALLOC_CTX *tmp_ctx;
719 struct PAC_LOGON_INFO *logon_info = NULL;
720 char *unixuser;
721 NTSTATUS status;
722 char *domain = NULL;
723 char *realm = NULL;
724 char *user = NULL;
725 char *p;
727 tmp_ctx = talloc_new(mem_ctx);
728 if (!tmp_ctx) {
729 return NT_STATUS_NO_MEMORY;
732 if (pac_blob) {
733 #ifdef HAVE_KRB5
734 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
735 NULL, NULL, 0, &logon_info);
736 #else
737 status = NT_STATUS_ACCESS_DENIED;
738 #endif
739 if (!NT_STATUS_IS_OK(status)) {
740 goto done;
744 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
746 p = strchr_m(princ_name, '@');
747 if (!p) {
748 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
749 princ_name));
750 return NT_STATUS_LOGON_FAILURE;
753 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
754 if (!user) {
755 return NT_STATUS_NO_MEMORY;
758 realm = talloc_strdup(talloc_tos(), p + 1);
759 if (!realm) {
760 return NT_STATUS_NO_MEMORY;
763 if (!strequal(realm, lp_realm())) {
764 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
765 if (!lp_allow_trusted_domains()) {
766 return NT_STATUS_LOGON_FAILURE;
770 if (logon_info && logon_info->info3.base.logon_domain.string) {
771 domain = talloc_strdup(mem_ctx,
772 logon_info->info3.base.logon_domain.string);
773 if (!domain) {
774 return NT_STATUS_NO_MEMORY;
776 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
777 } else {
779 /* If we have winbind running, we can (and must) shorten the
780 username by using the short netbios name. Otherwise we will
781 have inconsistent user names. With Kerberos, we get the
782 fully qualified realm, with ntlmssp we get the short
783 name. And even w2k3 does use ntlmssp if you for example
784 connect to an ip address. */
786 wbcErr wbc_status;
787 struct wbcDomainInfo *info = NULL;
789 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
790 realm));
792 wbc_status = wbcDomainInfo(realm, &info);
794 if (WBC_ERROR_IS_OK(wbc_status)) {
795 domain = talloc_strdup(mem_ctx,
796 info->short_name);
797 wbcFreeMemory(info);
798 } else {
799 DEBUG(3, ("Could not find short name: %s\n",
800 wbcErrorString(wbc_status)));
801 domain = talloc_strdup(mem_ctx, realm);
803 if (!domain) {
804 return NT_STATUS_NO_MEMORY;
806 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
809 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
810 if (!unixuser) {
811 status = NT_STATUS_NO_MEMORY;
812 goto done;
815 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
817 done:
818 TALLOC_FREE(tmp_ctx);
819 return status;
825 * Return the challenge as determined by the authentication subsystem
826 * @return an 8 byte random challenge
829 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
830 uint8_t chal[8])
832 if (auth_ctx->challenge.data.length == 8) {
833 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
834 auth_ctx->challenge.set_by));
835 memcpy(chal, auth_ctx->challenge.data.data, 8);
836 return NT_STATUS_OK;
839 if (!auth_ctx->challenge.set_by) {
840 generate_random_buffer(chal, 8);
842 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
843 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
844 auth_ctx->challenge.set_by = "random";
847 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
848 auth_ctx->challenge.set_by));
850 return NT_STATUS_OK;
854 * NTLM2 authentication modifies the effective challenge,
855 * @param challenge The new challenge value
857 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
859 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
860 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
862 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
863 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
865 return NT_STATUS_OK;
869 * Check the password on an NTLMSSP login.
871 * Return the session keys used on the connection.
874 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
875 TALLOC_CTX *mem_ctx,
876 const struct auth_usersupplied_info *user_info,
877 void **server_returned_info,
878 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
880 static const char zeros[16] = { 0, };
881 NTSTATUS nt_status;
882 char *error_string = NULL;
883 uint8 lm_key[8];
884 uint8 user_sess_key[16];
885 char *unix_name = NULL;
887 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
888 user_info->workstation_name,
889 &auth4_context->challenge.data,
890 &user_info->password.response.lanman,
891 &user_info->password.response.nt,
892 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
894 lm_key, user_sess_key,
895 &error_string, &unix_name);
897 if (NT_STATUS_IS_OK(nt_status)) {
898 if (memcmp(lm_key, zeros, 8) != 0) {
899 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
900 memcpy(lm_session_key->data, lm_key, 8);
901 memset(lm_session_key->data+8, '\0', 8);
904 if (memcmp(user_sess_key, zeros, 16) != 0) {
905 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
907 *server_returned_info = talloc_strdup(mem_ctx,
908 unix_name);
909 } else {
910 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
911 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
912 user_info->client.domain_name, user_info->client.account_name,
913 user_info->workstation_name,
914 error_string ? error_string : "unknown error (NULL)"));
917 SAFE_FREE(error_string);
918 SAFE_FREE(unix_name);
919 return nt_status;
922 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
923 TALLOC_CTX *mem_ctx,
924 const struct auth_usersupplied_info *user_info,
925 void **server_returned_info,
926 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
928 NTSTATUS nt_status;
929 struct samr_Password lm_pw, nt_pw;
931 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
933 nt_status = ntlm_password_check(mem_ctx,
934 true, true, 0,
935 &auth4_context->challenge.data,
936 &user_info->password.response.lanman,
937 &user_info->password.response.nt,
938 user_info->client.account_name,
939 user_info->client.account_name,
940 user_info->client.domain_name,
941 &lm_pw, &nt_pw, session_key, lm_session_key);
943 if (NT_STATUS_IS_OK(nt_status)) {
944 *server_returned_info = talloc_asprintf(mem_ctx,
945 "%s%c%s", user_info->client.domain_name,
946 *lp_winbind_separator(),
947 user_info->client.account_name);
948 } else {
949 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
950 user_info->client.domain_name, user_info->client.account_name,
951 user_info->workstation_name,
952 nt_errstr(nt_status)));
954 return nt_status;
957 static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state)
959 NTSTATUS status;
960 if ( (opt_username == NULL) || (opt_domain == NULL) ) {
961 status = NT_STATUS_UNSUCCESSFUL;
962 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
963 return NT_STATUS_INVALID_PARAMETER;
966 status = ntlmssp_client_start(NULL,
967 lp_netbios_name(),
968 lp_workgroup(),
969 lp_client_ntlmv2_auth(),
970 client_ntlmssp_state);
972 if (!NT_STATUS_IS_OK(status)) {
973 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
974 nt_errstr(status)));
975 TALLOC_FREE(*client_ntlmssp_state);
976 return status;
979 status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
981 if (!NT_STATUS_IS_OK(status)) {
982 DEBUG(1, ("Could not set username: %s\n",
983 nt_errstr(status)));
984 TALLOC_FREE(*client_ntlmssp_state);
985 return status;
988 status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
990 if (!NT_STATUS_IS_OK(status)) {
991 DEBUG(1, ("Could not set domain: %s\n",
992 nt_errstr(status)));
993 TALLOC_FREE(*client_ntlmssp_state);
994 return status;
997 if (opt_password) {
998 status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
1000 if (!NT_STATUS_IS_OK(status)) {
1001 DEBUG(1, ("Could not set password: %s\n",
1002 nt_errstr(status)));
1003 TALLOC_FREE(*client_ntlmssp_state);
1004 return status;
1008 return NT_STATUS_OK;
1011 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1013 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1014 if (auth4_context == NULL) {
1015 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1016 return NULL;
1018 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1019 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1020 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1021 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1022 if (local_pw) {
1023 auth4_context->check_ntlm_password = local_pw_check;
1024 } else {
1025 auth4_context->check_ntlm_password = winbind_pw_check;
1027 auth4_context->private_data = NULL;
1028 return auth4_context;
1031 static NTSTATUS ntlm_auth_start_ntlmssp_server(TALLOC_CTX *mem_ctx,
1032 struct loadparm_context *lp_ctx,
1033 struct gensec_security **gensec_security_out)
1035 struct gensec_security *gensec_security;
1036 NTSTATUS nt_status;
1038 TALLOC_CTX *tmp_ctx;
1040 struct gensec_settings *gensec_settings;
1041 size_t idx = 0;
1042 struct cli_credentials *server_credentials;
1044 struct auth4_context *auth4_context;
1046 tmp_ctx = talloc_new(mem_ctx);
1047 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1049 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1050 if (auth4_context == NULL) {
1051 TALLOC_FREE(tmp_ctx);
1052 return NT_STATUS_NO_MEMORY;
1055 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1056 if (lp_ctx == NULL) {
1057 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1058 TALLOC_FREE(tmp_ctx);
1059 return NT_STATUS_NO_MEMORY;
1063 * This should be a 'netbios domain -> DNS domain'
1064 * mapping, and can currently validly return NULL on
1065 * poorly configured systems.
1067 * This is used for the NTLMSSP server
1070 if (opt_password) {
1071 gensec_settings->server_netbios_name = lp_netbios_name();
1072 gensec_settings->server_netbios_domain = lp_workgroup();
1073 } else {
1074 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1075 gensec_settings->server_netbios_domain = get_winbind_domain();
1078 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1079 get_mydnsdomname(talloc_tos()));
1080 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1081 get_mydnsfullname());
1083 gensec_settings->backends = talloc_zero_array(gensec_settings,
1084 struct gensec_security_ops *, 4);
1086 if (gensec_settings->backends == NULL) {
1087 TALLOC_FREE(tmp_ctx);
1088 return NT_STATUS_NO_MEMORY;
1091 gensec_init();
1093 /* These need to be in priority order, krb5 before NTLMSSP */
1094 #if defined(HAVE_KRB5)
1095 gensec_settings->backends[idx++] = &gensec_gse_krb5_security_ops;
1096 #endif
1098 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1100 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL,
1101 GENSEC_OID_SPNEGO);
1104 * This is anonymous for now, because we just use it
1105 * to set the kerberos state at the moment
1107 server_credentials = cli_credentials_init_anon(tmp_ctx);
1108 if (!server_credentials) {
1109 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1110 return NT_STATUS_NO_MEMORY;
1113 cli_credentials_set_conf(server_credentials, lp_ctx);
1115 if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1116 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1117 } else {
1118 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1121 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1122 auth4_context, &gensec_security);
1124 if (!NT_STATUS_IS_OK(nt_status)) {
1125 TALLOC_FREE(tmp_ctx);
1126 return nt_status;
1129 gensec_set_credentials(gensec_security, server_credentials);
1131 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
1132 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
1134 talloc_unlink(tmp_ctx, lp_ctx);
1135 talloc_unlink(tmp_ctx, server_credentials);
1136 talloc_unlink(tmp_ctx, gensec_settings);
1137 talloc_unlink(tmp_ctx, auth4_context);
1139 nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP);
1140 if (!NT_STATUS_IS_OK(nt_status)) {
1141 TALLOC_FREE(tmp_ctx);
1142 return nt_status;
1145 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1146 TALLOC_FREE(tmp_ctx);
1147 return NT_STATUS_OK;
1150 /*******************************************************************
1151 Used by firefox to drive NTLM auth to IIS servers.
1152 *******************************************************************/
1154 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
1155 DATA_BLOB *reply)
1157 struct winbindd_request wb_request;
1158 struct winbindd_response wb_response;
1159 int ctrl = 0;
1160 NSS_STATUS result;
1162 /* get winbindd to do the ntlmssp step on our behalf */
1163 ZERO_STRUCT(wb_request);
1164 ZERO_STRUCT(wb_response);
1167 * This is tricky here. If we set krb5_auth in pam_winbind.conf
1168 * creds for users in trusted domain will be stored the winbindd
1169 * child of the trusted domain. If we ask the primary domain for
1170 * ntlm_ccache_auth, it will fail. So, we have to ask the trusted
1171 * domain's child for ccache_ntlm_auth. that is to say, we have to
1172 * set WBFLAG_PAM_CONTACT_TRUSTDOM in request.flags.
1174 ctrl = get_pam_winbind_config();
1176 if (ctrl & WINBIND_KRB5_AUTH) {
1177 wb_request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1180 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
1181 "%s%c%s", opt_domain, winbind_separator(), opt_username);
1182 wb_request.data.ccache_ntlm_auth.uid = geteuid();
1183 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
1184 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
1185 wb_request.extra_len = initial_msg.length + challenge_msg.length;
1187 if (wb_request.extra_len > 0) {
1188 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
1189 if (wb_request.extra_data.data == NULL) {
1190 return NT_STATUS_NO_MEMORY;
1193 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
1194 memcpy(wb_request.extra_data.data + initial_msg.length,
1195 challenge_msg.data, challenge_msg.length);
1198 result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
1199 SAFE_FREE(wb_request.extra_data.data);
1201 if (result != NSS_STATUS_SUCCESS) {
1202 winbindd_free_response(&wb_response);
1203 return NT_STATUS_UNSUCCESSFUL;
1206 if (reply) {
1207 *reply = data_blob(wb_response.extra_data.data,
1208 wb_response.data.ccache_ntlm_auth.auth_blob_len);
1209 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
1210 reply->data == NULL) {
1211 winbindd_free_response(&wb_response);
1212 return NT_STATUS_NO_MEMORY;
1216 winbindd_free_response(&wb_response);
1217 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1220 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1221 struct loadparm_context *lp_ctx,
1222 struct ntlm_auth_state *state,
1223 char *buf, int length, void **private2)
1225 DATA_BLOB request, reply;
1226 NTSTATUS nt_status;
1228 if (!opt_username || !*opt_username) {
1229 x_fprintf(x_stderr, "username must be specified!\n\n");
1230 exit(1);
1233 if (strlen(buf) < 2) {
1234 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1235 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1236 return;
1239 if (strlen(buf) > 3) {
1240 if(strncmp(buf, "SF ", 3) == 0) {
1241 DEBUG(10, ("Looking for flags to negotiate\n"));
1242 talloc_free(state->want_feature_list);
1243 state->want_feature_list = talloc_strdup(state->mem_ctx,
1244 buf+3);
1245 x_fprintf(x_stdout, "OK\n");
1246 return;
1248 request = base64_decode_data_blob(buf + 3);
1249 } else {
1250 request = data_blob_null;
1253 if (strncmp(buf, "PW ", 3) == 0) {
1254 /* We asked for a password and obviously got it :-) */
1256 opt_password = SMB_STRNDUP((const char *)request.data,
1257 request.length);
1259 if (opt_password == NULL) {
1260 DEBUG(1, ("Out of memory\n"));
1261 x_fprintf(x_stdout, "BH Out of memory\n");
1262 data_blob_free(&request);
1263 return;
1266 x_fprintf(x_stdout, "OK\n");
1267 data_blob_free(&request);
1268 return;
1271 if (!state->ntlmssp_state && use_cached_creds) {
1272 /* check whether cached credentials are usable. */
1273 DATA_BLOB empty_blob = data_blob_null;
1275 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
1276 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1277 /* failed to use cached creds */
1278 use_cached_creds = False;
1282 if (opt_password == NULL && !use_cached_creds) {
1283 /* Request a password from the calling process. After
1284 sending it, the calling process should retry asking for the
1285 negotiate. */
1287 DEBUG(10, ("Requesting password\n"));
1288 x_fprintf(x_stdout, "PW\n");
1289 return;
1292 if (strncmp(buf, "YR", 2) == 0) {
1293 TALLOC_FREE(state->ntlmssp_state);
1294 state->cli_state = CLIENT_INITIAL;
1295 } else if (strncmp(buf, "TT", 2) == 0) {
1296 /* No special preprocessing required */
1297 } else if (strncmp(buf, "GF", 2) == 0) {
1298 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
1300 if(state->cli_state == CLIENT_FINISHED) {
1301 x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
1303 else {
1304 x_fprintf(x_stdout, "BH\n");
1307 data_blob_free(&request);
1308 return;
1309 } else if (strncmp(buf, "GK", 2) == 0 ) {
1310 DEBUG(10, ("Requested session key\n"));
1312 if(state->cli_state == CLIENT_FINISHED) {
1313 char *key64 = base64_encode_data_blob(state->mem_ctx,
1314 state->session_key);
1315 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
1316 TALLOC_FREE(key64);
1318 else {
1319 x_fprintf(x_stdout, "BH\n");
1322 data_blob_free(&request);
1323 return;
1324 } else {
1325 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1326 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1327 return;
1330 if (!state->ntlmssp_state) {
1331 nt_status = ntlm_auth_start_ntlmssp_client(
1332 &state->ntlmssp_state);
1333 if (!NT_STATUS_IS_OK(nt_status)) {
1334 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1335 return;
1337 ntlmssp_want_feature_list(state->ntlmssp_state,
1338 state->want_feature_list);
1339 state->initial_message = data_blob_null;
1342 DEBUG(10, ("got NTLMSSP packet:\n"));
1343 dump_data(10, request.data, request.length);
1345 if (use_cached_creds && !opt_password &&
1346 (state->cli_state == CLIENT_RESPONSE)) {
1347 nt_status = do_ccache_ntlm_auth(state->initial_message, request,
1348 &reply);
1349 } else {
1350 nt_status = ntlmssp_update(state->ntlmssp_state, request,
1351 &reply);
1354 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1355 char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
1356 reply);
1357 if (state->cli_state == CLIENT_INITIAL) {
1358 x_fprintf(x_stdout, "YR %s\n", reply_base64);
1359 state->initial_message = reply;
1360 state->cli_state = CLIENT_RESPONSE;
1361 } else {
1362 x_fprintf(x_stdout, "KK %s\n", reply_base64);
1363 data_blob_free(&reply);
1365 TALLOC_FREE(reply_base64);
1366 DEBUG(10, ("NTLMSSP challenge\n"));
1367 } else if (NT_STATUS_IS_OK(nt_status)) {
1368 char *reply_base64 = base64_encode_data_blob(talloc_tos(),
1369 reply);
1370 x_fprintf(x_stdout, "AF %s\n", reply_base64);
1371 TALLOC_FREE(reply_base64);
1373 if(state->have_session_key)
1374 data_blob_free(&state->session_key);
1376 state->session_key = data_blob(
1377 state->ntlmssp_state->session_key.data,
1378 state->ntlmssp_state->session_key.length);
1379 state->neg_flags = state->ntlmssp_state->neg_flags;
1380 state->have_session_key = true;
1382 DEBUG(10, ("NTLMSSP OK!\n"));
1383 state->cli_state = CLIENT_FINISHED;
1384 TALLOC_FREE(state->ntlmssp_state);
1385 } else {
1386 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1387 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
1388 state->cli_state = CLIENT_ERROR;
1389 TALLOC_FREE(state->ntlmssp_state);
1392 data_blob_free(&request);
1395 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1396 struct loadparm_context *lp_ctx,
1397 struct ntlm_auth_state *state,
1398 char *buf, int length, void **private2)
1400 char *user, *pass;
1401 user=buf;
1403 pass=(char *)memchr(buf,' ',length);
1404 if (!pass) {
1405 DEBUG(2, ("Password not found. Denying access\n"));
1406 x_fprintf(x_stdout, "ERR\n");
1407 return;
1409 *pass='\0';
1410 pass++;
1412 if (state->helper_mode == SQUID_2_5_BASIC) {
1413 rfc1738_unescape(user);
1414 rfc1738_unescape(pass);
1417 if (check_plaintext_auth(user, pass, False)) {
1418 x_fprintf(x_stdout, "OK\n");
1419 } else {
1420 x_fprintf(x_stdout, "ERR\n");
1424 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1425 struct loadparm_context *lp_ctx,
1426 char *buf, int length, void **private1)
1428 DATA_BLOB in;
1429 DATA_BLOB out = data_blob(NULL, 0);
1430 char *out_base64 = NULL;
1431 const char *reply_arg = NULL;
1432 struct gensec_ntlm_state {
1433 struct gensec_security *gensec_state;
1434 const char *set_password;
1436 struct gensec_ntlm_state *state;
1438 NTSTATUS nt_status;
1439 bool first = false;
1440 const char *reply_code;
1441 struct cli_credentials *creds;
1443 static char *want_feature_list = NULL;
1444 static DATA_BLOB session_key;
1446 TALLOC_CTX *mem_ctx;
1448 if (*private1) {
1449 state = (struct gensec_ntlm_state *)*private1;
1450 } else {
1451 state = talloc_zero(NULL, struct gensec_ntlm_state);
1452 if (!state) {
1453 x_fprintf(x_stdout, "BH No Memory\n");
1454 exit(1);
1456 *private1 = state;
1457 if (opt_password) {
1458 state->set_password = opt_password;
1462 if (strlen(buf) < 2) {
1463 DEBUG(1, ("query [%s] invalid", buf));
1464 x_fprintf(x_stdout, "BH Query invalid\n");
1465 return;
1468 if (strlen(buf) > 3) {
1469 if(strncmp(buf, "SF ", 3) == 0) {
1470 DEBUG(10, ("Setting flags to negotiate\n"));
1471 talloc_free(want_feature_list);
1472 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1473 x_fprintf(x_stdout, "OK\n");
1474 return;
1476 in = base64_decode_data_blob(buf + 3);
1477 } else {
1478 in = data_blob(NULL, 0);
1481 if (strncmp(buf, "YR", 2) == 0) {
1482 if (state->gensec_state) {
1483 talloc_free(state->gensec_state);
1484 state->gensec_state = NULL;
1486 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1487 /* Just return BH, like ntlm_auth from Samba 3 does. */
1488 x_fprintf(x_stdout, "BH Command expected\n");
1489 data_blob_free(&in);
1490 return;
1491 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1492 (strncmp(buf, "KK ", 3) != 0) &&
1493 (strncmp(buf, "AF ", 3) != 0) &&
1494 (strncmp(buf, "NA ", 3) != 0) &&
1495 (strncmp(buf, "UG", 2) != 0) &&
1496 (strncmp(buf, "PW ", 3) != 0) &&
1497 (strncmp(buf, "GK", 2) != 0) &&
1498 (strncmp(buf, "GF", 2) != 0)) {
1499 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1500 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1501 data_blob_free(&in);
1502 return;
1505 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1507 /* setup gensec */
1508 if (!(state->gensec_state)) {
1509 switch (stdio_helper_mode) {
1510 case GSS_SPNEGO_CLIENT:
1511 case NTLMSSP_CLIENT_1:
1512 /* setup the client side */
1514 nt_status = gensec_client_start(NULL, &state->gensec_state,
1515 lpcfg_gensec_settings(NULL, lp_ctx));
1516 if (!NT_STATUS_IS_OK(nt_status)) {
1517 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1518 talloc_free(mem_ctx);
1519 return;
1522 creds = cli_credentials_init(state->gensec_state);
1523 cli_credentials_set_conf(creds, lp_ctx);
1524 if (opt_username) {
1525 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1527 if (opt_domain) {
1528 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1530 if (state->set_password) {
1531 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1532 } else {
1533 cli_credentials_set_password_callback(creds, get_password);
1535 if (opt_workstation) {
1536 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1539 gensec_set_credentials(state->gensec_state, creds);
1541 break;
1542 case GSS_SPNEGO_SERVER:
1543 case SQUID_2_5_NTLMSSP:
1545 nt_status = ntlm_auth_start_ntlmssp_server(state, lp_ctx,
1546 &state->gensec_state);
1547 if (!NT_STATUS_IS_OK(nt_status)) {
1548 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1549 talloc_free(mem_ctx);
1550 return;
1552 break;
1554 default:
1555 talloc_free(mem_ctx);
1556 abort();
1559 gensec_want_feature_list(state->gensec_state, want_feature_list);
1561 switch (stdio_helper_mode) {
1562 case GSS_SPNEGO_CLIENT:
1563 case GSS_SPNEGO_SERVER:
1564 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1565 if (!in.length) {
1566 first = true;
1568 break;
1569 case NTLMSSP_CLIENT_1:
1570 if (!in.length) {
1571 first = true;
1573 /* fall through */
1574 case SQUID_2_5_NTLMSSP:
1575 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1576 break;
1577 default:
1578 talloc_free(mem_ctx);
1579 abort();
1582 if (!NT_STATUS_IS_OK(nt_status)) {
1583 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1584 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1585 talloc_free(mem_ctx);
1586 return;
1591 /* update */
1593 if (strncmp(buf, "PW ", 3) == 0) {
1594 state->set_password = talloc_strndup(state,
1595 (const char *)in.data,
1596 in.length);
1598 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1599 state->set_password,
1600 CRED_SPECIFIED);
1601 x_fprintf(x_stdout, "OK\n");
1602 data_blob_free(&in);
1603 talloc_free(mem_ctx);
1604 return;
1607 if (strncmp(buf, "GK", 2) == 0) {
1608 char *base64_key;
1609 DEBUG(10, ("Requested session key\n"));
1610 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1611 if(!NT_STATUS_IS_OK(nt_status)) {
1612 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1613 x_fprintf(x_stdout, "BH No session key\n");
1614 talloc_free(mem_ctx);
1615 return;
1616 } else {
1617 base64_key = base64_encode_data_blob(state, session_key);
1618 x_fprintf(x_stdout, "GK %s\n", base64_key);
1619 talloc_free(base64_key);
1621 talloc_free(mem_ctx);
1622 return;
1625 if (stdio_helper_mode == SQUID_2_5_NTLMSSP && strncmp(buf, "GF", 2) == 0) {
1626 uint32_t neg_flags;
1628 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1630 DEBUG(10, ("Requested negotiated feature flags\n"));
1631 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1632 return;
1635 nt_status = gensec_update(state->gensec_state, mem_ctx, NULL, in, &out);
1637 /* don't leak 'bad password'/'no such user' info to the network client */
1638 nt_status = nt_status_squash(nt_status);
1640 if (out.length) {
1641 out_base64 = base64_encode_data_blob(mem_ctx, out);
1642 } else {
1643 out_base64 = NULL;
1646 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1647 reply_arg = "*";
1648 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1649 reply_code = "YR";
1650 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1651 reply_code = "KK";
1652 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1653 reply_code = "TT";
1654 } else {
1655 abort();
1659 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1660 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1661 reply_arg = nt_errstr(nt_status);
1662 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1663 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1664 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1665 reply_arg = nt_errstr(nt_status);
1666 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1667 } else if (!NT_STATUS_IS_OK(nt_status)) {
1668 reply_code = "NA";
1669 reply_arg = nt_errstr(nt_status);
1670 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1671 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1672 struct auth_session_info *session_info;
1674 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1675 if (!NT_STATUS_IS_OK(nt_status)) {
1676 reply_code = "BH Failed to retrive session info";
1677 reply_arg = nt_errstr(nt_status);
1678 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1679 } else {
1681 reply_code = "AF";
1682 reply_arg = session_info->unix_info->unix_name;
1683 talloc_free(session_info);
1685 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1686 reply_code = "AF";
1687 reply_arg = out_base64;
1688 } else {
1689 abort();
1692 switch (stdio_helper_mode) {
1693 case GSS_SPNEGO_SERVER:
1694 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1695 out_base64 ? out_base64 : "*",
1696 reply_arg ? reply_arg : "*");
1697 break;
1698 default:
1699 if (out_base64) {
1700 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1701 } else if (reply_arg) {
1702 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1703 } else {
1704 x_fprintf(x_stdout, "%s\n", reply_code);
1708 talloc_free(mem_ctx);
1709 return;
1712 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1713 struct loadparm_context *lp_ctx,
1714 struct ntlm_auth_state *state,
1715 char *buf, int length, void **private2)
1717 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1718 return;
1721 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1722 struct loadparm_context *lp_ctx,
1723 struct ntlm_auth_state *state,
1724 char *buf, int length, void **private2)
1726 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1727 return;
1730 static struct ntlmssp_state *client_ntlmssp_state = NULL;
1732 static bool manage_client_ntlmssp_init(struct spnego_data spnego)
1734 NTSTATUS status;
1735 DATA_BLOB null_blob = data_blob_null;
1736 DATA_BLOB to_server;
1737 char *to_server_base64;
1738 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1739 TALLOC_CTX *ctx = talloc_tos();
1741 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1743 if (client_ntlmssp_state != NULL) {
1744 DEBUG(1, ("Request for initial SPNEGO request where "
1745 "we already have a state\n"));
1746 return False;
1749 if (!client_ntlmssp_state) {
1750 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1751 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1752 return False;
1757 if (opt_password == NULL) {
1759 /* Request a password from the calling process. After
1760 sending it, the calling process should retry with
1761 the negTokenInit. */
1763 DEBUG(10, ("Requesting password\n"));
1764 x_fprintf(x_stdout, "PW\n");
1765 return True;
1768 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1769 spnego.negTokenInit.mechTypes = my_mechs;
1770 spnego.negTokenInit.reqFlags = data_blob_null;
1771 spnego.negTokenInit.reqFlagsPadding = 0;
1772 spnego.negTokenInit.mechListMIC = null_blob;
1774 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1775 &spnego.negTokenInit.mechToken);
1777 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1778 NT_STATUS_IS_OK(status)) ) {
1779 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1780 nt_errstr(status)));
1781 TALLOC_FREE(client_ntlmssp_state);
1782 return False;
1785 spnego_write_data(ctx, &to_server, &spnego);
1786 data_blob_free(&spnego.negTokenInit.mechToken);
1788 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1789 data_blob_free(&to_server);
1790 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1791 TALLOC_FREE(to_server_base64);
1792 return True;
1795 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
1797 NTSTATUS status;
1798 DATA_BLOB null_blob = data_blob_null;
1799 DATA_BLOB request;
1800 DATA_BLOB to_server;
1801 char *to_server_base64;
1802 TALLOC_CTX *ctx = talloc_tos();
1804 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1806 if (client_ntlmssp_state == NULL) {
1807 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1808 x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
1809 return;
1812 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1813 x_fprintf(x_stdout, "NA\n");
1814 TALLOC_FREE(client_ntlmssp_state);
1815 return;
1818 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1819 x_fprintf(x_stdout, "AF\n");
1820 TALLOC_FREE(client_ntlmssp_state);
1821 return;
1824 status = ntlmssp_update(client_ntlmssp_state,
1825 spnego.negTokenTarg.responseToken,
1826 &request);
1828 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
1829 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED or OK from "
1830 "ntlmssp_client_update, got: %s\n",
1831 nt_errstr(status)));
1832 x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
1833 "ntlmssp_client_update\n");
1834 data_blob_free(&request);
1835 TALLOC_FREE(client_ntlmssp_state);
1836 return;
1839 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1840 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1841 spnego.negTokenTarg.supportedMech = (const char *)OID_NTLMSSP;
1842 spnego.negTokenTarg.responseToken = request;
1843 spnego.negTokenTarg.mechListMIC = null_blob;
1845 spnego_write_data(ctx, &to_server, &spnego);
1846 data_blob_free(&request);
1848 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1849 data_blob_free(&to_server);
1850 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1851 TALLOC_FREE(to_server_base64);
1852 return;
1855 #ifdef HAVE_KRB5
1857 static bool manage_client_krb5_init(struct spnego_data spnego)
1859 char *principal;
1860 DATA_BLOB tkt, tkt_wrapped, to_server;
1861 DATA_BLOB session_key_krb5 = data_blob_null;
1862 struct spnego_data reply;
1863 char *reply_base64;
1864 int retval;
1866 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1867 ssize_t len;
1868 TALLOC_CTX *ctx = talloc_tos();
1870 principal = spnego.negTokenInit.targetPrincipal;
1872 /* We may not be allowed to use the server-supplied SPNEGO principal, or it may not have been supplied to us
1874 if (!lp_client_use_spnego_principal() || strequal(principal, ADS_IGNORE_PRINCIPAL)) {
1875 principal = NULL;
1878 if (principal == NULL &&
1879 opt_target_service && opt_target_hostname && !is_ipaddress(opt_target_hostname)) {
1880 DEBUG(3,("manage_client_krb5_init: using target "
1881 "hostname not SPNEGO principal\n"));
1883 principal = kerberos_get_principal_from_service_hostname(talloc_tos(),
1884 opt_target_service,
1885 opt_target_hostname,
1886 lp_realm());
1888 if (!principal) {
1889 return false;
1892 DEBUG(3,("manage_client_krb5_init: guessed "
1893 "server principal=%s\n",
1894 principal ? principal : "<null>"));
1897 if (principal == NULL) {
1898 DEBUG(3,("manage_client_krb5_init: could not guess server principal\n"));
1899 return false;
1902 retval = cli_krb5_get_ticket(ctx, principal, 0,
1903 &tkt, &session_key_krb5,
1904 0, NULL, NULL, NULL);
1905 if (retval) {
1906 char *user = NULL;
1908 /* Let's try to first get the TGT, for that we need a
1909 password. */
1911 if (opt_password == NULL) {
1912 DEBUG(10, ("Requesting password\n"));
1913 x_fprintf(x_stdout, "PW\n");
1914 return True;
1917 user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
1918 if (!user) {
1919 return false;
1922 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1923 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1924 return False;
1927 retval = cli_krb5_get_ticket(ctx, principal, 0,
1928 &tkt, &session_key_krb5,
1929 0, NULL, NULL, NULL);
1930 if (retval) {
1931 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1932 return False;
1937 /* wrap that up in a nice GSS-API wrapping */
1938 tkt_wrapped = spnego_gen_krb5_wrap(ctx, tkt, TOK_ID_KRB_AP_REQ);
1940 data_blob_free(&session_key_krb5);
1942 ZERO_STRUCT(reply);
1944 reply.type = SPNEGO_NEG_TOKEN_INIT;
1945 reply.negTokenInit.mechTypes = my_mechs;
1946 reply.negTokenInit.reqFlags = data_blob_null;
1947 reply.negTokenInit.reqFlagsPadding = 0;
1948 reply.negTokenInit.mechToken = tkt_wrapped;
1949 reply.negTokenInit.mechListMIC = data_blob_null;
1951 len = spnego_write_data(ctx, &to_server, &reply);
1952 data_blob_free(&tkt);
1954 if (len == -1) {
1955 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1956 return False;
1959 reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1960 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1962 TALLOC_FREE(reply_base64);
1963 data_blob_free(&to_server);
1964 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1965 return True;
1968 static void manage_client_krb5_targ(struct spnego_data spnego)
1970 switch (spnego.negTokenTarg.negResult) {
1971 case SPNEGO_ACCEPT_INCOMPLETE:
1972 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1973 x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
1974 "ACCEPT_INCOMPLETE\n");
1975 break;
1976 case SPNEGO_ACCEPT_COMPLETED:
1977 DEBUG(10, ("Accept completed\n"));
1978 x_fprintf(x_stdout, "AF\n");
1979 break;
1980 case SPNEGO_REJECT:
1981 DEBUG(10, ("Rejected\n"));
1982 x_fprintf(x_stdout, "NA\n");
1983 break;
1984 default:
1985 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1986 x_fprintf(x_stdout, "AF\n");
1990 #endif
1992 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1993 struct loadparm_context *lp_ctx,
1994 struct ntlm_auth_state *state,
1995 char *buf, int length, void **private2)
1997 DATA_BLOB request;
1998 struct spnego_data spnego;
1999 ssize_t len;
2000 TALLOC_CTX *ctx = talloc_tos();
2002 if (!opt_username || !*opt_username) {
2003 x_fprintf(x_stderr, "username must be specified!\n\n");
2004 exit(1);
2007 if (strlen(buf) <= 3) {
2008 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
2009 x_fprintf(x_stdout, "BH SPNEGO query too short\n");
2010 return;
2013 request = base64_decode_data_blob(buf+3);
2015 if (strncmp(buf, "PW ", 3) == 0) {
2017 /* We asked for a password and obviously got it :-) */
2019 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
2021 if (opt_password == NULL) {
2022 DEBUG(1, ("Out of memory\n"));
2023 x_fprintf(x_stdout, "BH Out of memory\n");
2024 data_blob_free(&request);
2025 return;
2028 x_fprintf(x_stdout, "OK\n");
2029 data_blob_free(&request);
2030 return;
2033 if ( (strncmp(buf, "TT ", 3) != 0) &&
2034 (strncmp(buf, "AF ", 3) != 0) &&
2035 (strncmp(buf, "NA ", 3) != 0) ) {
2036 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
2037 x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
2038 data_blob_free(&request);
2039 return;
2042 /* So we got a server challenge to generate a SPNEGO
2043 client-to-server request... */
2045 len = spnego_read_data(ctx, request, &spnego);
2046 data_blob_free(&request);
2048 if (len == -1) {
2049 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
2050 x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
2051 return;
2054 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
2056 /* The server offers a list of mechanisms */
2058 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
2060 while (*mechType != NULL) {
2062 #ifdef HAVE_KRB5
2063 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
2064 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
2065 if (manage_client_krb5_init(spnego))
2066 goto out;
2068 #endif
2070 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
2071 if (manage_client_ntlmssp_init(spnego))
2072 goto out;
2075 mechType++;
2078 DEBUG(1, ("Server offered no compatible mechanism\n"));
2079 x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
2080 return;
2083 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
2085 if (spnego.negTokenTarg.supportedMech == NULL) {
2086 /* On accept/reject Windows does not send the
2087 mechanism anymore. Handle that here and
2088 shut down the mechanisms. */
2090 switch (spnego.negTokenTarg.negResult) {
2091 case SPNEGO_ACCEPT_COMPLETED:
2092 x_fprintf(x_stdout, "AF\n");
2093 break;
2094 case SPNEGO_REJECT:
2095 x_fprintf(x_stdout, "NA\n");
2096 break;
2097 default:
2098 DEBUG(1, ("Got a negTokenTarg with no mech and an "
2099 "unknown negResult: %d\n",
2100 spnego.negTokenTarg.negResult));
2101 x_fprintf(x_stdout, "BH Got a negTokenTarg with"
2102 " no mech and an unknown "
2103 "negResult\n");
2106 TALLOC_FREE(client_ntlmssp_state);
2107 goto out;
2110 if (strcmp(spnego.negTokenTarg.supportedMech,
2111 OID_NTLMSSP) == 0) {
2112 manage_client_ntlmssp_targ(spnego);
2113 goto out;
2116 #if HAVE_KRB5
2117 if (strcmp(spnego.negTokenTarg.supportedMech,
2118 OID_KERBEROS5_OLD) == 0) {
2119 manage_client_krb5_targ(spnego);
2120 goto out;
2122 #endif
2126 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
2127 x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
2128 return;
2130 out:
2131 spnego_free_data(&spnego);
2132 return;
2135 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
2136 struct loadparm_context *lp_ctx,
2137 struct ntlm_auth_state *state,
2138 char *buf, int length, void **private2)
2140 char *request, *parameter;
2141 static DATA_BLOB challenge;
2142 static DATA_BLOB lm_response;
2143 static DATA_BLOB nt_response;
2144 static char *full_username;
2145 static char *username;
2146 static char *domain;
2147 static char *plaintext_password;
2148 static bool ntlm_server_1_user_session_key;
2149 static bool ntlm_server_1_lm_session_key;
2151 if (strequal(buf, ".")) {
2152 if (!full_username && !username) {
2153 x_fprintf(x_stdout, "Error: No username supplied!\n");
2154 } else if (plaintext_password) {
2155 /* handle this request as plaintext */
2156 if (!full_username) {
2157 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
2158 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
2159 return;
2162 if (check_plaintext_auth(full_username, plaintext_password, False)) {
2163 x_fprintf(x_stdout, "Authenticated: Yes\n");
2164 } else {
2165 x_fprintf(x_stdout, "Authenticated: No\n");
2167 } else if (!lm_response.data && !nt_response.data) {
2168 x_fprintf(x_stdout, "Error: No password supplied!\n");
2169 } else if (!challenge.data) {
2170 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
2171 } else {
2172 char *error_string = NULL;
2173 uchar lm_key[8];
2174 uchar user_session_key[16];
2175 uint32 flags = 0;
2177 if (full_username && !username) {
2178 fstring fstr_user;
2179 fstring fstr_domain;
2181 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
2182 /* username might be 'tainted', don't print into our new-line deleimianted stream */
2183 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
2185 SAFE_FREE(username);
2186 SAFE_FREE(domain);
2187 username = smb_xstrdup(fstr_user);
2188 domain = smb_xstrdup(fstr_domain);
2191 if (!domain) {
2192 domain = smb_xstrdup(get_winbind_domain());
2195 if (ntlm_server_1_lm_session_key)
2196 flags |= WBFLAG_PAM_LMKEY;
2198 if (ntlm_server_1_user_session_key)
2199 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2201 if (!NT_STATUS_IS_OK(
2202 contact_winbind_auth_crap(username,
2203 domain,
2204 lp_netbios_name(),
2205 &challenge,
2206 &lm_response,
2207 &nt_response,
2208 flags, 0,
2209 lm_key,
2210 user_session_key,
2211 &error_string,
2212 NULL))) {
2214 x_fprintf(x_stdout, "Authenticated: No\n");
2215 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
2216 } else {
2217 static char zeros[16];
2218 char *hex_lm_key;
2219 char *hex_user_session_key;
2221 x_fprintf(x_stdout, "Authenticated: Yes\n");
2223 if (ntlm_server_1_lm_session_key
2224 && (memcmp(zeros, lm_key,
2225 sizeof(lm_key)) != 0)) {
2226 hex_lm_key = hex_encode_talloc(NULL,
2227 (const unsigned char *)lm_key,
2228 sizeof(lm_key));
2229 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
2230 TALLOC_FREE(hex_lm_key);
2233 if (ntlm_server_1_user_session_key
2234 && (memcmp(zeros, user_session_key,
2235 sizeof(user_session_key)) != 0)) {
2236 hex_user_session_key = hex_encode_talloc(NULL,
2237 (const unsigned char *)user_session_key,
2238 sizeof(user_session_key));
2239 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
2240 TALLOC_FREE(hex_user_session_key);
2243 SAFE_FREE(error_string);
2245 /* clear out the state */
2246 challenge = data_blob_null;
2247 nt_response = data_blob_null;
2248 lm_response = data_blob_null;
2249 SAFE_FREE(full_username);
2250 SAFE_FREE(username);
2251 SAFE_FREE(domain);
2252 SAFE_FREE(plaintext_password);
2253 ntlm_server_1_user_session_key = False;
2254 ntlm_server_1_lm_session_key = False;
2255 x_fprintf(x_stdout, ".\n");
2257 return;
2260 request = buf;
2262 /* Indicates a base64 encoded structure */
2263 parameter = strstr_m(request, ":: ");
2264 if (!parameter) {
2265 parameter = strstr_m(request, ": ");
2267 if (!parameter) {
2268 DEBUG(0, ("Parameter not found!\n"));
2269 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2270 return;
2273 parameter[0] ='\0';
2274 parameter++;
2275 parameter[0] ='\0';
2276 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, "LANMAN-Challenge")) {
2290 challenge = strhex_to_data_blob(NULL, parameter);
2291 if (challenge.length != 8) {
2292 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
2293 parameter,
2294 (int)challenge.length);
2295 challenge = data_blob_null;
2297 } else if (strequal(request, "NT-Response")) {
2298 nt_response = strhex_to_data_blob(NULL, parameter);
2299 if (nt_response.length < 24) {
2300 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
2301 parameter,
2302 (int)nt_response.length);
2303 nt_response = data_blob_null;
2305 } else if (strequal(request, "LANMAN-Response")) {
2306 lm_response = strhex_to_data_blob(NULL, parameter);
2307 if (lm_response.length != 24) {
2308 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
2309 parameter,
2310 (int)lm_response.length);
2311 lm_response = data_blob_null;
2313 } else if (strequal(request, "Password")) {
2314 plaintext_password = smb_xstrdup(parameter);
2315 } else if (strequal(request, "NT-Domain")) {
2316 domain = smb_xstrdup(parameter);
2317 } else if (strequal(request, "Username")) {
2318 username = smb_xstrdup(parameter);
2319 } else if (strequal(request, "Full-Username")) {
2320 full_username = smb_xstrdup(parameter);
2321 } else if (strequal(request, "Request-User-Session-Key")) {
2322 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2323 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2324 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2325 } else {
2326 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2330 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2331 struct loadparm_context *lp_ctx,
2332 struct ntlm_auth_state *state,
2333 char *buf, int length, void **private2)
2335 char *request, *parameter;
2336 static DATA_BLOB new_nt_pswd;
2337 static DATA_BLOB old_nt_hash_enc;
2338 static DATA_BLOB new_lm_pswd;
2339 static DATA_BLOB old_lm_hash_enc;
2340 static char *full_username = NULL;
2341 static char *username = NULL;
2342 static char *domain = NULL;
2343 static char *newpswd = NULL;
2344 static char *oldpswd = NULL;
2346 if (strequal(buf, ".")) {
2347 if(newpswd && oldpswd) {
2348 uchar old_nt_hash[16];
2349 uchar old_lm_hash[16];
2350 uchar new_nt_hash[16];
2351 uchar new_lm_hash[16];
2353 new_nt_pswd = data_blob(NULL, 516);
2354 old_nt_hash_enc = data_blob(NULL, 16);
2356 /* Calculate the MD4 hash (NT compatible) of the
2357 * password */
2358 E_md4hash(oldpswd, old_nt_hash);
2359 E_md4hash(newpswd, new_nt_hash);
2361 /* E_deshash returns false for 'long'
2362 passwords (> 14 DOS chars).
2364 Therefore, don't send a buffer
2365 encrypted with the truncated hash
2366 (it could allow an even easier
2367 attack on the password)
2369 Likewise, obey the admin's restriction
2372 if (lp_client_lanman_auth() &&
2373 E_deshash(newpswd, new_lm_hash) &&
2374 E_deshash(oldpswd, old_lm_hash)) {
2375 new_lm_pswd = data_blob(NULL, 516);
2376 old_lm_hash_enc = data_blob(NULL, 16);
2377 encode_pw_buffer(new_lm_pswd.data, newpswd,
2378 STR_UNICODE);
2380 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
2381 E_old_pw_hash(new_nt_hash, old_lm_hash,
2382 old_lm_hash_enc.data);
2383 } else {
2384 new_lm_pswd.data = NULL;
2385 new_lm_pswd.length = 0;
2386 old_lm_hash_enc.data = NULL;
2387 old_lm_hash_enc.length = 0;
2390 encode_pw_buffer(new_nt_pswd.data, newpswd,
2391 STR_UNICODE);
2393 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
2394 E_old_pw_hash(new_nt_hash, old_nt_hash,
2395 old_nt_hash_enc.data);
2398 if (!full_username && !username) {
2399 x_fprintf(x_stdout, "Error: No username supplied!\n");
2400 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2401 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2402 x_fprintf(x_stdout, "Error: No NT or LM password "
2403 "blobs supplied!\n");
2404 } else {
2405 char *error_string = NULL;
2407 if (full_username && !username) {
2408 fstring fstr_user;
2409 fstring fstr_domain;
2411 if (!parse_ntlm_auth_domain_user(full_username,
2412 fstr_user,
2413 fstr_domain)) {
2414 /* username might be 'tainted', don't
2415 * print into our new-line
2416 * deleimianted stream */
2417 x_fprintf(x_stdout, "Error: Could not "
2418 "parse into domain and "
2419 "username\n");
2420 SAFE_FREE(username);
2421 username = smb_xstrdup(full_username);
2422 } else {
2423 SAFE_FREE(username);
2424 SAFE_FREE(domain);
2425 username = smb_xstrdup(fstr_user);
2426 domain = smb_xstrdup(fstr_domain);
2431 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2432 username, domain,
2433 new_nt_pswd,
2434 old_nt_hash_enc,
2435 new_lm_pswd,
2436 old_lm_hash_enc,
2437 &error_string))) {
2438 x_fprintf(x_stdout, "Password-Change: No\n");
2439 x_fprintf(x_stdout, "Password-Change-Error: "
2440 "%s\n.\n", error_string);
2441 } else {
2442 x_fprintf(x_stdout, "Password-Change: Yes\n");
2445 SAFE_FREE(error_string);
2447 /* clear out the state */
2448 new_nt_pswd = data_blob_null;
2449 old_nt_hash_enc = data_blob_null;
2450 new_lm_pswd = data_blob_null;
2451 old_nt_hash_enc = data_blob_null;
2452 SAFE_FREE(full_username);
2453 SAFE_FREE(username);
2454 SAFE_FREE(domain);
2455 SAFE_FREE(newpswd);
2456 SAFE_FREE(oldpswd);
2457 x_fprintf(x_stdout, ".\n");
2459 return;
2462 request = buf;
2464 /* Indicates a base64 encoded structure */
2465 parameter = strstr_m(request, ":: ");
2466 if (!parameter) {
2467 parameter = strstr_m(request, ": ");
2469 if (!parameter) {
2470 DEBUG(0, ("Parameter not found!\n"));
2471 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2472 return;
2475 parameter[0] ='\0';
2476 parameter++;
2477 parameter[0] ='\0';
2478 parameter++;
2479 } else {
2480 parameter[0] ='\0';
2481 parameter++;
2482 parameter[0] ='\0';
2483 parameter++;
2484 parameter[0] ='\0';
2485 parameter++;
2487 base64_decode_inplace(parameter);
2490 if (strequal(request, "new-nt-password-blob")) {
2491 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2492 if (new_nt_pswd.length != 516) {
2493 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2494 "(got %d bytes, expected 516)\n.\n",
2495 parameter,
2496 (int)new_nt_pswd.length);
2497 new_nt_pswd = data_blob_null;
2499 } else if (strequal(request, "old-nt-hash-blob")) {
2500 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2501 if (old_nt_hash_enc.length != 16) {
2502 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2503 "(got %d bytes, expected 16)\n.\n",
2504 parameter,
2505 (int)old_nt_hash_enc.length);
2506 old_nt_hash_enc = data_blob_null;
2508 } else if (strequal(request, "new-lm-password-blob")) {
2509 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2510 if (new_lm_pswd.length != 516) {
2511 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2512 "(got %d bytes, expected 516)\n.\n",
2513 parameter,
2514 (int)new_lm_pswd.length);
2515 new_lm_pswd = data_blob_null;
2518 else if (strequal(request, "old-lm-hash-blob")) {
2519 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2520 if (old_lm_hash_enc.length != 16)
2522 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2523 "(got %d bytes, expected 16)\n.\n",
2524 parameter,
2525 (int)old_lm_hash_enc.length);
2526 old_lm_hash_enc = data_blob_null;
2528 } else if (strequal(request, "nt-domain")) {
2529 domain = smb_xstrdup(parameter);
2530 } else if(strequal(request, "username")) {
2531 username = smb_xstrdup(parameter);
2532 } else if(strequal(request, "full-username")) {
2533 username = smb_xstrdup(parameter);
2534 } else if(strequal(request, "new-password")) {
2535 newpswd = smb_xstrdup(parameter);
2536 } else if (strequal(request, "old-password")) {
2537 oldpswd = smb_xstrdup(parameter);
2538 } else {
2539 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2543 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2544 struct loadparm_context *lp_ctx,
2545 struct ntlm_auth_state *state,
2546 stdio_helper_function fn, void **private2)
2548 char *buf;
2549 char tmp[INITIAL_BUFFER_SIZE+1];
2550 int length, buf_size = 0;
2551 char *c;
2553 buf = talloc_strdup(state->mem_ctx, "");
2554 if (!buf) {
2555 DEBUG(0, ("Failed to allocate input buffer.\n"));
2556 x_fprintf(x_stderr, "ERR\n");
2557 exit(1);
2560 do {
2562 /* this is not a typo - x_fgets doesn't work too well under
2563 * squid */
2564 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2565 if (ferror(stdin)) {
2566 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2567 "(%s)\n", ferror(stdin),
2568 strerror(ferror(stdin))));
2570 exit(1);
2572 exit(0);
2575 buf = talloc_strdup_append_buffer(buf, tmp);
2576 buf_size += INITIAL_BUFFER_SIZE;
2578 if (buf_size > MAX_BUFFER_SIZE) {
2579 DEBUG(2, ("Oversized message\n"));
2580 x_fprintf(x_stderr, "ERR\n");
2581 talloc_free(buf);
2582 return;
2585 c = strchr(buf, '\n');
2586 } while (c == NULL);
2588 *c = '\0';
2589 length = c-buf;
2591 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2593 if (buf[0] == '\0') {
2594 DEBUG(2, ("Invalid Request\n"));
2595 x_fprintf(x_stderr, "ERR\n");
2596 talloc_free(buf);
2597 return;
2600 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2601 talloc_free(buf);
2605 static void squid_stream(enum stdio_helper_mode stdio_mode,
2606 struct loadparm_context *lp_ctx,
2607 stdio_helper_function fn) {
2608 TALLOC_CTX *mem_ctx;
2609 struct ntlm_auth_state *state;
2611 /* initialize FDescs */
2612 x_setbuf(x_stdout, NULL);
2613 x_setbuf(x_stderr, NULL);
2615 mem_ctx = talloc_init("ntlm_auth");
2616 if (!mem_ctx) {
2617 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2618 x_fprintf(x_stderr, "ERR\n");
2619 exit(1);
2622 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2623 if (!state) {
2624 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2625 x_fprintf(x_stderr, "ERR\n");
2626 exit(1);
2629 state->mem_ctx = mem_ctx;
2630 state->helper_mode = stdio_mode;
2632 while(1) {
2633 TALLOC_CTX *frame = talloc_stackframe();
2634 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2635 TALLOC_FREE(frame);
2640 /* Authenticate a user with a challenge/response */
2642 static bool check_auth_crap(void)
2644 NTSTATUS nt_status;
2645 uint32 flags = 0;
2646 char lm_key[8];
2647 char user_session_key[16];
2648 char *hex_lm_key;
2649 char *hex_user_session_key;
2650 char *error_string;
2651 static uint8 zeros[16];
2653 x_setbuf(x_stdout, NULL);
2655 if (request_lm_key)
2656 flags |= WBFLAG_PAM_LMKEY;
2658 if (request_user_session_key)
2659 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2661 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2663 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2664 opt_workstation,
2665 &opt_challenge,
2666 &opt_lm_response,
2667 &opt_nt_response,
2668 flags, 0,
2669 (unsigned char *)lm_key,
2670 (unsigned char *)user_session_key,
2671 &error_string, NULL);
2673 if (!NT_STATUS_IS_OK(nt_status)) {
2674 x_fprintf(x_stdout, "%s (0x%x)\n",
2675 error_string,
2676 NT_STATUS_V(nt_status));
2677 SAFE_FREE(error_string);
2678 return False;
2681 if (request_lm_key
2682 && (memcmp(zeros, lm_key,
2683 sizeof(lm_key)) != 0)) {
2684 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2685 sizeof(lm_key));
2686 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2687 TALLOC_FREE(hex_lm_key);
2689 if (request_user_session_key
2690 && (memcmp(zeros, user_session_key,
2691 sizeof(user_session_key)) != 0)) {
2692 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2693 sizeof(user_session_key));
2694 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2695 TALLOC_FREE(hex_user_session_key);
2698 return True;
2701 /* Main program */
2703 enum {
2704 OPT_USERNAME = 1000,
2705 OPT_DOMAIN,
2706 OPT_WORKSTATION,
2707 OPT_CHALLENGE,
2708 OPT_RESPONSE,
2709 OPT_LM,
2710 OPT_NT,
2711 OPT_PASSWORD,
2712 OPT_LM_KEY,
2713 OPT_USER_SESSION_KEY,
2714 OPT_DIAGNOSTICS,
2715 OPT_REQUIRE_MEMBERSHIP,
2716 OPT_USE_CACHED_CREDS,
2717 OPT_PAM_WINBIND_CONF,
2718 OPT_TARGET_SERVICE,
2719 OPT_TARGET_HOSTNAME
2722 int main(int argc, const char **argv)
2724 TALLOC_CTX *frame = talloc_stackframe();
2725 int opt;
2726 static const char *helper_protocol;
2727 static int diagnostics;
2729 static const char *hex_challenge;
2730 static const char *hex_lm_response;
2731 static const char *hex_nt_response;
2732 struct loadparm_context *lp_ctx;
2733 poptContext pc;
2735 /* NOTE: DO NOT change this interface without considering the implications!
2736 This is an external interface, which other programs will use to interact
2737 with this helper.
2740 /* We do not use single-letter command abbreviations, because they harm future
2741 interface stability. */
2743 struct poptOption long_options[] = {
2744 POPT_AUTOHELP
2745 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2746 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2747 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2748 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2749 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2750 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2751 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2752 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2753 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2754 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2755 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2756 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2757 OPT_DIAGNOSTICS,
2758 "Perform diagnostics on the authentication chain"},
2759 { "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" },
2760 { "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" },
2761 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2762 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2763 POPT_COMMON_CONFIGFILE
2764 POPT_COMMON_VERSION
2765 POPT_TABLEEND
2768 /* Samba client initialisation */
2769 load_case_tables();
2771 setup_logging("ntlm_auth", DEBUG_STDERR);
2773 /* Parse options */
2775 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2777 /* Parse command line options */
2779 if (argc == 1) {
2780 poptPrintHelp(pc, stderr, 0);
2781 return 1;
2784 while((opt = poptGetNextOpt(pc)) != -1) {
2785 /* Get generic config options like --configfile */
2788 poptFreeContext(pc);
2790 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2791 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2792 get_dyn_CONFIGFILE(), strerror(errno));
2793 exit(1);
2796 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2797 POPT_CONTEXT_KEEP_FIRST);
2799 while((opt = poptGetNextOpt(pc)) != -1) {
2800 switch (opt) {
2801 case OPT_CHALLENGE:
2802 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2803 if (opt_challenge.length != 8) {
2804 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2805 hex_challenge,
2806 (int)opt_challenge.length);
2807 exit(1);
2809 break;
2810 case OPT_LM:
2811 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2812 if (opt_lm_response.length != 24) {
2813 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2814 hex_lm_response,
2815 (int)opt_lm_response.length);
2816 exit(1);
2818 break;
2820 case OPT_NT:
2821 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2822 if (opt_nt_response.length < 24) {
2823 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2824 hex_nt_response,
2825 (int)opt_nt_response.length);
2826 exit(1);
2828 break;
2830 case OPT_REQUIRE_MEMBERSHIP:
2831 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2832 require_membership_of_sid = require_membership_of;
2834 break;
2838 if (opt_username) {
2839 char *domain = SMB_STRDUP(opt_username);
2840 char *p = strchr_m(domain, *lp_winbind_separator());
2841 if (p) {
2842 opt_username = p+1;
2843 *p = '\0';
2844 if (opt_domain && !strequal(opt_domain, domain)) {
2845 x_fprintf(x_stderr, "Domain specified in username (%s) "
2846 "doesn't match specified domain (%s)!\n\n",
2847 domain, opt_domain);
2848 poptPrintHelp(pc, stderr, 0);
2849 exit(1);
2851 opt_domain = domain;
2852 } else {
2853 SAFE_FREE(domain);
2857 /* Note: if opt_domain is "" then send no domain */
2858 if (opt_domain == NULL) {
2859 opt_domain = get_winbind_domain();
2862 if (opt_workstation == NULL) {
2863 opt_workstation = "";
2866 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2867 if (lp_ctx == NULL) {
2868 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2869 exit(1);
2872 if (helper_protocol) {
2873 int i;
2874 for (i=0; i<NUM_HELPER_MODES; i++) {
2875 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2876 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2877 exit(0);
2880 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2882 for (i=0; i<NUM_HELPER_MODES; i++) {
2883 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2886 exit(1);
2889 if (!opt_username || !*opt_username) {
2890 x_fprintf(x_stderr, "username must be specified!\n\n");
2891 poptPrintHelp(pc, stderr, 0);
2892 exit(1);
2895 if (opt_challenge.length) {
2896 if (!check_auth_crap()) {
2897 exit(1);
2899 exit(0);
2902 if (!opt_password) {
2903 opt_password = getpass("password: ");
2906 if (diagnostics) {
2907 if (!diagnose_ntlm_auth()) {
2908 return 1;
2910 } else {
2911 fstring user;
2913 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2914 if (!check_plaintext_auth(user, opt_password, True)) {
2915 return 1;
2919 /* Exit code */
2921 poptFreeContext(pc);
2922 TALLOC_FREE(frame);
2923 return 0;