Add two flags to allow for handling of Extended Signatures (Session Key Protection...
[Samba/gebeck_regimport.git] / source3 / utils / ntlm_auth.c
blob103a1f6d16ef6d7ddbf2266381776c3f9e242ae3
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"
48 #include "lib/param/loadparm.h"
50 #if HAVE_KRB5
51 #include "auth/kerberos/pac_utils.h"
52 #endif
54 #ifndef PAM_WINBIND_CONFIG_FILE
55 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
56 #endif
58 #define WINBIND_KRB5_AUTH 0x00000080
60 #undef DBGC_CLASS
61 #define DBGC_CLASS DBGC_WINBIND
63 #define INITIAL_BUFFER_SIZE 300
64 #define MAX_BUFFER_SIZE 630000
66 enum stdio_helper_mode {
67 SQUID_2_4_BASIC,
68 SQUID_2_5_BASIC,
69 SQUID_2_5_NTLMSSP,
70 NTLMSSP_CLIENT_1,
71 GSS_SPNEGO_SERVER,
72 GSS_SPNEGO_CLIENT,
73 NTLM_SERVER_1,
74 NTLM_CHANGE_PASSWORD_1,
75 NUM_HELPER_MODES
78 enum ntlm_auth_cli_state {
79 CLIENT_INITIAL = 0,
80 CLIENT_RESPONSE,
81 CLIENT_FINISHED,
82 CLIENT_ERROR
85 struct ntlm_auth_state {
86 TALLOC_CTX *mem_ctx;
87 enum stdio_helper_mode helper_mode;
88 enum ntlm_auth_cli_state cli_state;
89 struct ntlmssp_state *ntlmssp_state;
90 uint32_t neg_flags;
91 char *want_feature_list;
92 bool have_session_key;
93 DATA_BLOB session_key;
94 DATA_BLOB initial_message;
95 void *gensec_private_1;
97 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
98 struct loadparm_context *lp_ctx,
99 struct ntlm_auth_state *state, char *buf,
100 int length, void **private2);
102 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
103 struct loadparm_context *lp_ctx,
104 struct ntlm_auth_state *state,
105 stdio_helper_function fn, void **private2);
107 static void manage_squid_basic_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_squid_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_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
118 struct loadparm_context *lp_ctx,
119 struct ntlm_auth_state *state,
120 char *buf, int length, void **private2);
122 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
123 struct loadparm_context *lp_ctx,
124 struct ntlm_auth_state *state,
125 char *buf, int length, void **private2);
127 static void manage_gss_spnego_client_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_server_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 void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
138 struct loadparm_context *lp_ctx,
139 struct ntlm_auth_state *state,
140 char *buf, int length, void **private2);
142 static const struct {
143 enum stdio_helper_mode mode;
144 const char *name;
145 stdio_helper_function fn;
146 } stdio_helper_protocols[] = {
147 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
148 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
149 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
150 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
151 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
152 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
153 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
154 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
155 { NUM_HELPER_MODES, NULL, NULL}
158 const char *opt_username;
159 const char *opt_domain;
160 const char *opt_workstation;
161 const char *opt_password;
162 static DATA_BLOB opt_challenge;
163 static DATA_BLOB opt_lm_response;
164 static DATA_BLOB opt_nt_response;
165 static int request_lm_key;
166 static int request_user_session_key;
167 static int use_cached_creds;
169 static const char *require_membership_of;
170 static const char *require_membership_of_sid;
171 static const char *opt_pam_winbind_conf;
173 const char *opt_target_service;
174 const char *opt_target_hostname;
177 /* This is a bit hairy, but the basic idea is to do a password callback
178 to the calling application. The callback comes from within gensec */
180 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
181 struct loadparm_context *lp_ctx,
182 struct ntlm_auth_state *state, char *buf, int length,
183 void **password)
185 DATA_BLOB in;
186 if (strlen(buf) < 2) {
187 DEBUG(1, ("query [%s] invalid", buf));
188 x_fprintf(x_stdout, "BH Query invalid\n");
189 return;
192 if (strlen(buf) > 3) {
193 in = base64_decode_data_blob(buf + 3);
194 } else {
195 in = data_blob(NULL, 0);
198 if (strncmp(buf, "PW ", 3) == 0) {
200 *password = talloc_strndup(NULL,
201 (const char *)in.data, in.length);
203 if (*password == NULL) {
204 DEBUG(1, ("Out of memory\n"));
205 x_fprintf(x_stdout, "BH Out of memory\n");
206 data_blob_free(&in);
207 return;
210 x_fprintf(x_stdout, "OK\n");
211 data_blob_free(&in);
212 return;
214 DEBUG(1, ("Asked for (and expected) a password\n"));
215 x_fprintf(x_stdout, "BH Expected a password\n");
216 data_blob_free(&in);
220 * Callback for password credentials. This is not async, and when
221 * GENSEC and the credentials code is made async, it will look rather
222 * different.
225 static const char *get_password(struct cli_credentials *credentials)
227 char *password = NULL;
229 /* Ask for a password */
230 x_fprintf(x_stdout, "PW\n");
231 credentials->priv_data = NULL;
233 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, NULL, manage_gensec_get_pw_request, (void **)&password);
234 talloc_steal(credentials, password);
235 return password;
239 * A limited set of features are defined with text strings as needed
240 * by ntlm_auth
243 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
245 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
246 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
247 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
249 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
250 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
251 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
253 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
254 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
255 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
259 static char winbind_separator(void)
261 struct winbindd_response response;
262 static bool got_sep;
263 static char sep;
265 if (got_sep)
266 return sep;
268 ZERO_STRUCT(response);
270 /* Send off request */
272 if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
273 NSS_STATUS_SUCCESS) {
274 d_printf("could not obtain winbind separator!\n");
275 return *lp_winbind_separator();
278 sep = response.data.info.winbind_separator;
279 got_sep = True;
281 if (!sep) {
282 d_printf("winbind separator was NULL!\n");
283 return *lp_winbind_separator();
286 return sep;
289 const char *get_winbind_domain(void)
291 struct winbindd_response response;
293 static fstring winbind_domain;
294 if (*winbind_domain) {
295 return winbind_domain;
298 ZERO_STRUCT(response);
300 /* Send off request */
302 if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
303 NSS_STATUS_SUCCESS) {
304 DEBUG(0, ("could not obtain winbind domain name!\n"));
305 return lp_workgroup();
308 fstrcpy(winbind_domain, response.data.domain_name);
310 return winbind_domain;
314 const char *get_winbind_netbios_name(void)
316 struct winbindd_response response;
318 static fstring winbind_netbios_name;
320 if (*winbind_netbios_name) {
321 return winbind_netbios_name;
324 ZERO_STRUCT(response);
326 /* Send off request */
328 if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) !=
329 NSS_STATUS_SUCCESS) {
330 DEBUG(0, ("could not obtain winbind netbios name!\n"));
331 return lp_netbios_name();
334 fstrcpy(winbind_netbios_name, response.data.netbios_name);
336 return winbind_netbios_name;
340 DATA_BLOB get_challenge(void)
342 static DATA_BLOB chal;
343 if (opt_challenge.length)
344 return opt_challenge;
346 chal = data_blob(NULL, 8);
348 generate_random_buffer(chal.data, chal.length);
349 return chal;
352 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
353 form DOMAIN/user into a domain and a user */
355 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
356 fstring user)
359 char *p = strchr(domuser,winbind_separator());
361 if (!p) {
362 return False;
365 fstrcpy(user, p+1);
366 fstrcpy(domain, domuser);
367 domain[PTR_DIFF(p, domuser)] = 0;
368 strupper_m(domain);
370 return True;
373 static bool get_require_membership_sid(void) {
374 struct winbindd_request request;
375 struct winbindd_response response;
377 if (!require_membership_of) {
378 return True;
381 if (require_membership_of_sid) {
382 return True;
385 /* Otherwise, ask winbindd for the name->sid request */
387 ZERO_STRUCT(request);
388 ZERO_STRUCT(response);
390 if (!parse_ntlm_auth_domain_user(require_membership_of,
391 request.data.name.dom_name,
392 request.data.name.name)) {
393 DEBUG(0, ("Could not parse %s into seperate domain/name parts!\n",
394 require_membership_of));
395 return False;
398 if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
399 NSS_STATUS_SUCCESS) {
400 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
401 require_membership_of));
402 return False;
405 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
407 if (require_membership_of_sid)
408 return True;
410 return False;
414 * Get some configuration from pam_winbind.conf to see if we
415 * need to contact trusted domain
418 int get_pam_winbind_config()
420 int ctrl = 0;
421 dictionary *d = NULL;
423 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
424 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
427 d = iniparser_load(discard_const_p(char, opt_pam_winbind_conf));
429 if (!d) {
430 return 0;
433 if (iniparser_getboolean(d, discard_const_p(char, "global:krb5_auth"), false)) {
434 ctrl |= WINBIND_KRB5_AUTH;
437 iniparser_freedict(d);
439 return ctrl;
442 /* Authenticate a user with a plaintext password */
444 static bool check_plaintext_auth(const char *user, const char *pass,
445 bool stdout_diagnostics)
447 struct winbindd_request request;
448 struct winbindd_response response;
449 NSS_STATUS result;
451 if (!get_require_membership_sid()) {
452 return False;
455 /* Send off request */
457 ZERO_STRUCT(request);
458 ZERO_STRUCT(response);
460 fstrcpy(request.data.auth.user, user);
461 fstrcpy(request.data.auth.pass, pass);
462 if (require_membership_of_sid) {
463 strlcpy(request.data.auth.require_membership_of_sid,
464 require_membership_of_sid,
465 sizeof(request.data.auth.require_membership_of_sid));
468 result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
470 /* Display response */
472 if (stdout_diagnostics) {
473 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
474 d_printf("Reading winbind reply failed! (0x01)\n");
477 d_printf("%s: %s (0x%x)\n",
478 response.data.auth.nt_status_string,
479 response.data.auth.error_string,
480 response.data.auth.nt_status);
481 } else {
482 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
483 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
486 DEBUG(3, ("%s: %s (0x%x)\n",
487 response.data.auth.nt_status_string,
488 response.data.auth.error_string,
489 response.data.auth.nt_status));
492 return (result == NSS_STATUS_SUCCESS);
495 /* authenticate a user with an encrypted username/password */
497 NTSTATUS contact_winbind_auth_crap(const char *username,
498 const char *domain,
499 const char *workstation,
500 const DATA_BLOB *challenge,
501 const DATA_BLOB *lm_response,
502 const DATA_BLOB *nt_response,
503 uint32 flags,
504 uint32 extra_logon_parameters,
505 uint8 lm_key[8],
506 uint8 user_session_key[16],
507 char **error_string,
508 char **unix_name)
510 NTSTATUS nt_status;
511 NSS_STATUS result;
512 struct winbindd_request request;
513 struct winbindd_response response;
515 if (!get_require_membership_sid()) {
516 return NT_STATUS_INVALID_PARAMETER;
519 ZERO_STRUCT(request);
520 ZERO_STRUCT(response);
522 request.flags = flags;
524 request.data.auth_crap.logon_parameters = extra_logon_parameters
525 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
527 if (require_membership_of_sid)
528 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
530 fstrcpy(request.data.auth_crap.user, username);
531 fstrcpy(request.data.auth_crap.domain, domain);
533 fstrcpy(request.data.auth_crap.workstation,
534 workstation);
536 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
538 if (lm_response && lm_response->length) {
539 memcpy(request.data.auth_crap.lm_resp,
540 lm_response->data,
541 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
542 request.data.auth_crap.lm_resp_len = lm_response->length;
545 if (nt_response && nt_response->length) {
546 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
547 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
548 request.extra_len = nt_response->length;
549 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
550 if (request.extra_data.data == NULL) {
551 return NT_STATUS_NO_MEMORY;
553 memcpy(request.extra_data.data, nt_response->data,
554 nt_response->length);
556 } else {
557 memcpy(request.data.auth_crap.nt_resp,
558 nt_response->data, nt_response->length);
560 request.data.auth_crap.nt_resp_len = nt_response->length;
563 result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
564 SAFE_FREE(request.extra_data.data);
566 /* Display response */
568 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
569 nt_status = NT_STATUS_UNSUCCESSFUL;
570 if (error_string)
571 *error_string = smb_xstrdup("Reading winbind reply failed!");
572 winbindd_free_response(&response);
573 return nt_status;
576 nt_status = (NT_STATUS(response.data.auth.nt_status));
577 if (!NT_STATUS_IS_OK(nt_status)) {
578 if (error_string)
579 *error_string = smb_xstrdup(response.data.auth.error_string);
580 winbindd_free_response(&response);
581 return nt_status;
584 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
585 memcpy(lm_key, response.data.auth.first_8_lm_hash,
586 sizeof(response.data.auth.first_8_lm_hash));
588 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
589 memcpy(user_session_key, response.data.auth.user_session_key,
590 sizeof(response.data.auth.user_session_key));
593 if (flags & WBFLAG_PAM_UNIX_NAME) {
594 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
595 if (!*unix_name) {
596 winbindd_free_response(&response);
597 return NT_STATUS_NO_MEMORY;
601 winbindd_free_response(&response);
602 return nt_status;
605 /* contact server to change user password using auth crap */
606 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
607 const char *domain,
608 const DATA_BLOB new_nt_pswd,
609 const DATA_BLOB old_nt_hash_enc,
610 const DATA_BLOB new_lm_pswd,
611 const DATA_BLOB old_lm_hash_enc,
612 char **error_string)
614 NTSTATUS nt_status;
615 NSS_STATUS result;
616 struct winbindd_request request;
617 struct winbindd_response response;
619 if (!get_require_membership_sid())
621 if(error_string)
622 *error_string = smb_xstrdup("Can't get membership sid.");
623 return NT_STATUS_INVALID_PARAMETER;
626 ZERO_STRUCT(request);
627 ZERO_STRUCT(response);
629 if(username != NULL)
630 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
631 if(domain != NULL)
632 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
634 if(new_nt_pswd.length)
636 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
637 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
640 if(old_nt_hash_enc.length)
642 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));
643 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
646 if(new_lm_pswd.length)
648 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
649 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
652 if(old_lm_hash_enc.length)
654 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));
655 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
658 result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
660 /* Display response */
662 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
664 nt_status = NT_STATUS_UNSUCCESSFUL;
665 if (error_string)
666 *error_string = smb_xstrdup("Reading winbind reply failed!");
667 winbindd_free_response(&response);
668 return nt_status;
671 nt_status = (NT_STATUS(response.data.auth.nt_status));
672 if (!NT_STATUS_IS_OK(nt_status))
674 if (error_string)
675 *error_string = smb_xstrdup(response.data.auth.error_string);
676 winbindd_free_response(&response);
677 return nt_status;
680 winbindd_free_response(&response);
682 return nt_status;
685 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
686 TALLOC_CTX *mem_ctx,
687 void *server_returned_info,
688 const char *original_user_name,
689 uint32_t session_info_flags,
690 struct auth_session_info **session_info_out)
692 char *unix_username = (char *)server_returned_info;
693 struct auth_session_info *session_info = talloc_zero(mem_ctx, struct auth_session_info);
694 if (!session_info) {
695 return NT_STATUS_NO_MEMORY;
698 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
699 if (!session_info->unix_info) {
700 TALLOC_FREE(session_info);
701 return NT_STATUS_NO_MEMORY;
703 session_info->unix_info->unix_name = talloc_steal(session_info->unix_info, unix_username);
705 *session_info_out = session_info;
707 return NT_STATUS_OK;
710 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
711 TALLOC_CTX *mem_ctx,
712 struct smb_krb5_context *smb_krb5_context,
713 DATA_BLOB *pac_blob,
714 const char *princ_name,
715 const struct tsocket_address *remote_address,
716 uint32_t session_info_flags,
717 struct auth_session_info **session_info)
719 TALLOC_CTX *tmp_ctx;
720 struct PAC_LOGON_INFO *logon_info = NULL;
721 char *unixuser;
722 NTSTATUS status;
723 char *domain = NULL;
724 char *realm = NULL;
725 char *user = NULL;
726 char *p;
728 tmp_ctx = talloc_new(mem_ctx);
729 if (!tmp_ctx) {
730 return NT_STATUS_NO_MEMORY;
733 if (pac_blob) {
734 #ifdef HAVE_KRB5
735 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
736 NULL, NULL, 0, &logon_info);
737 #else
738 status = NT_STATUS_ACCESS_DENIED;
739 #endif
740 if (!NT_STATUS_IS_OK(status)) {
741 goto done;
745 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
747 p = strchr_m(princ_name, '@');
748 if (!p) {
749 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
750 princ_name));
751 return NT_STATUS_LOGON_FAILURE;
754 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
755 if (!user) {
756 return NT_STATUS_NO_MEMORY;
759 realm = talloc_strdup(talloc_tos(), p + 1);
760 if (!realm) {
761 return NT_STATUS_NO_MEMORY;
764 if (!strequal(realm, lp_realm())) {
765 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
766 if (!lp_allow_trusted_domains()) {
767 return NT_STATUS_LOGON_FAILURE;
771 if (logon_info && logon_info->info3.base.logon_domain.string) {
772 domain = talloc_strdup(mem_ctx,
773 logon_info->info3.base.logon_domain.string);
774 if (!domain) {
775 return NT_STATUS_NO_MEMORY;
777 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
778 } else {
780 /* If we have winbind running, we can (and must) shorten the
781 username by using the short netbios name. Otherwise we will
782 have inconsistent user names. With Kerberos, we get the
783 fully qualified realm, with ntlmssp we get the short
784 name. And even w2k3 does use ntlmssp if you for example
785 connect to an ip address. */
787 wbcErr wbc_status;
788 struct wbcDomainInfo *info = NULL;
790 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
791 realm));
793 wbc_status = wbcDomainInfo(realm, &info);
795 if (WBC_ERROR_IS_OK(wbc_status)) {
796 domain = talloc_strdup(mem_ctx,
797 info->short_name);
798 wbcFreeMemory(info);
799 } else {
800 DEBUG(3, ("Could not find short name: %s\n",
801 wbcErrorString(wbc_status)));
802 domain = talloc_strdup(mem_ctx, realm);
804 if (!domain) {
805 return NT_STATUS_NO_MEMORY;
807 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
810 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
811 if (!unixuser) {
812 status = NT_STATUS_NO_MEMORY;
813 goto done;
816 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
818 done:
819 TALLOC_FREE(tmp_ctx);
820 return status;
826 * Return the challenge as determined by the authentication subsystem
827 * @return an 8 byte random challenge
830 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
831 uint8_t chal[8])
833 if (auth_ctx->challenge.data.length == 8) {
834 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
835 auth_ctx->challenge.set_by));
836 memcpy(chal, auth_ctx->challenge.data.data, 8);
837 return NT_STATUS_OK;
840 if (!auth_ctx->challenge.set_by) {
841 generate_random_buffer(chal, 8);
843 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
844 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
845 auth_ctx->challenge.set_by = "random";
848 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
849 auth_ctx->challenge.set_by));
851 return NT_STATUS_OK;
855 * NTLM2 authentication modifies the effective challenge,
856 * @param challenge The new challenge value
858 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
860 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
861 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
863 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
864 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
866 return NT_STATUS_OK;
870 * Check the password on an NTLMSSP login.
872 * Return the session keys used on the connection.
875 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
876 TALLOC_CTX *mem_ctx,
877 const struct auth_usersupplied_info *user_info,
878 void **server_returned_info,
879 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
881 static const char zeros[16] = { 0, };
882 NTSTATUS nt_status;
883 char *error_string = NULL;
884 uint8 lm_key[8];
885 uint8 user_sess_key[16];
886 char *unix_name = NULL;
888 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
889 user_info->workstation_name,
890 &auth4_context->challenge.data,
891 &user_info->password.response.lanman,
892 &user_info->password.response.nt,
893 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
895 lm_key, user_sess_key,
896 &error_string, &unix_name);
898 if (NT_STATUS_IS_OK(nt_status)) {
899 if (memcmp(lm_key, zeros, 8) != 0) {
900 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
901 memcpy(lm_session_key->data, lm_key, 8);
902 memset(lm_session_key->data+8, '\0', 8);
905 if (memcmp(user_sess_key, zeros, 16) != 0) {
906 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
908 *server_returned_info = talloc_strdup(mem_ctx,
909 unix_name);
910 } else {
911 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
912 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
913 user_info->client.domain_name, user_info->client.account_name,
914 user_info->workstation_name,
915 error_string ? error_string : "unknown error (NULL)"));
918 SAFE_FREE(error_string);
919 SAFE_FREE(unix_name);
920 return nt_status;
923 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
924 TALLOC_CTX *mem_ctx,
925 const struct auth_usersupplied_info *user_info,
926 void **server_returned_info,
927 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
929 NTSTATUS nt_status;
930 struct samr_Password lm_pw, nt_pw;
932 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
934 nt_status = ntlm_password_check(mem_ctx,
935 true, true, 0,
936 &auth4_context->challenge.data,
937 &user_info->password.response.lanman,
938 &user_info->password.response.nt,
939 user_info->client.account_name,
940 user_info->client.account_name,
941 user_info->client.domain_name,
942 &lm_pw, &nt_pw, session_key, lm_session_key);
944 if (NT_STATUS_IS_OK(nt_status)) {
945 *server_returned_info = talloc_asprintf(mem_ctx,
946 "%s%c%s", user_info->client.domain_name,
947 *lp_winbind_separator(),
948 user_info->client.account_name);
949 } else {
950 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
951 user_info->client.domain_name, user_info->client.account_name,
952 user_info->workstation_name,
953 nt_errstr(nt_status)));
955 return nt_status;
958 static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state)
960 NTSTATUS status;
961 if ( (opt_username == NULL) || (opt_domain == NULL) ) {
962 status = NT_STATUS_UNSUCCESSFUL;
963 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
964 return NT_STATUS_INVALID_PARAMETER;
967 status = ntlmssp_client_start(NULL,
968 lp_netbios_name(),
969 lp_workgroup(),
970 lp_client_ntlmv2_auth(),
971 client_ntlmssp_state);
973 if (!NT_STATUS_IS_OK(status)) {
974 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
975 nt_errstr(status)));
976 TALLOC_FREE(*client_ntlmssp_state);
977 return status;
980 status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
982 if (!NT_STATUS_IS_OK(status)) {
983 DEBUG(1, ("Could not set username: %s\n",
984 nt_errstr(status)));
985 TALLOC_FREE(*client_ntlmssp_state);
986 return status;
989 status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
991 if (!NT_STATUS_IS_OK(status)) {
992 DEBUG(1, ("Could not set domain: %s\n",
993 nt_errstr(status)));
994 TALLOC_FREE(*client_ntlmssp_state);
995 return status;
998 if (opt_password) {
999 status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
1001 if (!NT_STATUS_IS_OK(status)) {
1002 DEBUG(1, ("Could not set password: %s\n",
1003 nt_errstr(status)));
1004 TALLOC_FREE(*client_ntlmssp_state);
1005 return status;
1009 return NT_STATUS_OK;
1012 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1014 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1015 if (auth4_context == NULL) {
1016 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1017 return NULL;
1019 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1020 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1021 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1022 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1023 if (local_pw) {
1024 auth4_context->check_ntlm_password = local_pw_check;
1025 } else {
1026 auth4_context->check_ntlm_password = winbind_pw_check;
1028 auth4_context->private_data = NULL;
1029 return auth4_context;
1032 static NTSTATUS ntlm_auth_start_ntlmssp_server(TALLOC_CTX *mem_ctx,
1033 struct loadparm_context *lp_ctx,
1034 struct gensec_security **gensec_security_out)
1036 struct gensec_security *gensec_security;
1037 NTSTATUS nt_status;
1039 TALLOC_CTX *tmp_ctx;
1041 struct gensec_settings *gensec_settings;
1042 size_t idx = 0;
1043 struct cli_credentials *server_credentials;
1045 struct auth4_context *auth4_context;
1047 tmp_ctx = talloc_new(mem_ctx);
1048 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1050 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1051 if (auth4_context == NULL) {
1052 TALLOC_FREE(tmp_ctx);
1053 return NT_STATUS_NO_MEMORY;
1056 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1057 if (lp_ctx == NULL) {
1058 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1059 TALLOC_FREE(tmp_ctx);
1060 return NT_STATUS_NO_MEMORY;
1064 * This should be a 'netbios domain -> DNS domain'
1065 * mapping, and can currently validly return NULL on
1066 * poorly configured systems.
1068 * This is used for the NTLMSSP server
1071 if (opt_password) {
1072 gensec_settings->server_netbios_name = lp_netbios_name();
1073 gensec_settings->server_netbios_domain = lp_workgroup();
1074 } else {
1075 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1076 gensec_settings->server_netbios_domain = get_winbind_domain();
1079 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1080 get_mydnsdomname(talloc_tos()));
1081 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1082 get_mydnsfullname());
1084 gensec_settings->backends = talloc_zero_array(gensec_settings,
1085 struct gensec_security_ops *, 4);
1087 if (gensec_settings->backends == NULL) {
1088 TALLOC_FREE(tmp_ctx);
1089 return NT_STATUS_NO_MEMORY;
1092 gensec_init();
1094 /* These need to be in priority order, krb5 before NTLMSSP */
1095 #if defined(HAVE_KRB5)
1096 gensec_settings->backends[idx++] = &gensec_gse_krb5_security_ops;
1097 #endif
1099 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1101 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL,
1102 GENSEC_OID_SPNEGO);
1105 * This is anonymous for now, because we just use it
1106 * to set the kerberos state at the moment
1108 server_credentials = cli_credentials_init_anon(tmp_ctx);
1109 if (!server_credentials) {
1110 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1111 return NT_STATUS_NO_MEMORY;
1114 cli_credentials_set_conf(server_credentials, lp_ctx);
1116 if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1117 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1118 } else {
1119 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1122 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1123 auth4_context, &gensec_security);
1125 if (!NT_STATUS_IS_OK(nt_status)) {
1126 TALLOC_FREE(tmp_ctx);
1127 return nt_status;
1130 gensec_set_credentials(gensec_security, server_credentials);
1132 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
1133 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
1135 talloc_unlink(tmp_ctx, lp_ctx);
1136 talloc_unlink(tmp_ctx, server_credentials);
1137 talloc_unlink(tmp_ctx, gensec_settings);
1138 talloc_unlink(tmp_ctx, auth4_context);
1140 nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP);
1141 if (!NT_STATUS_IS_OK(nt_status)) {
1142 TALLOC_FREE(tmp_ctx);
1143 return nt_status;
1146 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1147 TALLOC_FREE(tmp_ctx);
1148 return NT_STATUS_OK;
1151 /*******************************************************************
1152 Used by firefox to drive NTLM auth to IIS servers.
1153 *******************************************************************/
1155 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
1156 DATA_BLOB *reply)
1158 struct winbindd_request wb_request;
1159 struct winbindd_response wb_response;
1160 int ctrl = 0;
1161 NSS_STATUS result;
1163 /* get winbindd to do the ntlmssp step on our behalf */
1164 ZERO_STRUCT(wb_request);
1165 ZERO_STRUCT(wb_response);
1168 * This is tricky here. If we set krb5_auth in pam_winbind.conf
1169 * creds for users in trusted domain will be stored the winbindd
1170 * child of the trusted domain. If we ask the primary domain for
1171 * ntlm_ccache_auth, it will fail. So, we have to ask the trusted
1172 * domain's child for ccache_ntlm_auth. that is to say, we have to
1173 * set WBFLAG_PAM_CONTACT_TRUSTDOM in request.flags.
1175 ctrl = get_pam_winbind_config();
1177 if (ctrl & WINBIND_KRB5_AUTH) {
1178 wb_request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1181 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
1182 "%s%c%s", opt_domain, winbind_separator(), opt_username);
1183 wb_request.data.ccache_ntlm_auth.uid = geteuid();
1184 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
1185 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
1186 wb_request.extra_len = initial_msg.length + challenge_msg.length;
1188 if (wb_request.extra_len > 0) {
1189 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
1190 if (wb_request.extra_data.data == NULL) {
1191 return NT_STATUS_NO_MEMORY;
1194 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
1195 memcpy(wb_request.extra_data.data + initial_msg.length,
1196 challenge_msg.data, challenge_msg.length);
1199 result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
1200 SAFE_FREE(wb_request.extra_data.data);
1202 if (result != NSS_STATUS_SUCCESS) {
1203 winbindd_free_response(&wb_response);
1204 return NT_STATUS_UNSUCCESSFUL;
1207 if (reply) {
1208 *reply = data_blob(wb_response.extra_data.data,
1209 wb_response.data.ccache_ntlm_auth.auth_blob_len);
1210 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
1211 reply->data == NULL) {
1212 winbindd_free_response(&wb_response);
1213 return NT_STATUS_NO_MEMORY;
1217 winbindd_free_response(&wb_response);
1218 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1221 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1222 struct loadparm_context *lp_ctx,
1223 struct ntlm_auth_state *state,
1224 char *buf, int length, void **private2)
1226 DATA_BLOB request, reply;
1227 NTSTATUS nt_status;
1229 if (!opt_username || !*opt_username) {
1230 x_fprintf(x_stderr, "username must be specified!\n\n");
1231 exit(1);
1234 if (strlen(buf) < 2) {
1235 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1236 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1237 return;
1240 if (strlen(buf) > 3) {
1241 if(strncmp(buf, "SF ", 3) == 0) {
1242 DEBUG(10, ("Looking for flags to negotiate\n"));
1243 talloc_free(state->want_feature_list);
1244 state->want_feature_list = talloc_strdup(state->mem_ctx,
1245 buf+3);
1246 x_fprintf(x_stdout, "OK\n");
1247 return;
1249 request = base64_decode_data_blob(buf + 3);
1250 } else {
1251 request = data_blob_null;
1254 if (strncmp(buf, "PW ", 3) == 0) {
1255 /* We asked for a password and obviously got it :-) */
1257 opt_password = SMB_STRNDUP((const char *)request.data,
1258 request.length);
1260 if (opt_password == NULL) {
1261 DEBUG(1, ("Out of memory\n"));
1262 x_fprintf(x_stdout, "BH Out of memory\n");
1263 data_blob_free(&request);
1264 return;
1267 x_fprintf(x_stdout, "OK\n");
1268 data_blob_free(&request);
1269 return;
1272 if (!state->ntlmssp_state && use_cached_creds) {
1273 /* check whether cached credentials are usable. */
1274 DATA_BLOB empty_blob = data_blob_null;
1276 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
1277 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1278 /* failed to use cached creds */
1279 use_cached_creds = False;
1283 if (opt_password == NULL && !use_cached_creds) {
1284 /* Request a password from the calling process. After
1285 sending it, the calling process should retry asking for the
1286 negotiate. */
1288 DEBUG(10, ("Requesting password\n"));
1289 x_fprintf(x_stdout, "PW\n");
1290 return;
1293 if (strncmp(buf, "YR", 2) == 0) {
1294 TALLOC_FREE(state->ntlmssp_state);
1295 state->cli_state = CLIENT_INITIAL;
1296 } else if (strncmp(buf, "TT", 2) == 0) {
1297 /* No special preprocessing required */
1298 } else if (strncmp(buf, "GF", 2) == 0) {
1299 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
1301 if(state->cli_state == CLIENT_FINISHED) {
1302 x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
1304 else {
1305 x_fprintf(x_stdout, "BH\n");
1308 data_blob_free(&request);
1309 return;
1310 } else if (strncmp(buf, "GK", 2) == 0 ) {
1311 DEBUG(10, ("Requested session key\n"));
1313 if(state->cli_state == CLIENT_FINISHED) {
1314 char *key64 = base64_encode_data_blob(state->mem_ctx,
1315 state->session_key);
1316 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
1317 TALLOC_FREE(key64);
1319 else {
1320 x_fprintf(x_stdout, "BH\n");
1323 data_blob_free(&request);
1324 return;
1325 } else {
1326 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1327 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1328 return;
1331 if (!state->ntlmssp_state) {
1332 nt_status = ntlm_auth_start_ntlmssp_client(
1333 &state->ntlmssp_state);
1334 if (!NT_STATUS_IS_OK(nt_status)) {
1335 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1336 return;
1338 ntlmssp_want_feature_list(state->ntlmssp_state,
1339 state->want_feature_list);
1340 state->initial_message = data_blob_null;
1343 DEBUG(10, ("got NTLMSSP packet:\n"));
1344 dump_data(10, request.data, request.length);
1346 if (use_cached_creds && !opt_password &&
1347 (state->cli_state == CLIENT_RESPONSE)) {
1348 nt_status = do_ccache_ntlm_auth(state->initial_message, request,
1349 &reply);
1350 } else {
1351 nt_status = ntlmssp_update(state->ntlmssp_state, request,
1352 &reply);
1355 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1356 char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
1357 reply);
1358 if (state->cli_state == CLIENT_INITIAL) {
1359 x_fprintf(x_stdout, "YR %s\n", reply_base64);
1360 state->initial_message = reply;
1361 state->cli_state = CLIENT_RESPONSE;
1362 } else {
1363 x_fprintf(x_stdout, "KK %s\n", reply_base64);
1364 data_blob_free(&reply);
1366 TALLOC_FREE(reply_base64);
1367 DEBUG(10, ("NTLMSSP challenge\n"));
1368 } else if (NT_STATUS_IS_OK(nt_status)) {
1369 char *reply_base64 = base64_encode_data_blob(talloc_tos(),
1370 reply);
1371 x_fprintf(x_stdout, "AF %s\n", reply_base64);
1372 TALLOC_FREE(reply_base64);
1374 if(state->have_session_key)
1375 data_blob_free(&state->session_key);
1377 state->session_key = data_blob(
1378 state->ntlmssp_state->session_key.data,
1379 state->ntlmssp_state->session_key.length);
1380 state->neg_flags = state->ntlmssp_state->neg_flags;
1381 state->have_session_key = true;
1383 DEBUG(10, ("NTLMSSP OK!\n"));
1384 state->cli_state = CLIENT_FINISHED;
1385 TALLOC_FREE(state->ntlmssp_state);
1386 } else {
1387 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1388 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
1389 state->cli_state = CLIENT_ERROR;
1390 TALLOC_FREE(state->ntlmssp_state);
1393 data_blob_free(&request);
1396 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1397 struct loadparm_context *lp_ctx,
1398 struct ntlm_auth_state *state,
1399 char *buf, int length, void **private2)
1401 char *user, *pass;
1402 user=buf;
1404 pass=(char *)memchr(buf,' ',length);
1405 if (!pass) {
1406 DEBUG(2, ("Password not found. Denying access\n"));
1407 x_fprintf(x_stdout, "ERR\n");
1408 return;
1410 *pass='\0';
1411 pass++;
1413 if (state->helper_mode == SQUID_2_5_BASIC) {
1414 rfc1738_unescape(user);
1415 rfc1738_unescape(pass);
1418 if (check_plaintext_auth(user, pass, False)) {
1419 x_fprintf(x_stdout, "OK\n");
1420 } else {
1421 x_fprintf(x_stdout, "ERR\n");
1425 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1426 struct loadparm_context *lp_ctx,
1427 char *buf, int length, void **private1)
1429 DATA_BLOB in;
1430 DATA_BLOB out = data_blob(NULL, 0);
1431 char *out_base64 = NULL;
1432 const char *reply_arg = NULL;
1433 struct gensec_ntlm_state {
1434 struct gensec_security *gensec_state;
1435 const char *set_password;
1437 struct gensec_ntlm_state *state;
1439 NTSTATUS nt_status;
1440 bool first = false;
1441 const char *reply_code;
1442 struct cli_credentials *creds;
1444 static char *want_feature_list = NULL;
1445 static DATA_BLOB session_key;
1447 TALLOC_CTX *mem_ctx;
1449 if (*private1) {
1450 state = (struct gensec_ntlm_state *)*private1;
1451 } else {
1452 state = talloc_zero(NULL, struct gensec_ntlm_state);
1453 if (!state) {
1454 x_fprintf(x_stdout, "BH No Memory\n");
1455 exit(1);
1457 *private1 = state;
1458 if (opt_password) {
1459 state->set_password = opt_password;
1463 if (strlen(buf) < 2) {
1464 DEBUG(1, ("query [%s] invalid", buf));
1465 x_fprintf(x_stdout, "BH Query invalid\n");
1466 return;
1469 if (strlen(buf) > 3) {
1470 if(strncmp(buf, "SF ", 3) == 0) {
1471 DEBUG(10, ("Setting flags to negotiate\n"));
1472 talloc_free(want_feature_list);
1473 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1474 x_fprintf(x_stdout, "OK\n");
1475 return;
1477 in = base64_decode_data_blob(buf + 3);
1478 } else {
1479 in = data_blob(NULL, 0);
1482 if (strncmp(buf, "YR", 2) == 0) {
1483 if (state->gensec_state) {
1484 talloc_free(state->gensec_state);
1485 state->gensec_state = NULL;
1487 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1488 /* Just return BH, like ntlm_auth from Samba 3 does. */
1489 x_fprintf(x_stdout, "BH Command expected\n");
1490 data_blob_free(&in);
1491 return;
1492 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1493 (strncmp(buf, "KK ", 3) != 0) &&
1494 (strncmp(buf, "AF ", 3) != 0) &&
1495 (strncmp(buf, "NA ", 3) != 0) &&
1496 (strncmp(buf, "UG", 2) != 0) &&
1497 (strncmp(buf, "PW ", 3) != 0) &&
1498 (strncmp(buf, "GK", 2) != 0) &&
1499 (strncmp(buf, "GF", 2) != 0)) {
1500 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1501 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1502 data_blob_free(&in);
1503 return;
1506 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1508 /* setup gensec */
1509 if (!(state->gensec_state)) {
1510 switch (stdio_helper_mode) {
1511 case GSS_SPNEGO_CLIENT:
1512 case NTLMSSP_CLIENT_1:
1513 /* setup the client side */
1515 nt_status = gensec_client_start(NULL, &state->gensec_state,
1516 lpcfg_gensec_settings(NULL, lp_ctx));
1517 if (!NT_STATUS_IS_OK(nt_status)) {
1518 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1519 talloc_free(mem_ctx);
1520 return;
1523 creds = cli_credentials_init(state->gensec_state);
1524 cli_credentials_set_conf(creds, lp_ctx);
1525 if (opt_username) {
1526 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1528 if (opt_domain) {
1529 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1531 if (state->set_password) {
1532 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1533 } else {
1534 cli_credentials_set_password_callback(creds, get_password);
1536 if (opt_workstation) {
1537 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1540 gensec_set_credentials(state->gensec_state, creds);
1542 break;
1543 case GSS_SPNEGO_SERVER:
1544 case SQUID_2_5_NTLMSSP:
1546 nt_status = ntlm_auth_start_ntlmssp_server(state, lp_ctx,
1547 &state->gensec_state);
1548 if (!NT_STATUS_IS_OK(nt_status)) {
1549 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1550 talloc_free(mem_ctx);
1551 return;
1553 break;
1555 default:
1556 talloc_free(mem_ctx);
1557 abort();
1560 gensec_want_feature_list(state->gensec_state, want_feature_list);
1562 switch (stdio_helper_mode) {
1563 case GSS_SPNEGO_CLIENT:
1564 case GSS_SPNEGO_SERVER:
1565 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1566 if (!in.length) {
1567 first = true;
1569 break;
1570 case NTLMSSP_CLIENT_1:
1571 if (!in.length) {
1572 first = true;
1574 /* fall through */
1575 case SQUID_2_5_NTLMSSP:
1576 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1577 break;
1578 default:
1579 talloc_free(mem_ctx);
1580 abort();
1583 if (!NT_STATUS_IS_OK(nt_status)) {
1584 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1585 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1586 talloc_free(mem_ctx);
1587 return;
1592 /* update */
1594 if (strncmp(buf, "PW ", 3) == 0) {
1595 state->set_password = talloc_strndup(state,
1596 (const char *)in.data,
1597 in.length);
1599 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1600 state->set_password,
1601 CRED_SPECIFIED);
1602 x_fprintf(x_stdout, "OK\n");
1603 data_blob_free(&in);
1604 talloc_free(mem_ctx);
1605 return;
1608 if (strncmp(buf, "GK", 2) == 0) {
1609 char *base64_key;
1610 DEBUG(10, ("Requested session key\n"));
1611 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1612 if(!NT_STATUS_IS_OK(nt_status)) {
1613 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1614 x_fprintf(x_stdout, "BH No session key\n");
1615 talloc_free(mem_ctx);
1616 return;
1617 } else {
1618 base64_key = base64_encode_data_blob(state, session_key);
1619 x_fprintf(x_stdout, "GK %s\n", base64_key);
1620 talloc_free(base64_key);
1622 talloc_free(mem_ctx);
1623 return;
1626 if (stdio_helper_mode == SQUID_2_5_NTLMSSP && strncmp(buf, "GF", 2) == 0) {
1627 uint32_t neg_flags;
1629 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1631 DEBUG(10, ("Requested negotiated feature flags\n"));
1632 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1633 return;
1636 nt_status = gensec_update(state->gensec_state, mem_ctx, NULL, in, &out);
1638 /* don't leak 'bad password'/'no such user' info to the network client */
1639 nt_status = nt_status_squash(nt_status);
1641 if (out.length) {
1642 out_base64 = base64_encode_data_blob(mem_ctx, out);
1643 } else {
1644 out_base64 = NULL;
1647 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1648 reply_arg = "*";
1649 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1650 reply_code = "YR";
1651 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1652 reply_code = "KK";
1653 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1654 reply_code = "TT";
1655 } else {
1656 abort();
1660 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1661 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1662 reply_arg = nt_errstr(nt_status);
1663 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1664 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1665 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1666 reply_arg = nt_errstr(nt_status);
1667 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1668 } else if (!NT_STATUS_IS_OK(nt_status)) {
1669 reply_code = "NA";
1670 reply_arg = nt_errstr(nt_status);
1671 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1672 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1673 struct auth_session_info *session_info;
1675 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1676 if (!NT_STATUS_IS_OK(nt_status)) {
1677 reply_code = "BH Failed to retrive session info";
1678 reply_arg = nt_errstr(nt_status);
1679 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1680 } else {
1682 reply_code = "AF";
1683 reply_arg = session_info->unix_info->unix_name;
1684 talloc_free(session_info);
1686 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1687 reply_code = "AF";
1688 reply_arg = out_base64;
1689 } else {
1690 abort();
1693 switch (stdio_helper_mode) {
1694 case GSS_SPNEGO_SERVER:
1695 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1696 out_base64 ? out_base64 : "*",
1697 reply_arg ? reply_arg : "*");
1698 break;
1699 default:
1700 if (out_base64) {
1701 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1702 } else if (reply_arg) {
1703 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1704 } else {
1705 x_fprintf(x_stdout, "%s\n", reply_code);
1709 talloc_free(mem_ctx);
1710 return;
1713 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1714 struct loadparm_context *lp_ctx,
1715 struct ntlm_auth_state *state,
1716 char *buf, int length, void **private2)
1718 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1719 return;
1722 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1723 struct loadparm_context *lp_ctx,
1724 struct ntlm_auth_state *state,
1725 char *buf, int length, void **private2)
1727 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1728 return;
1731 static struct ntlmssp_state *client_ntlmssp_state = NULL;
1733 static bool manage_client_ntlmssp_init(struct spnego_data spnego)
1735 NTSTATUS status;
1736 DATA_BLOB null_blob = data_blob_null;
1737 DATA_BLOB to_server;
1738 char *to_server_base64;
1739 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1740 TALLOC_CTX *ctx = talloc_tos();
1742 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1744 if (client_ntlmssp_state != NULL) {
1745 DEBUG(1, ("Request for initial SPNEGO request where "
1746 "we already have a state\n"));
1747 return False;
1750 if (!client_ntlmssp_state) {
1751 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1752 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1753 return False;
1758 if (opt_password == NULL) {
1760 /* Request a password from the calling process. After
1761 sending it, the calling process should retry with
1762 the negTokenInit. */
1764 DEBUG(10, ("Requesting password\n"));
1765 x_fprintf(x_stdout, "PW\n");
1766 return True;
1769 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1770 spnego.negTokenInit.mechTypes = my_mechs;
1771 spnego.negTokenInit.reqFlags = data_blob_null;
1772 spnego.negTokenInit.reqFlagsPadding = 0;
1773 spnego.negTokenInit.mechListMIC = null_blob;
1775 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1776 &spnego.negTokenInit.mechToken);
1778 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1779 NT_STATUS_IS_OK(status)) ) {
1780 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1781 nt_errstr(status)));
1782 TALLOC_FREE(client_ntlmssp_state);
1783 return False;
1786 spnego_write_data(ctx, &to_server, &spnego);
1787 data_blob_free(&spnego.negTokenInit.mechToken);
1789 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1790 data_blob_free(&to_server);
1791 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1792 TALLOC_FREE(to_server_base64);
1793 return True;
1796 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
1798 NTSTATUS status;
1799 DATA_BLOB null_blob = data_blob_null;
1800 DATA_BLOB request;
1801 DATA_BLOB to_server;
1802 char *to_server_base64;
1803 TALLOC_CTX *ctx = talloc_tos();
1805 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1807 if (client_ntlmssp_state == NULL) {
1808 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1809 x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
1810 return;
1813 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1814 x_fprintf(x_stdout, "NA\n");
1815 TALLOC_FREE(client_ntlmssp_state);
1816 return;
1819 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1820 x_fprintf(x_stdout, "AF\n");
1821 TALLOC_FREE(client_ntlmssp_state);
1822 return;
1825 status = ntlmssp_update(client_ntlmssp_state,
1826 spnego.negTokenTarg.responseToken,
1827 &request);
1829 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
1830 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED or OK from "
1831 "ntlmssp_client_update, got: %s\n",
1832 nt_errstr(status)));
1833 x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
1834 "ntlmssp_client_update\n");
1835 data_blob_free(&request);
1836 TALLOC_FREE(client_ntlmssp_state);
1837 return;
1840 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1841 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1842 spnego.negTokenTarg.supportedMech = (const char *)OID_NTLMSSP;
1843 spnego.negTokenTarg.responseToken = request;
1844 spnego.negTokenTarg.mechListMIC = null_blob;
1846 spnego_write_data(ctx, &to_server, &spnego);
1847 data_blob_free(&request);
1849 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1850 data_blob_free(&to_server);
1851 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1852 TALLOC_FREE(to_server_base64);
1853 return;
1856 #ifdef HAVE_KRB5
1858 static bool manage_client_krb5_init(struct spnego_data spnego)
1860 char *principal;
1861 DATA_BLOB tkt, tkt_wrapped, to_server;
1862 DATA_BLOB session_key_krb5 = data_blob_null;
1863 struct spnego_data reply;
1864 char *reply_base64;
1865 int retval;
1867 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1868 ssize_t len;
1869 TALLOC_CTX *ctx = talloc_tos();
1871 principal = spnego.negTokenInit.targetPrincipal;
1873 /* We may not be allowed to use the server-supplied SPNEGO principal, or it may not have been supplied to us
1875 if (!lp_client_use_spnego_principal() || strequal(principal, ADS_IGNORE_PRINCIPAL)) {
1876 principal = NULL;
1879 if (principal == NULL &&
1880 opt_target_service && opt_target_hostname && !is_ipaddress(opt_target_hostname)) {
1881 DEBUG(3,("manage_client_krb5_init: using target "
1882 "hostname not SPNEGO principal\n"));
1884 principal = kerberos_get_principal_from_service_hostname(talloc_tos(),
1885 opt_target_service,
1886 opt_target_hostname,
1887 lp_realm());
1889 if (!principal) {
1890 return false;
1893 DEBUG(3,("manage_client_krb5_init: guessed "
1894 "server principal=%s\n",
1895 principal ? principal : "<null>"));
1898 if (principal == NULL) {
1899 DEBUG(3,("manage_client_krb5_init: could not guess server principal\n"));
1900 return false;
1903 retval = cli_krb5_get_ticket(ctx, principal, 0,
1904 &tkt, &session_key_krb5,
1905 0, NULL, NULL, NULL);
1906 if (retval) {
1907 char *user = NULL;
1909 /* Let's try to first get the TGT, for that we need a
1910 password. */
1912 if (opt_password == NULL) {
1913 DEBUG(10, ("Requesting password\n"));
1914 x_fprintf(x_stdout, "PW\n");
1915 return True;
1918 user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
1919 if (!user) {
1920 return false;
1923 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1924 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1925 return False;
1928 retval = cli_krb5_get_ticket(ctx, principal, 0,
1929 &tkt, &session_key_krb5,
1930 0, NULL, NULL, NULL);
1931 if (retval) {
1932 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1933 return False;
1938 /* wrap that up in a nice GSS-API wrapping */
1939 tkt_wrapped = spnego_gen_krb5_wrap(ctx, tkt, TOK_ID_KRB_AP_REQ);
1941 data_blob_free(&session_key_krb5);
1943 ZERO_STRUCT(reply);
1945 reply.type = SPNEGO_NEG_TOKEN_INIT;
1946 reply.negTokenInit.mechTypes = my_mechs;
1947 reply.negTokenInit.reqFlags = data_blob_null;
1948 reply.negTokenInit.reqFlagsPadding = 0;
1949 reply.negTokenInit.mechToken = tkt_wrapped;
1950 reply.negTokenInit.mechListMIC = data_blob_null;
1952 len = spnego_write_data(ctx, &to_server, &reply);
1953 data_blob_free(&tkt);
1955 if (len == -1) {
1956 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1957 return False;
1960 reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1961 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1963 TALLOC_FREE(reply_base64);
1964 data_blob_free(&to_server);
1965 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1966 return True;
1969 static void manage_client_krb5_targ(struct spnego_data spnego)
1971 switch (spnego.negTokenTarg.negResult) {
1972 case SPNEGO_ACCEPT_INCOMPLETE:
1973 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1974 x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
1975 "ACCEPT_INCOMPLETE\n");
1976 break;
1977 case SPNEGO_ACCEPT_COMPLETED:
1978 DEBUG(10, ("Accept completed\n"));
1979 x_fprintf(x_stdout, "AF\n");
1980 break;
1981 case SPNEGO_REJECT:
1982 DEBUG(10, ("Rejected\n"));
1983 x_fprintf(x_stdout, "NA\n");
1984 break;
1985 default:
1986 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1987 x_fprintf(x_stdout, "AF\n");
1991 #endif
1993 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1994 struct loadparm_context *lp_ctx,
1995 struct ntlm_auth_state *state,
1996 char *buf, int length, void **private2)
1998 DATA_BLOB request;
1999 struct spnego_data spnego;
2000 ssize_t len;
2001 TALLOC_CTX *ctx = talloc_tos();
2003 if (!opt_username || !*opt_username) {
2004 x_fprintf(x_stderr, "username must be specified!\n\n");
2005 exit(1);
2008 if (strlen(buf) <= 3) {
2009 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
2010 x_fprintf(x_stdout, "BH SPNEGO query too short\n");
2011 return;
2014 request = base64_decode_data_blob(buf+3);
2016 if (strncmp(buf, "PW ", 3) == 0) {
2018 /* We asked for a password and obviously got it :-) */
2020 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
2022 if (opt_password == NULL) {
2023 DEBUG(1, ("Out of memory\n"));
2024 x_fprintf(x_stdout, "BH Out of memory\n");
2025 data_blob_free(&request);
2026 return;
2029 x_fprintf(x_stdout, "OK\n");
2030 data_blob_free(&request);
2031 return;
2034 if ( (strncmp(buf, "TT ", 3) != 0) &&
2035 (strncmp(buf, "AF ", 3) != 0) &&
2036 (strncmp(buf, "NA ", 3) != 0) ) {
2037 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
2038 x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
2039 data_blob_free(&request);
2040 return;
2043 /* So we got a server challenge to generate a SPNEGO
2044 client-to-server request... */
2046 len = spnego_read_data(ctx, request, &spnego);
2047 data_blob_free(&request);
2049 if (len == -1) {
2050 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
2051 x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
2052 return;
2055 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
2057 /* The server offers a list of mechanisms */
2059 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
2061 while (*mechType != NULL) {
2063 #ifdef HAVE_KRB5
2064 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
2065 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
2066 if (manage_client_krb5_init(spnego))
2067 goto out;
2069 #endif
2071 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
2072 if (manage_client_ntlmssp_init(spnego))
2073 goto out;
2076 mechType++;
2079 DEBUG(1, ("Server offered no compatible mechanism\n"));
2080 x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
2081 return;
2084 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
2086 if (spnego.negTokenTarg.supportedMech == NULL) {
2087 /* On accept/reject Windows does not send the
2088 mechanism anymore. Handle that here and
2089 shut down the mechanisms. */
2091 switch (spnego.negTokenTarg.negResult) {
2092 case SPNEGO_ACCEPT_COMPLETED:
2093 x_fprintf(x_stdout, "AF\n");
2094 break;
2095 case SPNEGO_REJECT:
2096 x_fprintf(x_stdout, "NA\n");
2097 break;
2098 default:
2099 DEBUG(1, ("Got a negTokenTarg with no mech and an "
2100 "unknown negResult: %d\n",
2101 spnego.negTokenTarg.negResult));
2102 x_fprintf(x_stdout, "BH Got a negTokenTarg with"
2103 " no mech and an unknown "
2104 "negResult\n");
2107 TALLOC_FREE(client_ntlmssp_state);
2108 goto out;
2111 if (strcmp(spnego.negTokenTarg.supportedMech,
2112 OID_NTLMSSP) == 0) {
2113 manage_client_ntlmssp_targ(spnego);
2114 goto out;
2117 #if HAVE_KRB5
2118 if (strcmp(spnego.negTokenTarg.supportedMech,
2119 OID_KERBEROS5_OLD) == 0) {
2120 manage_client_krb5_targ(spnego);
2121 goto out;
2123 #endif
2127 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
2128 x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
2129 return;
2131 out:
2132 spnego_free_data(&spnego);
2133 return;
2136 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
2137 struct loadparm_context *lp_ctx,
2138 struct ntlm_auth_state *state,
2139 char *buf, int length, void **private2)
2141 char *request, *parameter;
2142 static DATA_BLOB challenge;
2143 static DATA_BLOB lm_response;
2144 static DATA_BLOB nt_response;
2145 static char *full_username;
2146 static char *username;
2147 static char *domain;
2148 static char *plaintext_password;
2149 static bool ntlm_server_1_user_session_key;
2150 static bool ntlm_server_1_lm_session_key;
2152 if (strequal(buf, ".")) {
2153 if (!full_username && !username) {
2154 x_fprintf(x_stdout, "Error: No username supplied!\n");
2155 } else if (plaintext_password) {
2156 /* handle this request as plaintext */
2157 if (!full_username) {
2158 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
2159 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
2160 return;
2163 if (check_plaintext_auth(full_username, plaintext_password, False)) {
2164 x_fprintf(x_stdout, "Authenticated: Yes\n");
2165 } else {
2166 x_fprintf(x_stdout, "Authenticated: No\n");
2168 } else if (!lm_response.data && !nt_response.data) {
2169 x_fprintf(x_stdout, "Error: No password supplied!\n");
2170 } else if (!challenge.data) {
2171 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
2172 } else {
2173 char *error_string = NULL;
2174 uchar lm_key[8];
2175 uchar user_session_key[16];
2176 uint32 flags = 0;
2178 if (full_username && !username) {
2179 fstring fstr_user;
2180 fstring fstr_domain;
2182 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
2183 /* username might be 'tainted', don't print into our new-line deleimianted stream */
2184 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
2186 SAFE_FREE(username);
2187 SAFE_FREE(domain);
2188 username = smb_xstrdup(fstr_user);
2189 domain = smb_xstrdup(fstr_domain);
2192 if (!domain) {
2193 domain = smb_xstrdup(get_winbind_domain());
2196 if (ntlm_server_1_lm_session_key)
2197 flags |= WBFLAG_PAM_LMKEY;
2199 if (ntlm_server_1_user_session_key)
2200 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2202 if (!NT_STATUS_IS_OK(
2203 contact_winbind_auth_crap(username,
2204 domain,
2205 lp_netbios_name(),
2206 &challenge,
2207 &lm_response,
2208 &nt_response,
2209 flags, 0,
2210 lm_key,
2211 user_session_key,
2212 &error_string,
2213 NULL))) {
2215 x_fprintf(x_stdout, "Authenticated: No\n");
2216 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
2217 } else {
2218 static char zeros[16];
2219 char *hex_lm_key;
2220 char *hex_user_session_key;
2222 x_fprintf(x_stdout, "Authenticated: Yes\n");
2224 if (ntlm_server_1_lm_session_key
2225 && (memcmp(zeros, lm_key,
2226 sizeof(lm_key)) != 0)) {
2227 hex_lm_key = hex_encode_talloc(NULL,
2228 (const unsigned char *)lm_key,
2229 sizeof(lm_key));
2230 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
2231 TALLOC_FREE(hex_lm_key);
2234 if (ntlm_server_1_user_session_key
2235 && (memcmp(zeros, user_session_key,
2236 sizeof(user_session_key)) != 0)) {
2237 hex_user_session_key = hex_encode_talloc(NULL,
2238 (const unsigned char *)user_session_key,
2239 sizeof(user_session_key));
2240 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
2241 TALLOC_FREE(hex_user_session_key);
2244 SAFE_FREE(error_string);
2246 /* clear out the state */
2247 challenge = data_blob_null;
2248 nt_response = data_blob_null;
2249 lm_response = data_blob_null;
2250 SAFE_FREE(full_username);
2251 SAFE_FREE(username);
2252 SAFE_FREE(domain);
2253 SAFE_FREE(plaintext_password);
2254 ntlm_server_1_user_session_key = False;
2255 ntlm_server_1_lm_session_key = False;
2256 x_fprintf(x_stdout, ".\n");
2258 return;
2261 request = buf;
2263 /* Indicates a base64 encoded structure */
2264 parameter = strstr_m(request, ":: ");
2265 if (!parameter) {
2266 parameter = strstr_m(request, ": ");
2268 if (!parameter) {
2269 DEBUG(0, ("Parameter not found!\n"));
2270 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2271 return;
2274 parameter[0] ='\0';
2275 parameter++;
2276 parameter[0] ='\0';
2277 parameter++;
2279 } else {
2280 parameter[0] ='\0';
2281 parameter++;
2282 parameter[0] ='\0';
2283 parameter++;
2284 parameter[0] ='\0';
2285 parameter++;
2287 base64_decode_inplace(parameter);
2290 if (strequal(request, "LANMAN-Challenge")) {
2291 challenge = strhex_to_data_blob(NULL, parameter);
2292 if (challenge.length != 8) {
2293 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
2294 parameter,
2295 (int)challenge.length);
2296 challenge = data_blob_null;
2298 } else if (strequal(request, "NT-Response")) {
2299 nt_response = strhex_to_data_blob(NULL, parameter);
2300 if (nt_response.length < 24) {
2301 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
2302 parameter,
2303 (int)nt_response.length);
2304 nt_response = data_blob_null;
2306 } else if (strequal(request, "LANMAN-Response")) {
2307 lm_response = strhex_to_data_blob(NULL, parameter);
2308 if (lm_response.length != 24) {
2309 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
2310 parameter,
2311 (int)lm_response.length);
2312 lm_response = data_blob_null;
2314 } else if (strequal(request, "Password")) {
2315 plaintext_password = smb_xstrdup(parameter);
2316 } else if (strequal(request, "NT-Domain")) {
2317 domain = smb_xstrdup(parameter);
2318 } else if (strequal(request, "Username")) {
2319 username = smb_xstrdup(parameter);
2320 } else if (strequal(request, "Full-Username")) {
2321 full_username = smb_xstrdup(parameter);
2322 } else if (strequal(request, "Request-User-Session-Key")) {
2323 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2324 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2325 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2326 } else {
2327 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2331 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2332 struct loadparm_context *lp_ctx,
2333 struct ntlm_auth_state *state,
2334 char *buf, int length, void **private2)
2336 char *request, *parameter;
2337 static DATA_BLOB new_nt_pswd;
2338 static DATA_BLOB old_nt_hash_enc;
2339 static DATA_BLOB new_lm_pswd;
2340 static DATA_BLOB old_lm_hash_enc;
2341 static char *full_username = NULL;
2342 static char *username = NULL;
2343 static char *domain = NULL;
2344 static char *newpswd = NULL;
2345 static char *oldpswd = NULL;
2347 if (strequal(buf, ".")) {
2348 if(newpswd && oldpswd) {
2349 uchar old_nt_hash[16];
2350 uchar old_lm_hash[16];
2351 uchar new_nt_hash[16];
2352 uchar new_lm_hash[16];
2354 new_nt_pswd = data_blob(NULL, 516);
2355 old_nt_hash_enc = data_blob(NULL, 16);
2357 /* Calculate the MD4 hash (NT compatible) of the
2358 * password */
2359 E_md4hash(oldpswd, old_nt_hash);
2360 E_md4hash(newpswd, new_nt_hash);
2362 /* E_deshash returns false for 'long'
2363 passwords (> 14 DOS chars).
2365 Therefore, don't send a buffer
2366 encrypted with the truncated hash
2367 (it could allow an even easier
2368 attack on the password)
2370 Likewise, obey the admin's restriction
2373 if (lp_client_lanman_auth() &&
2374 E_deshash(newpswd, new_lm_hash) &&
2375 E_deshash(oldpswd, old_lm_hash)) {
2376 new_lm_pswd = data_blob(NULL, 516);
2377 old_lm_hash_enc = data_blob(NULL, 16);
2378 encode_pw_buffer(new_lm_pswd.data, newpswd,
2379 STR_UNICODE);
2381 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
2382 E_old_pw_hash(new_nt_hash, old_lm_hash,
2383 old_lm_hash_enc.data);
2384 } else {
2385 new_lm_pswd.data = NULL;
2386 new_lm_pswd.length = 0;
2387 old_lm_hash_enc.data = NULL;
2388 old_lm_hash_enc.length = 0;
2391 encode_pw_buffer(new_nt_pswd.data, newpswd,
2392 STR_UNICODE);
2394 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
2395 E_old_pw_hash(new_nt_hash, old_nt_hash,
2396 old_nt_hash_enc.data);
2399 if (!full_username && !username) {
2400 x_fprintf(x_stdout, "Error: No username supplied!\n");
2401 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2402 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2403 x_fprintf(x_stdout, "Error: No NT or LM password "
2404 "blobs supplied!\n");
2405 } else {
2406 char *error_string = NULL;
2408 if (full_username && !username) {
2409 fstring fstr_user;
2410 fstring fstr_domain;
2412 if (!parse_ntlm_auth_domain_user(full_username,
2413 fstr_user,
2414 fstr_domain)) {
2415 /* username might be 'tainted', don't
2416 * print into our new-line
2417 * deleimianted stream */
2418 x_fprintf(x_stdout, "Error: Could not "
2419 "parse into domain and "
2420 "username\n");
2421 SAFE_FREE(username);
2422 username = smb_xstrdup(full_username);
2423 } else {
2424 SAFE_FREE(username);
2425 SAFE_FREE(domain);
2426 username = smb_xstrdup(fstr_user);
2427 domain = smb_xstrdup(fstr_domain);
2432 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2433 username, domain,
2434 new_nt_pswd,
2435 old_nt_hash_enc,
2436 new_lm_pswd,
2437 old_lm_hash_enc,
2438 &error_string))) {
2439 x_fprintf(x_stdout, "Password-Change: No\n");
2440 x_fprintf(x_stdout, "Password-Change-Error: "
2441 "%s\n.\n", error_string);
2442 } else {
2443 x_fprintf(x_stdout, "Password-Change: Yes\n");
2446 SAFE_FREE(error_string);
2448 /* clear out the state */
2449 new_nt_pswd = data_blob_null;
2450 old_nt_hash_enc = data_blob_null;
2451 new_lm_pswd = data_blob_null;
2452 old_nt_hash_enc = data_blob_null;
2453 SAFE_FREE(full_username);
2454 SAFE_FREE(username);
2455 SAFE_FREE(domain);
2456 SAFE_FREE(newpswd);
2457 SAFE_FREE(oldpswd);
2458 x_fprintf(x_stdout, ".\n");
2460 return;
2463 request = buf;
2465 /* Indicates a base64 encoded structure */
2466 parameter = strstr_m(request, ":: ");
2467 if (!parameter) {
2468 parameter = strstr_m(request, ": ");
2470 if (!parameter) {
2471 DEBUG(0, ("Parameter not found!\n"));
2472 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2473 return;
2476 parameter[0] ='\0';
2477 parameter++;
2478 parameter[0] ='\0';
2479 parameter++;
2480 } else {
2481 parameter[0] ='\0';
2482 parameter++;
2483 parameter[0] ='\0';
2484 parameter++;
2485 parameter[0] ='\0';
2486 parameter++;
2488 base64_decode_inplace(parameter);
2491 if (strequal(request, "new-nt-password-blob")) {
2492 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2493 if (new_nt_pswd.length != 516) {
2494 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2495 "(got %d bytes, expected 516)\n.\n",
2496 parameter,
2497 (int)new_nt_pswd.length);
2498 new_nt_pswd = data_blob_null;
2500 } else if (strequal(request, "old-nt-hash-blob")) {
2501 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2502 if (old_nt_hash_enc.length != 16) {
2503 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2504 "(got %d bytes, expected 16)\n.\n",
2505 parameter,
2506 (int)old_nt_hash_enc.length);
2507 old_nt_hash_enc = data_blob_null;
2509 } else if (strequal(request, "new-lm-password-blob")) {
2510 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2511 if (new_lm_pswd.length != 516) {
2512 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2513 "(got %d bytes, expected 516)\n.\n",
2514 parameter,
2515 (int)new_lm_pswd.length);
2516 new_lm_pswd = data_blob_null;
2519 else if (strequal(request, "old-lm-hash-blob")) {
2520 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2521 if (old_lm_hash_enc.length != 16)
2523 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2524 "(got %d bytes, expected 16)\n.\n",
2525 parameter,
2526 (int)old_lm_hash_enc.length);
2527 old_lm_hash_enc = data_blob_null;
2529 } else if (strequal(request, "nt-domain")) {
2530 domain = smb_xstrdup(parameter);
2531 } else if(strequal(request, "username")) {
2532 username = smb_xstrdup(parameter);
2533 } else if(strequal(request, "full-username")) {
2534 username = smb_xstrdup(parameter);
2535 } else if(strequal(request, "new-password")) {
2536 newpswd = smb_xstrdup(parameter);
2537 } else if (strequal(request, "old-password")) {
2538 oldpswd = smb_xstrdup(parameter);
2539 } else {
2540 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2544 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2545 struct loadparm_context *lp_ctx,
2546 struct ntlm_auth_state *state,
2547 stdio_helper_function fn, void **private2)
2549 char *buf;
2550 char tmp[INITIAL_BUFFER_SIZE+1];
2551 int length, buf_size = 0;
2552 char *c;
2554 buf = talloc_strdup(state->mem_ctx, "");
2555 if (!buf) {
2556 DEBUG(0, ("Failed to allocate input buffer.\n"));
2557 x_fprintf(x_stderr, "ERR\n");
2558 exit(1);
2561 do {
2563 /* this is not a typo - x_fgets doesn't work too well under
2564 * squid */
2565 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2566 if (ferror(stdin)) {
2567 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2568 "(%s)\n", ferror(stdin),
2569 strerror(ferror(stdin))));
2571 exit(1);
2573 exit(0);
2576 buf = talloc_strdup_append_buffer(buf, tmp);
2577 buf_size += INITIAL_BUFFER_SIZE;
2579 if (buf_size > MAX_BUFFER_SIZE) {
2580 DEBUG(2, ("Oversized message\n"));
2581 x_fprintf(x_stderr, "ERR\n");
2582 talloc_free(buf);
2583 return;
2586 c = strchr(buf, '\n');
2587 } while (c == NULL);
2589 *c = '\0';
2590 length = c-buf;
2592 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2594 if (buf[0] == '\0') {
2595 DEBUG(2, ("Invalid Request\n"));
2596 x_fprintf(x_stderr, "ERR\n");
2597 talloc_free(buf);
2598 return;
2601 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2602 talloc_free(buf);
2606 static void squid_stream(enum stdio_helper_mode stdio_mode,
2607 struct loadparm_context *lp_ctx,
2608 stdio_helper_function fn) {
2609 TALLOC_CTX *mem_ctx;
2610 struct ntlm_auth_state *state;
2612 /* initialize FDescs */
2613 x_setbuf(x_stdout, NULL);
2614 x_setbuf(x_stderr, NULL);
2616 mem_ctx = talloc_init("ntlm_auth");
2617 if (!mem_ctx) {
2618 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2619 x_fprintf(x_stderr, "ERR\n");
2620 exit(1);
2623 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2624 if (!state) {
2625 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2626 x_fprintf(x_stderr, "ERR\n");
2627 exit(1);
2630 state->mem_ctx = mem_ctx;
2631 state->helper_mode = stdio_mode;
2633 while(1) {
2634 TALLOC_CTX *frame = talloc_stackframe();
2635 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2636 TALLOC_FREE(frame);
2641 /* Authenticate a user with a challenge/response */
2643 static bool check_auth_crap(void)
2645 NTSTATUS nt_status;
2646 uint32 flags = 0;
2647 char lm_key[8];
2648 char user_session_key[16];
2649 char *hex_lm_key;
2650 char *hex_user_session_key;
2651 char *error_string;
2652 static uint8 zeros[16];
2654 x_setbuf(x_stdout, NULL);
2656 if (request_lm_key)
2657 flags |= WBFLAG_PAM_LMKEY;
2659 if (request_user_session_key)
2660 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2662 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2664 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2665 opt_workstation,
2666 &opt_challenge,
2667 &opt_lm_response,
2668 &opt_nt_response,
2669 flags, 0,
2670 (unsigned char *)lm_key,
2671 (unsigned char *)user_session_key,
2672 &error_string, NULL);
2674 if (!NT_STATUS_IS_OK(nt_status)) {
2675 x_fprintf(x_stdout, "%s (0x%x)\n",
2676 error_string,
2677 NT_STATUS_V(nt_status));
2678 SAFE_FREE(error_string);
2679 return False;
2682 if (request_lm_key
2683 && (memcmp(zeros, lm_key,
2684 sizeof(lm_key)) != 0)) {
2685 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2686 sizeof(lm_key));
2687 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2688 TALLOC_FREE(hex_lm_key);
2690 if (request_user_session_key
2691 && (memcmp(zeros, user_session_key,
2692 sizeof(user_session_key)) != 0)) {
2693 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2694 sizeof(user_session_key));
2695 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2696 TALLOC_FREE(hex_user_session_key);
2699 return True;
2702 /* Main program */
2704 enum {
2705 OPT_USERNAME = 1000,
2706 OPT_DOMAIN,
2707 OPT_WORKSTATION,
2708 OPT_CHALLENGE,
2709 OPT_RESPONSE,
2710 OPT_LM,
2711 OPT_NT,
2712 OPT_PASSWORD,
2713 OPT_LM_KEY,
2714 OPT_USER_SESSION_KEY,
2715 OPT_DIAGNOSTICS,
2716 OPT_REQUIRE_MEMBERSHIP,
2717 OPT_USE_CACHED_CREDS,
2718 OPT_PAM_WINBIND_CONF,
2719 OPT_TARGET_SERVICE,
2720 OPT_TARGET_HOSTNAME
2723 int main(int argc, const char **argv)
2725 TALLOC_CTX *frame = talloc_stackframe();
2726 int opt;
2727 static const char *helper_protocol;
2728 static int diagnostics;
2730 static const char *hex_challenge;
2731 static const char *hex_lm_response;
2732 static const char *hex_nt_response;
2733 struct loadparm_context *lp_ctx;
2734 poptContext pc;
2736 /* NOTE: DO NOT change this interface without considering the implications!
2737 This is an external interface, which other programs will use to interact
2738 with this helper.
2741 /* We do not use single-letter command abbreviations, because they harm future
2742 interface stability. */
2744 struct poptOption long_options[] = {
2745 POPT_AUTOHELP
2746 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2747 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2748 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2749 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2750 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2751 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2752 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2753 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2754 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2755 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2756 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2757 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2758 OPT_DIAGNOSTICS,
2759 "Perform diagnostics on the authentication chain"},
2760 { "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" },
2761 { "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" },
2762 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2763 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2764 POPT_COMMON_CONFIGFILE
2765 POPT_COMMON_VERSION
2766 POPT_TABLEEND
2769 /* Samba client initialisation */
2770 load_case_tables();
2772 setup_logging("ntlm_auth", DEBUG_STDERR);
2774 /* Parse options */
2776 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2778 /* Parse command line options */
2780 if (argc == 1) {
2781 poptPrintHelp(pc, stderr, 0);
2782 return 1;
2785 while((opt = poptGetNextOpt(pc)) != -1) {
2786 /* Get generic config options like --configfile */
2789 poptFreeContext(pc);
2791 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2792 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2793 get_dyn_CONFIGFILE(), strerror(errno));
2794 exit(1);
2797 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2798 POPT_CONTEXT_KEEP_FIRST);
2800 while((opt = poptGetNextOpt(pc)) != -1) {
2801 switch (opt) {
2802 case OPT_CHALLENGE:
2803 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2804 if (opt_challenge.length != 8) {
2805 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2806 hex_challenge,
2807 (int)opt_challenge.length);
2808 exit(1);
2810 break;
2811 case OPT_LM:
2812 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2813 if (opt_lm_response.length != 24) {
2814 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2815 hex_lm_response,
2816 (int)opt_lm_response.length);
2817 exit(1);
2819 break;
2821 case OPT_NT:
2822 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2823 if (opt_nt_response.length < 24) {
2824 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2825 hex_nt_response,
2826 (int)opt_nt_response.length);
2827 exit(1);
2829 break;
2831 case OPT_REQUIRE_MEMBERSHIP:
2832 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2833 require_membership_of_sid = require_membership_of;
2835 break;
2839 if (opt_username) {
2840 char *domain = SMB_STRDUP(opt_username);
2841 char *p = strchr_m(domain, *lp_winbind_separator());
2842 if (p) {
2843 opt_username = p+1;
2844 *p = '\0';
2845 if (opt_domain && !strequal(opt_domain, domain)) {
2846 x_fprintf(x_stderr, "Domain specified in username (%s) "
2847 "doesn't match specified domain (%s)!\n\n",
2848 domain, opt_domain);
2849 poptPrintHelp(pc, stderr, 0);
2850 exit(1);
2852 opt_domain = domain;
2853 } else {
2854 SAFE_FREE(domain);
2858 /* Note: if opt_domain is "" then send no domain */
2859 if (opt_domain == NULL) {
2860 opt_domain = get_winbind_domain();
2863 if (opt_workstation == NULL) {
2864 opt_workstation = "";
2867 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2868 if (lp_ctx == NULL) {
2869 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2870 exit(1);
2873 if (helper_protocol) {
2874 int i;
2875 for (i=0; i<NUM_HELPER_MODES; i++) {
2876 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2877 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2878 exit(0);
2881 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2883 for (i=0; i<NUM_HELPER_MODES; i++) {
2884 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2887 exit(1);
2890 if (!opt_username || !*opt_username) {
2891 x_fprintf(x_stderr, "username must be specified!\n\n");
2892 poptPrintHelp(pc, stderr, 0);
2893 exit(1);
2896 if (opt_challenge.length) {
2897 if (!check_auth_crap()) {
2898 exit(1);
2900 exit(0);
2903 if (!opt_password) {
2904 opt_password = getpass("password: ");
2907 if (diagnostics) {
2908 if (!diagnose_ntlm_auth()) {
2909 return 1;
2911 } else {
2912 fstring user;
2914 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2915 if (!check_plaintext_auth(user, opt_password, True)) {
2916 return 1;
2920 /* Exit code */
2922 poptFreeContext(pc);
2923 TALLOC_FREE(frame);
2924 return 0;