charset: Avoid compiler warnings with --enable-developer
[Samba.git] / source3 / utils / ntlm_auth.c
blobf907742a2685821b25cb949831691ec6a0d17ede
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/gensec/gensec_internal.h"
36 #include "auth/credentials/credentials.h"
37 #include "librpc/crypto/gse.h"
38 #include "smb_krb5.h"
39 #include "lib/util/tiniparser.h"
40 #include "../lib/crypto/arcfour.h"
41 #include "libads/kerberos_proto.h"
42 #include "nsswitch/winbind_client.h"
43 #include "librpc/gen_ndr/krb5pac.h"
44 #include "../lib/util/asn1.h"
45 #include "auth/common_auth.h"
46 #include "source3/include/auth.h"
47 #include "source3/auth/proto.h"
48 #include "nsswitch/libwbclient/wbclient.h"
49 #include "lib/param/loadparm.h"
51 #if HAVE_KRB5
52 #include "auth/kerberos/pac_utils.h"
53 #endif
55 #ifndef PAM_WINBIND_CONFIG_FILE
56 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
57 #endif
59 #define WINBIND_KRB5_AUTH 0x00000080
61 #undef DBGC_CLASS
62 #define DBGC_CLASS DBGC_WINBIND
64 #define INITIAL_BUFFER_SIZE 300
65 #define MAX_BUFFER_SIZE 630000
67 enum stdio_helper_mode {
68 SQUID_2_4_BASIC,
69 SQUID_2_5_BASIC,
70 SQUID_2_5_NTLMSSP,
71 NTLMSSP_CLIENT_1,
72 GSS_SPNEGO_SERVER,
73 GSS_SPNEGO_CLIENT,
74 NTLM_SERVER_1,
75 NTLM_CHANGE_PASSWORD_1,
76 NUM_HELPER_MODES
79 enum ntlm_auth_cli_state {
80 CLIENT_INITIAL = 0,
81 CLIENT_RESPONSE,
82 CLIENT_FINISHED,
83 CLIENT_ERROR
86 struct ntlm_auth_state {
87 TALLOC_CTX *mem_ctx;
88 enum stdio_helper_mode helper_mode;
89 enum ntlm_auth_cli_state cli_state;
90 struct ntlmssp_state *ntlmssp_state;
91 uint32_t neg_flags;
92 char *want_feature_list;
93 bool have_session_key;
94 DATA_BLOB session_key;
95 DATA_BLOB initial_message;
96 void *gensec_private_1;
98 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
99 struct loadparm_context *lp_ctx,
100 struct ntlm_auth_state *state, char *buf,
101 int length, void **private2);
103 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
104 struct loadparm_context *lp_ctx,
105 struct ntlm_auth_state *state,
106 stdio_helper_function fn, void **private2);
108 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
109 struct loadparm_context *lp_ctx,
110 struct ntlm_auth_state *state,
111 char *buf, int length, void **private2);
113 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
114 struct loadparm_context *lp_ctx,
115 struct ntlm_auth_state *state,
116 char *buf, int length, void **private2);
118 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
119 struct loadparm_context *lp_ctx,
120 struct ntlm_auth_state *state,
121 char *buf, int length, void **private2);
123 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
124 struct loadparm_context *lp_ctx,
125 struct ntlm_auth_state *state,
126 char *buf, int length, void **private2);
128 static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
129 struct loadparm_context *lp_ctx,
130 struct ntlm_auth_state *state,
131 char *buf, int length, void **private2);
133 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
134 struct loadparm_context *lp_ctx,
135 struct ntlm_auth_state *state,
136 char *buf, int length, void **private2);
138 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
139 struct loadparm_context *lp_ctx,
140 struct ntlm_auth_state *state,
141 char *buf, int length, void **private2);
143 static const struct {
144 enum stdio_helper_mode mode;
145 const char *name;
146 stdio_helper_function fn;
147 } stdio_helper_protocols[] = {
148 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
149 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
150 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
151 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
152 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
153 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
154 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
155 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
156 { NUM_HELPER_MODES, NULL, NULL}
159 const char *opt_username;
160 const char *opt_domain;
161 const char *opt_workstation;
162 const char *opt_password;
163 static DATA_BLOB opt_challenge;
164 static DATA_BLOB opt_lm_response;
165 static DATA_BLOB opt_nt_response;
166 static int request_lm_key;
167 static int request_user_session_key;
168 static int use_cached_creds;
170 static const char *require_membership_of;
171 static const char *require_membership_of_sid;
172 static const char *opt_pam_winbind_conf;
174 const char *opt_target_service;
175 const char *opt_target_hostname;
178 /* This is a bit hairy, but the basic idea is to do a password callback
179 to the calling application. The callback comes from within gensec */
181 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
182 struct loadparm_context *lp_ctx,
183 struct ntlm_auth_state *state, char *buf, int length,
184 void **password)
186 DATA_BLOB in;
187 if (strlen(buf) < 2) {
188 DEBUG(1, ("query [%s] invalid", buf));
189 x_fprintf(x_stdout, "BH Query invalid\n");
190 return;
193 if (strlen(buf) > 3) {
194 in = base64_decode_data_blob(buf + 3);
195 } else {
196 in = data_blob(NULL, 0);
199 if (strncmp(buf, "PW ", 3) == 0) {
201 *password = talloc_strndup(NULL,
202 (const char *)in.data, in.length);
204 if (*password == NULL) {
205 DEBUG(1, ("Out of memory\n"));
206 x_fprintf(x_stdout, "BH Out of memory\n");
207 data_blob_free(&in);
208 return;
211 x_fprintf(x_stdout, "OK\n");
212 data_blob_free(&in);
213 return;
215 DEBUG(1, ("Asked for (and expected) a password\n"));
216 x_fprintf(x_stdout, "BH Expected a password\n");
217 data_blob_free(&in);
221 * Callback for password credentials. This is not async, and when
222 * GENSEC and the credentials code is made async, it will look rather
223 * different.
226 static const char *get_password(struct cli_credentials *credentials)
228 char *password = NULL;
230 /* Ask for a password */
231 x_fprintf(x_stdout, "PW\n");
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(1, ("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(1, ("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 return strupper_m(domain);
371 static bool get_require_membership_sid(void) {
372 struct winbindd_request request;
373 struct winbindd_response response;
375 if (!require_membership_of) {
376 return True;
379 if (require_membership_of_sid) {
380 return True;
383 /* Otherwise, ask winbindd for the name->sid request */
385 ZERO_STRUCT(request);
386 ZERO_STRUCT(response);
388 if (!parse_ntlm_auth_domain_user(require_membership_of,
389 request.data.name.dom_name,
390 request.data.name.name)) {
391 DEBUG(0, ("Could not parse %s into separate domain/name parts!\n",
392 require_membership_of));
393 return False;
396 if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
397 NSS_STATUS_SUCCESS) {
398 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
399 require_membership_of));
400 return False;
403 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
405 if (require_membership_of_sid)
406 return True;
408 return False;
412 * Get some configuration from pam_winbind.conf to see if we
413 * need to contact trusted domain
416 int get_pam_winbind_config()
418 int ctrl = 0;
419 struct tiniparser_dictionary *d = NULL;
421 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
422 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
425 d = tiniparser_load(opt_pam_winbind_conf);
427 if (!d) {
428 return 0;
431 if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
432 ctrl |= WINBIND_KRB5_AUTH;
435 tiniparser_freedict(d);
437 return ctrl;
440 /* Authenticate a user with a plaintext password */
442 static bool check_plaintext_auth(const char *user, const char *pass,
443 bool stdout_diagnostics)
445 struct winbindd_request request;
446 struct winbindd_response response;
447 NSS_STATUS result;
449 if (!get_require_membership_sid()) {
450 return False;
453 /* Send off request */
455 ZERO_STRUCT(request);
456 ZERO_STRUCT(response);
458 fstrcpy(request.data.auth.user, user);
459 fstrcpy(request.data.auth.pass, pass);
460 if (require_membership_of_sid) {
461 strlcpy(request.data.auth.require_membership_of_sid,
462 require_membership_of_sid,
463 sizeof(request.data.auth.require_membership_of_sid));
466 result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
468 /* Display response */
470 if (stdout_diagnostics) {
471 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
472 d_printf("Reading winbind reply failed! (0x01)\n");
475 d_printf("%s: %s (0x%x)\n",
476 response.data.auth.nt_status_string,
477 response.data.auth.error_string,
478 response.data.auth.nt_status);
479 } else {
480 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
481 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
484 DEBUG(3, ("%s: %s (0x%x)\n",
485 response.data.auth.nt_status_string,
486 response.data.auth.error_string,
487 response.data.auth.nt_status));
490 return (result == NSS_STATUS_SUCCESS);
493 /* authenticate a user with an encrypted username/password */
495 NTSTATUS contact_winbind_auth_crap(const char *username,
496 const char *domain,
497 const char *workstation,
498 const DATA_BLOB *challenge,
499 const DATA_BLOB *lm_response,
500 const DATA_BLOB *nt_response,
501 uint32 flags,
502 uint32 extra_logon_parameters,
503 uint8 lm_key[8],
504 uint8 user_session_key[16],
505 char **error_string,
506 char **unix_name)
508 NTSTATUS nt_status;
509 NSS_STATUS result;
510 struct winbindd_request request;
511 struct winbindd_response response;
513 if (!get_require_membership_sid()) {
514 return NT_STATUS_INVALID_PARAMETER;
517 ZERO_STRUCT(request);
518 ZERO_STRUCT(response);
520 request.flags = flags;
522 request.data.auth_crap.logon_parameters = extra_logon_parameters
523 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
525 if (require_membership_of_sid)
526 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
528 fstrcpy(request.data.auth_crap.user, username);
529 fstrcpy(request.data.auth_crap.domain, domain);
531 fstrcpy(request.data.auth_crap.workstation,
532 workstation);
534 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
536 if (lm_response && lm_response->length) {
537 memcpy(request.data.auth_crap.lm_resp,
538 lm_response->data,
539 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
540 request.data.auth_crap.lm_resp_len = lm_response->length;
543 if (nt_response && nt_response->length) {
544 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
545 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
546 request.extra_len = nt_response->length;
547 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
548 if (request.extra_data.data == NULL) {
549 return NT_STATUS_NO_MEMORY;
551 memcpy(request.extra_data.data, nt_response->data,
552 nt_response->length);
554 } else {
555 memcpy(request.data.auth_crap.nt_resp,
556 nt_response->data, nt_response->length);
558 request.data.auth_crap.nt_resp_len = nt_response->length;
561 result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
562 SAFE_FREE(request.extra_data.data);
564 /* Display response */
566 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
567 nt_status = NT_STATUS_UNSUCCESSFUL;
568 if (error_string)
569 *error_string = smb_xstrdup("Reading winbind reply failed!");
570 winbindd_free_response(&response);
571 return nt_status;
574 nt_status = (NT_STATUS(response.data.auth.nt_status));
575 if (!NT_STATUS_IS_OK(nt_status)) {
576 if (error_string)
577 *error_string = smb_xstrdup(response.data.auth.error_string);
578 winbindd_free_response(&response);
579 return nt_status;
582 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
583 memcpy(lm_key, response.data.auth.first_8_lm_hash,
584 sizeof(response.data.auth.first_8_lm_hash));
586 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
587 memcpy(user_session_key, response.data.auth.user_session_key,
588 sizeof(response.data.auth.user_session_key));
591 if (flags & WBFLAG_PAM_UNIX_NAME) {
592 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
593 if (!*unix_name) {
594 winbindd_free_response(&response);
595 return NT_STATUS_NO_MEMORY;
599 winbindd_free_response(&response);
600 return nt_status;
603 /* contact server to change user password using auth crap */
604 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
605 const char *domain,
606 const DATA_BLOB new_nt_pswd,
607 const DATA_BLOB old_nt_hash_enc,
608 const DATA_BLOB new_lm_pswd,
609 const DATA_BLOB old_lm_hash_enc,
610 char **error_string)
612 NTSTATUS nt_status;
613 NSS_STATUS result;
614 struct winbindd_request request;
615 struct winbindd_response response;
617 if (!get_require_membership_sid())
619 if(error_string)
620 *error_string = smb_xstrdup("Can't get membership sid.");
621 return NT_STATUS_INVALID_PARAMETER;
624 ZERO_STRUCT(request);
625 ZERO_STRUCT(response);
627 if(username != NULL)
628 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
629 if(domain != NULL)
630 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
632 if(new_nt_pswd.length)
634 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
635 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
638 if(old_nt_hash_enc.length)
640 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));
641 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
644 if(new_lm_pswd.length)
646 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
647 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
650 if(old_lm_hash_enc.length)
652 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));
653 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
656 result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
658 /* Display response */
660 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
662 nt_status = NT_STATUS_UNSUCCESSFUL;
663 if (error_string)
664 *error_string = smb_xstrdup("Reading winbind reply failed!");
665 winbindd_free_response(&response);
666 return nt_status;
669 nt_status = (NT_STATUS(response.data.auth.nt_status));
670 if (!NT_STATUS_IS_OK(nt_status))
672 if (error_string)
673 *error_string = smb_xstrdup(response.data.auth.error_string);
674 winbindd_free_response(&response);
675 return nt_status;
678 winbindd_free_response(&response);
680 return nt_status;
683 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
684 TALLOC_CTX *mem_ctx,
685 void *server_returned_info,
686 const char *original_user_name,
687 uint32_t session_info_flags,
688 struct auth_session_info **session_info_out)
690 char *unix_username = (char *)server_returned_info;
691 struct auth_session_info *session_info = talloc_zero(mem_ctx, struct auth_session_info);
692 if (!session_info) {
693 return NT_STATUS_NO_MEMORY;
696 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
697 if (!session_info->unix_info) {
698 TALLOC_FREE(session_info);
699 return NT_STATUS_NO_MEMORY;
701 session_info->unix_info->unix_name = talloc_steal(session_info->unix_info, unix_username);
703 *session_info_out = session_info;
705 return NT_STATUS_OK;
708 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
709 TALLOC_CTX *mem_ctx,
710 struct smb_krb5_context *smb_krb5_context,
711 DATA_BLOB *pac_blob,
712 const char *princ_name,
713 const struct tsocket_address *remote_address,
714 uint32_t session_info_flags,
715 struct auth_session_info **session_info)
717 TALLOC_CTX *tmp_ctx;
718 struct PAC_LOGON_INFO *logon_info = NULL;
719 char *unixuser;
720 NTSTATUS status;
721 char *domain = NULL;
722 char *realm = NULL;
723 char *user = NULL;
724 char *p;
726 tmp_ctx = talloc_new(mem_ctx);
727 if (!tmp_ctx) {
728 return NT_STATUS_NO_MEMORY;
731 if (pac_blob) {
732 #ifdef HAVE_KRB5
733 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
734 NULL, NULL, 0, &logon_info);
735 #else
736 status = NT_STATUS_ACCESS_DENIED;
737 #endif
738 if (!NT_STATUS_IS_OK(status)) {
739 goto done;
743 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
745 p = strchr_m(princ_name, '@');
746 if (!p) {
747 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
748 princ_name));
749 return NT_STATUS_LOGON_FAILURE;
752 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
753 if (!user) {
754 return NT_STATUS_NO_MEMORY;
757 realm = talloc_strdup(talloc_tos(), p + 1);
758 if (!realm) {
759 return NT_STATUS_NO_MEMORY;
762 if (!strequal(realm, lp_realm())) {
763 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
764 if (!lp_allow_trusted_domains()) {
765 return NT_STATUS_LOGON_FAILURE;
769 if (logon_info && logon_info->info3.base.logon_domain.string) {
770 domain = talloc_strdup(mem_ctx,
771 logon_info->info3.base.logon_domain.string);
772 if (!domain) {
773 return NT_STATUS_NO_MEMORY;
775 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
776 } else {
778 /* If we have winbind running, we can (and must) shorten the
779 username by using the short netbios name. Otherwise we will
780 have inconsistent user names. With Kerberos, we get the
781 fully qualified realm, with ntlmssp we get the short
782 name. And even w2k3 does use ntlmssp if you for example
783 connect to an ip address. */
785 wbcErr wbc_status;
786 struct wbcDomainInfo *info = NULL;
788 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
789 realm));
791 wbc_status = wbcDomainInfo(realm, &info);
793 if (WBC_ERROR_IS_OK(wbc_status)) {
794 domain = talloc_strdup(mem_ctx,
795 info->short_name);
796 wbcFreeMemory(info);
797 } else {
798 DEBUG(3, ("Could not find short name: %s\n",
799 wbcErrorString(wbc_status)));
800 domain = talloc_strdup(mem_ctx, realm);
802 if (!domain) {
803 return NT_STATUS_NO_MEMORY;
805 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
808 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
809 if (!unixuser) {
810 status = NT_STATUS_NO_MEMORY;
811 goto done;
814 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
816 done:
817 TALLOC_FREE(tmp_ctx);
818 return status;
824 * Return the challenge as determined by the authentication subsystem
825 * @return an 8 byte random challenge
828 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
829 uint8_t chal[8])
831 if (auth_ctx->challenge.data.length == 8) {
832 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
833 auth_ctx->challenge.set_by));
834 memcpy(chal, auth_ctx->challenge.data.data, 8);
835 return NT_STATUS_OK;
838 if (!auth_ctx->challenge.set_by) {
839 generate_random_buffer(chal, 8);
841 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
842 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
843 auth_ctx->challenge.set_by = "random";
846 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
847 auth_ctx->challenge.set_by));
849 return NT_STATUS_OK;
853 * NTLM2 authentication modifies the effective challenge,
854 * @param challenge The new challenge value
856 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
858 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
859 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
861 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
862 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
864 return NT_STATUS_OK;
868 * Check the password on an NTLMSSP login.
870 * Return the session keys used on the connection.
873 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
874 TALLOC_CTX *mem_ctx,
875 const struct auth_usersupplied_info *user_info,
876 void **server_returned_info,
877 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
879 static const char zeros[16] = { 0, };
880 NTSTATUS nt_status;
881 char *error_string = NULL;
882 uint8 lm_key[8];
883 uint8 user_sess_key[16];
884 char *unix_name = NULL;
886 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
887 user_info->workstation_name,
888 &auth4_context->challenge.data,
889 &user_info->password.response.lanman,
890 &user_info->password.response.nt,
891 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
893 lm_key, user_sess_key,
894 &error_string, &unix_name);
896 if (NT_STATUS_IS_OK(nt_status)) {
897 if (memcmp(lm_key, zeros, 8) != 0) {
898 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
899 memcpy(lm_session_key->data, lm_key, 8);
900 memset(lm_session_key->data+8, '\0', 8);
903 if (memcmp(user_sess_key, zeros, 16) != 0) {
904 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
906 *server_returned_info = talloc_strdup(mem_ctx,
907 unix_name);
908 } else {
909 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
910 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
911 user_info->client.domain_name, user_info->client.account_name,
912 user_info->workstation_name,
913 error_string ? error_string : "unknown error (NULL)"));
916 SAFE_FREE(error_string);
917 SAFE_FREE(unix_name);
918 return nt_status;
921 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
922 TALLOC_CTX *mem_ctx,
923 const struct auth_usersupplied_info *user_info,
924 void **server_returned_info,
925 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
927 NTSTATUS nt_status;
928 struct samr_Password lm_pw, nt_pw;
930 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
932 nt_status = ntlm_password_check(mem_ctx,
933 true, true, 0,
934 &auth4_context->challenge.data,
935 &user_info->password.response.lanman,
936 &user_info->password.response.nt,
937 user_info->client.account_name,
938 user_info->client.account_name,
939 user_info->client.domain_name,
940 &lm_pw, &nt_pw, session_key, lm_session_key);
942 if (NT_STATUS_IS_OK(nt_status)) {
943 *server_returned_info = talloc_asprintf(mem_ctx,
944 "%s%c%s", user_info->client.domain_name,
945 *lp_winbind_separator(),
946 user_info->client.account_name);
947 } else {
948 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
949 user_info->client.domain_name, user_info->client.account_name,
950 user_info->workstation_name,
951 nt_errstr(nt_status)));
953 return nt_status;
956 static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state)
958 NTSTATUS status;
959 if ( (opt_username == NULL) || (opt_domain == NULL) ) {
960 status = NT_STATUS_UNSUCCESSFUL;
961 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
962 return NT_STATUS_INVALID_PARAMETER;
965 status = ntlmssp_client_start(NULL,
966 lp_netbios_name(),
967 lp_workgroup(),
968 lp_client_ntlmv2_auth(),
969 client_ntlmssp_state);
971 if (!NT_STATUS_IS_OK(status)) {
972 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
973 nt_errstr(status)));
974 TALLOC_FREE(*client_ntlmssp_state);
975 return status;
978 status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
980 if (!NT_STATUS_IS_OK(status)) {
981 DEBUG(1, ("Could not set username: %s\n",
982 nt_errstr(status)));
983 TALLOC_FREE(*client_ntlmssp_state);
984 return status;
987 status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
989 if (!NT_STATUS_IS_OK(status)) {
990 DEBUG(1, ("Could not set domain: %s\n",
991 nt_errstr(status)));
992 TALLOC_FREE(*client_ntlmssp_state);
993 return status;
996 if (opt_password) {
997 status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
999 if (!NT_STATUS_IS_OK(status)) {
1000 DEBUG(1, ("Could not set password: %s\n",
1001 nt_errstr(status)));
1002 TALLOC_FREE(*client_ntlmssp_state);
1003 return status;
1007 return NT_STATUS_OK;
1010 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1012 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1013 if (auth4_context == NULL) {
1014 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1015 return NULL;
1017 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1018 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1019 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1020 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1021 if (local_pw) {
1022 auth4_context->check_ntlm_password = local_pw_check;
1023 } else {
1024 auth4_context->check_ntlm_password = winbind_pw_check;
1026 auth4_context->private_data = NULL;
1027 return auth4_context;
1030 static NTSTATUS ntlm_auth_start_ntlmssp_server(TALLOC_CTX *mem_ctx,
1031 struct loadparm_context *lp_ctx,
1032 struct gensec_security **gensec_security_out)
1034 struct gensec_security *gensec_security;
1035 NTSTATUS nt_status;
1037 TALLOC_CTX *tmp_ctx;
1038 const struct gensec_security_ops **backends;
1039 struct gensec_settings *gensec_settings;
1040 size_t idx = 0;
1041 struct cli_credentials *server_credentials;
1043 struct auth4_context *auth4_context;
1045 tmp_ctx = talloc_new(mem_ctx);
1046 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1048 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1049 if (auth4_context == NULL) {
1050 TALLOC_FREE(tmp_ctx);
1051 return NT_STATUS_NO_MEMORY;
1054 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1055 if (lp_ctx == NULL) {
1056 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1057 TALLOC_FREE(tmp_ctx);
1058 return NT_STATUS_NO_MEMORY;
1062 * This should be a 'netbios domain -> DNS domain'
1063 * mapping, and can currently validly return NULL on
1064 * poorly configured systems.
1066 * This is used for the NTLMSSP server
1069 if (opt_password) {
1070 gensec_settings->server_netbios_name = lp_netbios_name();
1071 gensec_settings->server_netbios_domain = lp_workgroup();
1072 } else {
1073 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1074 gensec_settings->server_netbios_domain = get_winbind_domain();
1077 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1078 get_mydnsdomname(talloc_tos()));
1079 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1080 get_mydnsfullname());
1082 backends = talloc_zero_array(gensec_settings,
1083 const struct gensec_security_ops *, 4);
1085 if (backends == NULL) {
1086 TALLOC_FREE(tmp_ctx);
1087 return NT_STATUS_NO_MEMORY;
1089 gensec_settings->backends = backends;
1091 gensec_init();
1093 /* These need to be in priority order, krb5 before NTLMSSP */
1094 #if defined(HAVE_KRB5)
1095 backends[idx++] = &gensec_gse_krb5_security_ops;
1096 #endif
1098 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1100 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1103 * This is anonymous for now, because we just use it
1104 * to set the kerberos state at the moment
1106 server_credentials = cli_credentials_init_anon(tmp_ctx);
1107 if (!server_credentials) {
1108 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1109 return NT_STATUS_NO_MEMORY;
1112 cli_credentials_set_conf(server_credentials, lp_ctx);
1114 if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1115 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1116 } else {
1117 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1120 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1121 auth4_context, &gensec_security);
1123 if (!NT_STATUS_IS_OK(nt_status)) {
1124 TALLOC_FREE(tmp_ctx);
1125 return nt_status;
1128 gensec_set_credentials(gensec_security, server_credentials);
1130 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
1131 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
1133 talloc_unlink(tmp_ctx, lp_ctx);
1134 talloc_unlink(tmp_ctx, server_credentials);
1135 talloc_unlink(tmp_ctx, gensec_settings);
1136 talloc_unlink(tmp_ctx, auth4_context);
1138 nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP);
1139 if (!NT_STATUS_IS_OK(nt_status)) {
1140 TALLOC_FREE(tmp_ctx);
1141 return nt_status;
1144 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1145 TALLOC_FREE(tmp_ctx);
1146 return NT_STATUS_OK;
1149 /*******************************************************************
1150 Used by firefox to drive NTLM auth to IIS servers.
1151 *******************************************************************/
1153 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
1154 DATA_BLOB *reply)
1156 struct winbindd_request wb_request;
1157 struct winbindd_response wb_response;
1158 int ctrl = 0;
1159 NSS_STATUS result;
1161 /* get winbindd to do the ntlmssp step on our behalf */
1162 ZERO_STRUCT(wb_request);
1163 ZERO_STRUCT(wb_response);
1166 * This is tricky here. If we set krb5_auth in pam_winbind.conf
1167 * creds for users in trusted domain will be stored the winbindd
1168 * child of the trusted domain. If we ask the primary domain for
1169 * ntlm_ccache_auth, it will fail. So, we have to ask the trusted
1170 * domain's child for ccache_ntlm_auth. that is to say, we have to
1171 * set WBFLAG_PAM_CONTACT_TRUSTDOM in request.flags.
1173 ctrl = get_pam_winbind_config();
1175 if (ctrl & WINBIND_KRB5_AUTH) {
1176 wb_request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1179 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
1180 "%s%c%s", opt_domain, winbind_separator(), opt_username);
1181 wb_request.data.ccache_ntlm_auth.uid = geteuid();
1182 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
1183 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
1184 wb_request.extra_len = initial_msg.length + challenge_msg.length;
1186 if (wb_request.extra_len > 0) {
1187 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
1188 if (wb_request.extra_data.data == NULL) {
1189 return NT_STATUS_NO_MEMORY;
1192 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
1193 memcpy(wb_request.extra_data.data + initial_msg.length,
1194 challenge_msg.data, challenge_msg.length);
1197 result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
1198 SAFE_FREE(wb_request.extra_data.data);
1200 if (result != NSS_STATUS_SUCCESS) {
1201 winbindd_free_response(&wb_response);
1202 return NT_STATUS_UNSUCCESSFUL;
1205 if (reply) {
1206 *reply = data_blob(wb_response.extra_data.data,
1207 wb_response.data.ccache_ntlm_auth.auth_blob_len);
1208 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
1209 reply->data == NULL) {
1210 winbindd_free_response(&wb_response);
1211 return NT_STATUS_NO_MEMORY;
1215 winbindd_free_response(&wb_response);
1216 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1219 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1220 struct loadparm_context *lp_ctx,
1221 struct ntlm_auth_state *state,
1222 char *buf, int length, void **private2)
1224 DATA_BLOB request, reply;
1225 NTSTATUS nt_status;
1227 if (!opt_username || !*opt_username) {
1228 x_fprintf(x_stderr, "username must be specified!\n\n");
1229 exit(1);
1232 if (strlen(buf) < 2) {
1233 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1234 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1235 return;
1238 if (strlen(buf) > 3) {
1239 if(strncmp(buf, "SF ", 3) == 0) {
1240 DEBUG(10, ("Looking for flags to negotiate\n"));
1241 talloc_free(state->want_feature_list);
1242 state->want_feature_list = talloc_strdup(state->mem_ctx,
1243 buf+3);
1244 x_fprintf(x_stdout, "OK\n");
1245 return;
1247 request = base64_decode_data_blob(buf + 3);
1248 } else {
1249 request = data_blob_null;
1252 if (strncmp(buf, "PW ", 3) == 0) {
1253 /* We asked for a password and obviously got it :-) */
1255 opt_password = SMB_STRNDUP((const char *)request.data,
1256 request.length);
1258 if (opt_password == NULL) {
1259 DEBUG(1, ("Out of memory\n"));
1260 x_fprintf(x_stdout, "BH Out of memory\n");
1261 data_blob_free(&request);
1262 return;
1265 x_fprintf(x_stdout, "OK\n");
1266 data_blob_free(&request);
1267 return;
1270 if (!state->ntlmssp_state && use_cached_creds) {
1271 /* check whether cached credentials are usable. */
1272 DATA_BLOB empty_blob = data_blob_null;
1274 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
1275 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1276 /* failed to use cached creds */
1277 use_cached_creds = False;
1281 if (opt_password == NULL && !use_cached_creds) {
1282 /* Request a password from the calling process. After
1283 sending it, the calling process should retry asking for the
1284 negotiate. */
1286 DEBUG(10, ("Requesting password\n"));
1287 x_fprintf(x_stdout, "PW\n");
1288 return;
1291 if (strncmp(buf, "YR", 2) == 0) {
1292 TALLOC_FREE(state->ntlmssp_state);
1293 state->cli_state = CLIENT_INITIAL;
1294 } else if (strncmp(buf, "TT", 2) == 0) {
1295 /* No special preprocessing required */
1296 } else if (strncmp(buf, "GF", 2) == 0) {
1297 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
1299 if(state->cli_state == CLIENT_FINISHED) {
1300 x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
1302 else {
1303 x_fprintf(x_stdout, "BH\n");
1306 data_blob_free(&request);
1307 return;
1308 } else if (strncmp(buf, "GK", 2) == 0 ) {
1309 DEBUG(10, ("Requested session key\n"));
1311 if(state->cli_state == CLIENT_FINISHED) {
1312 char *key64 = base64_encode_data_blob(state->mem_ctx,
1313 state->session_key);
1314 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
1315 TALLOC_FREE(key64);
1317 else {
1318 x_fprintf(x_stdout, "BH\n");
1321 data_blob_free(&request);
1322 return;
1323 } else {
1324 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1325 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1326 return;
1329 if (!state->ntlmssp_state) {
1330 nt_status = ntlm_auth_start_ntlmssp_client(
1331 &state->ntlmssp_state);
1332 if (!NT_STATUS_IS_OK(nt_status)) {
1333 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1334 return;
1336 ntlmssp_want_feature_list(state->ntlmssp_state,
1337 state->want_feature_list);
1338 state->initial_message = data_blob_null;
1341 DEBUG(10, ("got NTLMSSP packet:\n"));
1342 dump_data(10, request.data, request.length);
1344 if (use_cached_creds && !opt_password &&
1345 (state->cli_state == CLIENT_RESPONSE)) {
1346 nt_status = do_ccache_ntlm_auth(state->initial_message, request,
1347 &reply);
1348 } else {
1349 nt_status = ntlmssp_update(state->ntlmssp_state, request,
1350 &reply);
1353 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1354 char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
1355 reply);
1356 if (state->cli_state == CLIENT_INITIAL) {
1357 x_fprintf(x_stdout, "YR %s\n", reply_base64);
1358 state->initial_message = reply;
1359 state->cli_state = CLIENT_RESPONSE;
1360 } else {
1361 x_fprintf(x_stdout, "KK %s\n", reply_base64);
1362 data_blob_free(&reply);
1364 TALLOC_FREE(reply_base64);
1365 DEBUG(10, ("NTLMSSP challenge\n"));
1366 } else if (NT_STATUS_IS_OK(nt_status)) {
1367 char *reply_base64 = base64_encode_data_blob(talloc_tos(),
1368 reply);
1369 x_fprintf(x_stdout, "AF %s\n", reply_base64);
1370 TALLOC_FREE(reply_base64);
1372 if(state->have_session_key)
1373 data_blob_free(&state->session_key);
1375 state->session_key = data_blob(
1376 state->ntlmssp_state->session_key.data,
1377 state->ntlmssp_state->session_key.length);
1378 state->neg_flags = state->ntlmssp_state->neg_flags;
1379 state->have_session_key = true;
1381 DEBUG(10, ("NTLMSSP OK!\n"));
1382 state->cli_state = CLIENT_FINISHED;
1383 TALLOC_FREE(state->ntlmssp_state);
1384 } else {
1385 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1386 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
1387 state->cli_state = CLIENT_ERROR;
1388 TALLOC_FREE(state->ntlmssp_state);
1391 data_blob_free(&request);
1394 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1395 struct loadparm_context *lp_ctx,
1396 struct ntlm_auth_state *state,
1397 char *buf, int length, void **private2)
1399 char *user, *pass;
1400 user=buf;
1402 pass=(char *)memchr(buf,' ',length);
1403 if (!pass) {
1404 DEBUG(2, ("Password not found. Denying access\n"));
1405 x_fprintf(x_stdout, "ERR\n");
1406 return;
1408 *pass='\0';
1409 pass++;
1411 if (state->helper_mode == SQUID_2_5_BASIC) {
1412 rfc1738_unescape(user);
1413 rfc1738_unescape(pass);
1416 if (check_plaintext_auth(user, pass, False)) {
1417 x_fprintf(x_stdout, "OK\n");
1418 } else {
1419 x_fprintf(x_stdout, "ERR\n");
1423 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1424 struct loadparm_context *lp_ctx,
1425 char *buf, int length, void **private1)
1427 DATA_BLOB in;
1428 DATA_BLOB out = data_blob(NULL, 0);
1429 char *out_base64 = NULL;
1430 const char *reply_arg = NULL;
1431 struct gensec_ntlm_state {
1432 struct gensec_security *gensec_state;
1433 const char *set_password;
1435 struct gensec_ntlm_state *state;
1437 NTSTATUS nt_status;
1438 bool first = false;
1439 const char *reply_code;
1440 struct cli_credentials *creds;
1442 static char *want_feature_list = NULL;
1443 static DATA_BLOB session_key;
1445 TALLOC_CTX *mem_ctx;
1447 if (*private1) {
1448 state = (struct gensec_ntlm_state *)*private1;
1449 } else {
1450 state = talloc_zero(NULL, struct gensec_ntlm_state);
1451 if (!state) {
1452 x_fprintf(x_stdout, "BH No Memory\n");
1453 exit(1);
1455 *private1 = state;
1456 if (opt_password) {
1457 state->set_password = opt_password;
1461 if (strlen(buf) < 2) {
1462 DEBUG(1, ("query [%s] invalid", buf));
1463 x_fprintf(x_stdout, "BH Query invalid\n");
1464 return;
1467 if (strlen(buf) > 3) {
1468 if(strncmp(buf, "SF ", 3) == 0) {
1469 DEBUG(10, ("Setting flags to negotiate\n"));
1470 talloc_free(want_feature_list);
1471 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1472 x_fprintf(x_stdout, "OK\n");
1473 return;
1475 in = base64_decode_data_blob(buf + 3);
1476 } else {
1477 in = data_blob(NULL, 0);
1480 if (strncmp(buf, "YR", 2) == 0) {
1481 if (state->gensec_state) {
1482 talloc_free(state->gensec_state);
1483 state->gensec_state = NULL;
1485 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1486 /* Just return BH, like ntlm_auth from Samba 3 does. */
1487 x_fprintf(x_stdout, "BH Command expected\n");
1488 data_blob_free(&in);
1489 return;
1490 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1491 (strncmp(buf, "KK ", 3) != 0) &&
1492 (strncmp(buf, "AF ", 3) != 0) &&
1493 (strncmp(buf, "NA ", 3) != 0) &&
1494 (strncmp(buf, "UG", 2) != 0) &&
1495 (strncmp(buf, "PW ", 3) != 0) &&
1496 (strncmp(buf, "GK", 2) != 0) &&
1497 (strncmp(buf, "GF", 2) != 0)) {
1498 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1499 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1500 data_blob_free(&in);
1501 return;
1504 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1506 /* setup gensec */
1507 if (!(state->gensec_state)) {
1508 switch (stdio_helper_mode) {
1509 case GSS_SPNEGO_CLIENT:
1510 case NTLMSSP_CLIENT_1:
1511 /* setup the client side */
1513 nt_status = gensec_client_start(NULL, &state->gensec_state,
1514 lpcfg_gensec_settings(NULL, lp_ctx));
1515 if (!NT_STATUS_IS_OK(nt_status)) {
1516 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1517 talloc_free(mem_ctx);
1518 return;
1521 creds = cli_credentials_init(state->gensec_state);
1522 cli_credentials_set_conf(creds, lp_ctx);
1523 if (opt_username) {
1524 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1526 if (opt_domain) {
1527 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1529 if (state->set_password) {
1530 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1531 } else {
1532 cli_credentials_set_password_callback(creds, get_password);
1534 if (opt_workstation) {
1535 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1538 gensec_set_credentials(state->gensec_state, creds);
1540 break;
1541 case GSS_SPNEGO_SERVER:
1542 case SQUID_2_5_NTLMSSP:
1544 nt_status = ntlm_auth_start_ntlmssp_server(state, lp_ctx,
1545 &state->gensec_state);
1546 if (!NT_STATUS_IS_OK(nt_status)) {
1547 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1548 talloc_free(mem_ctx);
1549 return;
1551 break;
1553 default:
1554 talloc_free(mem_ctx);
1555 abort();
1558 gensec_want_feature_list(state->gensec_state, want_feature_list);
1560 switch (stdio_helper_mode) {
1561 case GSS_SPNEGO_CLIENT:
1562 case GSS_SPNEGO_SERVER:
1563 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1564 if (!in.length) {
1565 first = true;
1567 break;
1568 case NTLMSSP_CLIENT_1:
1569 if (!in.length) {
1570 first = true;
1572 /* fall through */
1573 case SQUID_2_5_NTLMSSP:
1574 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1575 break;
1576 default:
1577 talloc_free(mem_ctx);
1578 abort();
1581 if (!NT_STATUS_IS_OK(nt_status)) {
1582 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1583 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1584 talloc_free(mem_ctx);
1585 return;
1590 /* update */
1592 if (strncmp(buf, "PW ", 3) == 0) {
1593 state->set_password = talloc_strndup(state,
1594 (const char *)in.data,
1595 in.length);
1597 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1598 state->set_password,
1599 CRED_SPECIFIED);
1600 x_fprintf(x_stdout, "OK\n");
1601 data_blob_free(&in);
1602 talloc_free(mem_ctx);
1603 return;
1606 if (strncmp(buf, "GK", 2) == 0) {
1607 char *base64_key;
1608 DEBUG(10, ("Requested session key\n"));
1609 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1610 if(!NT_STATUS_IS_OK(nt_status)) {
1611 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1612 x_fprintf(x_stdout, "BH No session key\n");
1613 talloc_free(mem_ctx);
1614 return;
1615 } else {
1616 base64_key = base64_encode_data_blob(state, session_key);
1617 x_fprintf(x_stdout, "GK %s\n", base64_key);
1618 talloc_free(base64_key);
1620 talloc_free(mem_ctx);
1621 return;
1624 if (stdio_helper_mode == SQUID_2_5_NTLMSSP && strncmp(buf, "GF", 2) == 0) {
1625 uint32_t neg_flags;
1627 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1629 DEBUG(10, ("Requested negotiated feature flags\n"));
1630 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1631 return;
1634 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1636 /* don't leak 'bad password'/'no such user' info to the network client */
1637 nt_status = nt_status_squash(nt_status);
1639 if (out.length) {
1640 out_base64 = base64_encode_data_blob(mem_ctx, out);
1641 } else {
1642 out_base64 = NULL;
1645 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1646 reply_arg = "*";
1647 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1648 reply_code = "YR";
1649 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1650 reply_code = "KK";
1651 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1652 reply_code = "TT";
1653 } else {
1654 abort();
1658 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1659 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1660 reply_arg = nt_errstr(nt_status);
1661 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1662 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1663 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1664 reply_arg = nt_errstr(nt_status);
1665 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1666 } else if (!NT_STATUS_IS_OK(nt_status)) {
1667 reply_code = "NA";
1668 reply_arg = nt_errstr(nt_status);
1669 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1670 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1671 struct auth_session_info *session_info;
1673 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1674 if (!NT_STATUS_IS_OK(nt_status)) {
1675 reply_code = "BH Failed to retrive session info";
1676 reply_arg = nt_errstr(nt_status);
1677 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1678 } else {
1680 reply_code = "AF";
1681 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1682 if (reply_arg == NULL) {
1683 reply_code = "BH out of memory";
1684 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1686 talloc_free(session_info);
1688 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1689 reply_code = "AF";
1690 reply_arg = out_base64;
1691 } else {
1692 abort();
1695 switch (stdio_helper_mode) {
1696 case GSS_SPNEGO_SERVER:
1697 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1698 out_base64 ? out_base64 : "*",
1699 reply_arg ? reply_arg : "*");
1700 break;
1701 default:
1702 if (out_base64) {
1703 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1704 } else if (reply_arg) {
1705 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1706 } else {
1707 x_fprintf(x_stdout, "%s\n", reply_code);
1711 talloc_free(mem_ctx);
1712 return;
1715 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1716 struct loadparm_context *lp_ctx,
1717 struct ntlm_auth_state *state,
1718 char *buf, int length, void **private2)
1720 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1721 return;
1724 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1725 struct loadparm_context *lp_ctx,
1726 struct ntlm_auth_state *state,
1727 char *buf, int length, void **private2)
1729 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1730 return;
1733 static struct ntlmssp_state *client_ntlmssp_state = NULL;
1735 static bool manage_client_ntlmssp_init(struct spnego_data spnego)
1737 NTSTATUS status;
1738 DATA_BLOB null_blob = data_blob_null;
1739 DATA_BLOB to_server;
1740 char *to_server_base64;
1741 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1742 TALLOC_CTX *ctx = talloc_tos();
1744 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1746 if (client_ntlmssp_state != NULL) {
1747 DEBUG(1, ("Request for initial SPNEGO request where "
1748 "we already have a state\n"));
1749 return False;
1752 if (!client_ntlmssp_state) {
1753 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1754 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1755 return False;
1760 if (opt_password == NULL) {
1762 /* Request a password from the calling process. After
1763 sending it, the calling process should retry with
1764 the negTokenInit. */
1766 DEBUG(10, ("Requesting password\n"));
1767 x_fprintf(x_stdout, "PW\n");
1768 return True;
1771 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1772 spnego.negTokenInit.mechTypes = my_mechs;
1773 spnego.negTokenInit.reqFlags = data_blob_null;
1774 spnego.negTokenInit.reqFlagsPadding = 0;
1775 spnego.negTokenInit.mechListMIC = null_blob;
1777 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1778 &spnego.negTokenInit.mechToken);
1780 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1781 NT_STATUS_IS_OK(status)) ) {
1782 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1783 nt_errstr(status)));
1784 TALLOC_FREE(client_ntlmssp_state);
1785 return False;
1788 spnego_write_data(ctx, &to_server, &spnego);
1789 data_blob_free(&spnego.negTokenInit.mechToken);
1791 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1792 data_blob_free(&to_server);
1793 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1794 TALLOC_FREE(to_server_base64);
1795 return True;
1798 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
1800 NTSTATUS status;
1801 DATA_BLOB null_blob = data_blob_null;
1802 DATA_BLOB request;
1803 DATA_BLOB to_server;
1804 char *to_server_base64;
1805 TALLOC_CTX *ctx = talloc_tos();
1807 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1809 if (client_ntlmssp_state == NULL) {
1810 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1811 x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
1812 return;
1815 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1816 x_fprintf(x_stdout, "NA\n");
1817 TALLOC_FREE(client_ntlmssp_state);
1818 return;
1821 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1822 x_fprintf(x_stdout, "AF\n");
1823 TALLOC_FREE(client_ntlmssp_state);
1824 return;
1827 status = ntlmssp_update(client_ntlmssp_state,
1828 spnego.negTokenTarg.responseToken,
1829 &request);
1831 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
1832 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED or OK from "
1833 "ntlmssp_client_update, got: %s\n",
1834 nt_errstr(status)));
1835 x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
1836 "ntlmssp_client_update\n");
1837 data_blob_free(&request);
1838 TALLOC_FREE(client_ntlmssp_state);
1839 return;
1842 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1843 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1844 spnego.negTokenTarg.supportedMech = (const char *)OID_NTLMSSP;
1845 spnego.negTokenTarg.responseToken = request;
1846 spnego.negTokenTarg.mechListMIC = null_blob;
1848 spnego_write_data(ctx, &to_server, &spnego);
1849 data_blob_free(&request);
1851 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1852 data_blob_free(&to_server);
1853 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1854 TALLOC_FREE(to_server_base64);
1855 return;
1858 #ifdef HAVE_KRB5
1860 static bool manage_client_krb5_init(struct spnego_data spnego)
1862 char *principal;
1863 DATA_BLOB tkt, tkt_wrapped, to_server;
1864 DATA_BLOB session_key_krb5 = data_blob_null;
1865 struct spnego_data reply;
1866 char *reply_base64;
1867 int retval;
1869 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1870 ssize_t len;
1871 TALLOC_CTX *ctx = talloc_tos();
1873 principal = spnego.negTokenInit.targetPrincipal;
1875 /* We may not be allowed to use the server-supplied SPNEGO principal, or it may not have been supplied to us
1877 if (!lp_client_use_spnego_principal() || strequal(principal, ADS_IGNORE_PRINCIPAL)) {
1878 principal = NULL;
1881 if (principal == NULL &&
1882 opt_target_service && opt_target_hostname && !is_ipaddress(opt_target_hostname)) {
1883 DEBUG(3,("manage_client_krb5_init: using target "
1884 "hostname not SPNEGO principal\n"));
1886 principal = kerberos_get_principal_from_service_hostname(talloc_tos(),
1887 opt_target_service,
1888 opt_target_hostname,
1889 lp_realm());
1891 if (!principal) {
1892 return false;
1895 DEBUG(3,("manage_client_krb5_init: guessed "
1896 "server principal=%s\n",
1897 principal ? principal : "<null>"));
1900 if (principal == NULL) {
1901 DEBUG(3,("manage_client_krb5_init: could not guess server principal\n"));
1902 return false;
1905 retval = cli_krb5_get_ticket(ctx, principal, 0,
1906 &tkt, &session_key_krb5,
1907 0, NULL, NULL, NULL);
1908 if (retval) {
1909 char *user = NULL;
1911 /* Let's try to first get the TGT, for that we need a
1912 password. */
1914 if (opt_password == NULL) {
1915 DEBUG(10, ("Requesting password\n"));
1916 x_fprintf(x_stdout, "PW\n");
1917 return True;
1920 user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
1921 if (!user) {
1922 return false;
1925 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1926 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1927 return False;
1930 retval = cli_krb5_get_ticket(ctx, principal, 0,
1931 &tkt, &session_key_krb5,
1932 0, NULL, NULL, NULL);
1933 if (retval) {
1934 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1935 return False;
1940 /* wrap that up in a nice GSS-API wrapping */
1941 tkt_wrapped = spnego_gen_krb5_wrap(ctx, tkt, TOK_ID_KRB_AP_REQ);
1943 data_blob_free(&session_key_krb5);
1945 ZERO_STRUCT(reply);
1947 reply.type = SPNEGO_NEG_TOKEN_INIT;
1948 reply.negTokenInit.mechTypes = my_mechs;
1949 reply.negTokenInit.reqFlags = data_blob_null;
1950 reply.negTokenInit.reqFlagsPadding = 0;
1951 reply.negTokenInit.mechToken = tkt_wrapped;
1952 reply.negTokenInit.mechListMIC = data_blob_null;
1954 len = spnego_write_data(ctx, &to_server, &reply);
1955 data_blob_free(&tkt);
1957 if (len == -1) {
1958 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1959 return False;
1962 reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1963 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1965 TALLOC_FREE(reply_base64);
1966 data_blob_free(&to_server);
1967 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1968 return True;
1971 static void manage_client_krb5_targ(struct spnego_data spnego)
1973 switch (spnego.negTokenTarg.negResult) {
1974 case SPNEGO_ACCEPT_INCOMPLETE:
1975 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1976 x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
1977 "ACCEPT_INCOMPLETE\n");
1978 break;
1979 case SPNEGO_ACCEPT_COMPLETED:
1980 DEBUG(10, ("Accept completed\n"));
1981 x_fprintf(x_stdout, "AF\n");
1982 break;
1983 case SPNEGO_REJECT:
1984 DEBUG(10, ("Rejected\n"));
1985 x_fprintf(x_stdout, "NA\n");
1986 break;
1987 default:
1988 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1989 x_fprintf(x_stdout, "AF\n");
1993 #endif
1995 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1996 struct loadparm_context *lp_ctx,
1997 struct ntlm_auth_state *state,
1998 char *buf, int length, void **private2)
2000 DATA_BLOB request;
2001 struct spnego_data spnego;
2002 ssize_t len;
2003 TALLOC_CTX *ctx = talloc_tos();
2005 if (!opt_username || !*opt_username) {
2006 x_fprintf(x_stderr, "username must be specified!\n\n");
2007 exit(1);
2010 if (strlen(buf) <= 3) {
2011 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
2012 x_fprintf(x_stdout, "BH SPNEGO query too short\n");
2013 return;
2016 request = base64_decode_data_blob(buf+3);
2018 if (strncmp(buf, "PW ", 3) == 0) {
2020 /* We asked for a password and obviously got it :-) */
2022 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
2024 if (opt_password == NULL) {
2025 DEBUG(1, ("Out of memory\n"));
2026 x_fprintf(x_stdout, "BH Out of memory\n");
2027 data_blob_free(&request);
2028 return;
2031 x_fprintf(x_stdout, "OK\n");
2032 data_blob_free(&request);
2033 return;
2036 if ( (strncmp(buf, "TT ", 3) != 0) &&
2037 (strncmp(buf, "AF ", 3) != 0) &&
2038 (strncmp(buf, "NA ", 3) != 0) ) {
2039 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
2040 x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
2041 data_blob_free(&request);
2042 return;
2045 /* So we got a server challenge to generate a SPNEGO
2046 client-to-server request... */
2048 len = spnego_read_data(ctx, request, &spnego);
2049 data_blob_free(&request);
2051 if (len == -1) {
2052 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
2053 x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
2054 return;
2057 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
2059 /* The server offers a list of mechanisms */
2061 const char *const *mechType = spnego.negTokenInit.mechTypes;
2063 while (*mechType != NULL) {
2065 #ifdef HAVE_KRB5
2066 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
2067 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
2068 if (manage_client_krb5_init(spnego))
2069 goto out;
2071 #endif
2073 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
2074 if (manage_client_ntlmssp_init(spnego))
2075 goto out;
2078 mechType++;
2081 DEBUG(1, ("Server offered no compatible mechanism\n"));
2082 x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
2083 return;
2086 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
2088 if (spnego.negTokenTarg.supportedMech == NULL) {
2089 /* On accept/reject Windows does not send the
2090 mechanism anymore. Handle that here and
2091 shut down the mechanisms. */
2093 switch (spnego.negTokenTarg.negResult) {
2094 case SPNEGO_ACCEPT_COMPLETED:
2095 x_fprintf(x_stdout, "AF\n");
2096 break;
2097 case SPNEGO_REJECT:
2098 x_fprintf(x_stdout, "NA\n");
2099 break;
2100 default:
2101 DEBUG(1, ("Got a negTokenTarg with no mech and an "
2102 "unknown negResult: %d\n",
2103 spnego.negTokenTarg.negResult));
2104 x_fprintf(x_stdout, "BH Got a negTokenTarg with"
2105 " no mech and an unknown "
2106 "negResult\n");
2109 TALLOC_FREE(client_ntlmssp_state);
2110 goto out;
2113 if (strcmp(spnego.negTokenTarg.supportedMech,
2114 OID_NTLMSSP) == 0) {
2115 manage_client_ntlmssp_targ(spnego);
2116 goto out;
2119 #if HAVE_KRB5
2120 if (strcmp(spnego.negTokenTarg.supportedMech,
2121 OID_KERBEROS5_OLD) == 0) {
2122 manage_client_krb5_targ(spnego);
2123 goto out;
2125 #endif
2129 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
2130 x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
2131 return;
2133 out:
2134 spnego_free_data(&spnego);
2135 return;
2138 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
2139 struct loadparm_context *lp_ctx,
2140 struct ntlm_auth_state *state,
2141 char *buf, int length, void **private2)
2143 char *request, *parameter;
2144 static DATA_BLOB challenge;
2145 static DATA_BLOB lm_response;
2146 static DATA_BLOB nt_response;
2147 static char *full_username;
2148 static char *username;
2149 static char *domain;
2150 static char *plaintext_password;
2151 static bool ntlm_server_1_user_session_key;
2152 static bool ntlm_server_1_lm_session_key;
2154 if (strequal(buf, ".")) {
2155 if (!full_username && !username) {
2156 x_fprintf(x_stdout, "Error: No username supplied!\n");
2157 } else if (plaintext_password) {
2158 /* handle this request as plaintext */
2159 if (!full_username) {
2160 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
2161 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
2162 return;
2165 if (check_plaintext_auth(full_username, plaintext_password, False)) {
2166 x_fprintf(x_stdout, "Authenticated: Yes\n");
2167 } else {
2168 x_fprintf(x_stdout, "Authenticated: No\n");
2170 } else if (!lm_response.data && !nt_response.data) {
2171 x_fprintf(x_stdout, "Error: No password supplied!\n");
2172 } else if (!challenge.data) {
2173 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
2174 } else {
2175 char *error_string = NULL;
2176 uchar lm_key[8];
2177 uchar user_session_key[16];
2178 uint32 flags = 0;
2180 if (full_username && !username) {
2181 fstring fstr_user;
2182 fstring fstr_domain;
2184 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
2185 /* username might be 'tainted', don't print into our new-line deleimianted stream */
2186 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
2188 SAFE_FREE(username);
2189 SAFE_FREE(domain);
2190 username = smb_xstrdup(fstr_user);
2191 domain = smb_xstrdup(fstr_domain);
2194 if (!domain) {
2195 domain = smb_xstrdup(get_winbind_domain());
2198 if (ntlm_server_1_lm_session_key)
2199 flags |= WBFLAG_PAM_LMKEY;
2201 if (ntlm_server_1_user_session_key)
2202 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2204 if (!NT_STATUS_IS_OK(
2205 contact_winbind_auth_crap(username,
2206 domain,
2207 lp_netbios_name(),
2208 &challenge,
2209 &lm_response,
2210 &nt_response,
2211 flags, 0,
2212 lm_key,
2213 user_session_key,
2214 &error_string,
2215 NULL))) {
2217 x_fprintf(x_stdout, "Authenticated: No\n");
2218 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
2219 } else {
2220 static char zeros[16];
2221 char *hex_lm_key;
2222 char *hex_user_session_key;
2224 x_fprintf(x_stdout, "Authenticated: Yes\n");
2226 if (ntlm_server_1_lm_session_key
2227 && (memcmp(zeros, lm_key,
2228 sizeof(lm_key)) != 0)) {
2229 hex_lm_key = hex_encode_talloc(NULL,
2230 (const unsigned char *)lm_key,
2231 sizeof(lm_key));
2232 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
2233 TALLOC_FREE(hex_lm_key);
2236 if (ntlm_server_1_user_session_key
2237 && (memcmp(zeros, user_session_key,
2238 sizeof(user_session_key)) != 0)) {
2239 hex_user_session_key = hex_encode_talloc(NULL,
2240 (const unsigned char *)user_session_key,
2241 sizeof(user_session_key));
2242 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
2243 TALLOC_FREE(hex_user_session_key);
2246 SAFE_FREE(error_string);
2248 /* clear out the state */
2249 challenge = data_blob_null;
2250 nt_response = data_blob_null;
2251 lm_response = data_blob_null;
2252 SAFE_FREE(full_username);
2253 SAFE_FREE(username);
2254 SAFE_FREE(domain);
2255 SAFE_FREE(plaintext_password);
2256 ntlm_server_1_user_session_key = False;
2257 ntlm_server_1_lm_session_key = False;
2258 x_fprintf(x_stdout, ".\n");
2260 return;
2263 request = buf;
2265 /* Indicates a base64 encoded structure */
2266 parameter = strstr_m(request, ":: ");
2267 if (!parameter) {
2268 parameter = strstr_m(request, ": ");
2270 if (!parameter) {
2271 DEBUG(0, ("Parameter not found!\n"));
2272 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2273 return;
2276 parameter[0] ='\0';
2277 parameter++;
2278 parameter[0] ='\0';
2279 parameter++;
2281 } else {
2282 parameter[0] ='\0';
2283 parameter++;
2284 parameter[0] ='\0';
2285 parameter++;
2286 parameter[0] ='\0';
2287 parameter++;
2289 base64_decode_inplace(parameter);
2292 if (strequal(request, "LANMAN-Challenge")) {
2293 challenge = strhex_to_data_blob(NULL, parameter);
2294 if (challenge.length != 8) {
2295 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
2296 parameter,
2297 (int)challenge.length);
2298 challenge = data_blob_null;
2300 } else if (strequal(request, "NT-Response")) {
2301 nt_response = strhex_to_data_blob(NULL, parameter);
2302 if (nt_response.length < 24) {
2303 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
2304 parameter,
2305 (int)nt_response.length);
2306 nt_response = data_blob_null;
2308 } else if (strequal(request, "LANMAN-Response")) {
2309 lm_response = strhex_to_data_blob(NULL, parameter);
2310 if (lm_response.length != 24) {
2311 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
2312 parameter,
2313 (int)lm_response.length);
2314 lm_response = data_blob_null;
2316 } else if (strequal(request, "Password")) {
2317 plaintext_password = smb_xstrdup(parameter);
2318 } else if (strequal(request, "NT-Domain")) {
2319 domain = smb_xstrdup(parameter);
2320 } else if (strequal(request, "Username")) {
2321 username = smb_xstrdup(parameter);
2322 } else if (strequal(request, "Full-Username")) {
2323 full_username = smb_xstrdup(parameter);
2324 } else if (strequal(request, "Request-User-Session-Key")) {
2325 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2326 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2327 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2328 } else {
2329 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2333 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2334 struct loadparm_context *lp_ctx,
2335 struct ntlm_auth_state *state,
2336 char *buf, int length, void **private2)
2338 char *request, *parameter;
2339 static DATA_BLOB new_nt_pswd;
2340 static DATA_BLOB old_nt_hash_enc;
2341 static DATA_BLOB new_lm_pswd;
2342 static DATA_BLOB old_lm_hash_enc;
2343 static char *full_username = NULL;
2344 static char *username = NULL;
2345 static char *domain = NULL;
2346 static char *newpswd = NULL;
2347 static char *oldpswd = NULL;
2349 if (strequal(buf, ".")) {
2350 if(newpswd && oldpswd) {
2351 uchar old_nt_hash[16];
2352 uchar old_lm_hash[16];
2353 uchar new_nt_hash[16];
2354 uchar new_lm_hash[16];
2356 new_nt_pswd = data_blob(NULL, 516);
2357 old_nt_hash_enc = data_blob(NULL, 16);
2359 /* Calculate the MD4 hash (NT compatible) of the
2360 * password */
2361 E_md4hash(oldpswd, old_nt_hash);
2362 E_md4hash(newpswd, new_nt_hash);
2364 /* E_deshash returns false for 'long'
2365 passwords (> 14 DOS chars).
2367 Therefore, don't send a buffer
2368 encrypted with the truncated hash
2369 (it could allow an even easier
2370 attack on the password)
2372 Likewise, obey the admin's restriction
2375 if (lp_client_lanman_auth() &&
2376 E_deshash(newpswd, new_lm_hash) &&
2377 E_deshash(oldpswd, old_lm_hash)) {
2378 new_lm_pswd = data_blob(NULL, 516);
2379 old_lm_hash_enc = data_blob(NULL, 16);
2380 encode_pw_buffer(new_lm_pswd.data, newpswd,
2381 STR_UNICODE);
2383 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
2384 E_old_pw_hash(new_nt_hash, old_lm_hash,
2385 old_lm_hash_enc.data);
2386 } else {
2387 new_lm_pswd.data = NULL;
2388 new_lm_pswd.length = 0;
2389 old_lm_hash_enc.data = NULL;
2390 old_lm_hash_enc.length = 0;
2393 encode_pw_buffer(new_nt_pswd.data, newpswd,
2394 STR_UNICODE);
2396 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
2397 E_old_pw_hash(new_nt_hash, old_nt_hash,
2398 old_nt_hash_enc.data);
2401 if (!full_username && !username) {
2402 x_fprintf(x_stdout, "Error: No username supplied!\n");
2403 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2404 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2405 x_fprintf(x_stdout, "Error: No NT or LM password "
2406 "blobs supplied!\n");
2407 } else {
2408 char *error_string = NULL;
2410 if (full_username && !username) {
2411 fstring fstr_user;
2412 fstring fstr_domain;
2414 if (!parse_ntlm_auth_domain_user(full_username,
2415 fstr_user,
2416 fstr_domain)) {
2417 /* username might be 'tainted', don't
2418 * print into our new-line
2419 * deleimianted stream */
2420 x_fprintf(x_stdout, "Error: Could not "
2421 "parse into domain and "
2422 "username\n");
2423 SAFE_FREE(username);
2424 username = smb_xstrdup(full_username);
2425 } else {
2426 SAFE_FREE(username);
2427 SAFE_FREE(domain);
2428 username = smb_xstrdup(fstr_user);
2429 domain = smb_xstrdup(fstr_domain);
2434 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2435 username, domain,
2436 new_nt_pswd,
2437 old_nt_hash_enc,
2438 new_lm_pswd,
2439 old_lm_hash_enc,
2440 &error_string))) {
2441 x_fprintf(x_stdout, "Password-Change: No\n");
2442 x_fprintf(x_stdout, "Password-Change-Error: "
2443 "%s\n.\n", error_string);
2444 } else {
2445 x_fprintf(x_stdout, "Password-Change: Yes\n");
2448 SAFE_FREE(error_string);
2450 /* clear out the state */
2451 new_nt_pswd = data_blob_null;
2452 old_nt_hash_enc = data_blob_null;
2453 new_lm_pswd = data_blob_null;
2454 old_nt_hash_enc = data_blob_null;
2455 SAFE_FREE(full_username);
2456 SAFE_FREE(username);
2457 SAFE_FREE(domain);
2458 SAFE_FREE(newpswd);
2459 SAFE_FREE(oldpswd);
2460 x_fprintf(x_stdout, ".\n");
2462 return;
2465 request = buf;
2467 /* Indicates a base64 encoded structure */
2468 parameter = strstr_m(request, ":: ");
2469 if (!parameter) {
2470 parameter = strstr_m(request, ": ");
2472 if (!parameter) {
2473 DEBUG(0, ("Parameter not found!\n"));
2474 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2475 return;
2478 parameter[0] ='\0';
2479 parameter++;
2480 parameter[0] ='\0';
2481 parameter++;
2482 } else {
2483 parameter[0] ='\0';
2484 parameter++;
2485 parameter[0] ='\0';
2486 parameter++;
2487 parameter[0] ='\0';
2488 parameter++;
2490 base64_decode_inplace(parameter);
2493 if (strequal(request, "new-nt-password-blob")) {
2494 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2495 if (new_nt_pswd.length != 516) {
2496 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2497 "(got %d bytes, expected 516)\n.\n",
2498 parameter,
2499 (int)new_nt_pswd.length);
2500 new_nt_pswd = data_blob_null;
2502 } else if (strequal(request, "old-nt-hash-blob")) {
2503 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2504 if (old_nt_hash_enc.length != 16) {
2505 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2506 "(got %d bytes, expected 16)\n.\n",
2507 parameter,
2508 (int)old_nt_hash_enc.length);
2509 old_nt_hash_enc = data_blob_null;
2511 } else if (strequal(request, "new-lm-password-blob")) {
2512 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2513 if (new_lm_pswd.length != 516) {
2514 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2515 "(got %d bytes, expected 516)\n.\n",
2516 parameter,
2517 (int)new_lm_pswd.length);
2518 new_lm_pswd = data_blob_null;
2521 else if (strequal(request, "old-lm-hash-blob")) {
2522 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2523 if (old_lm_hash_enc.length != 16)
2525 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2526 "(got %d bytes, expected 16)\n.\n",
2527 parameter,
2528 (int)old_lm_hash_enc.length);
2529 old_lm_hash_enc = data_blob_null;
2531 } else if (strequal(request, "nt-domain")) {
2532 domain = smb_xstrdup(parameter);
2533 } else if(strequal(request, "username")) {
2534 username = smb_xstrdup(parameter);
2535 } else if(strequal(request, "full-username")) {
2536 username = smb_xstrdup(parameter);
2537 } else if(strequal(request, "new-password")) {
2538 newpswd = smb_xstrdup(parameter);
2539 } else if (strequal(request, "old-password")) {
2540 oldpswd = smb_xstrdup(parameter);
2541 } else {
2542 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2546 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2547 struct loadparm_context *lp_ctx,
2548 struct ntlm_auth_state *state,
2549 stdio_helper_function fn, void **private2)
2551 char *buf;
2552 char tmp[INITIAL_BUFFER_SIZE+1];
2553 int length, buf_size = 0;
2554 char *c;
2556 buf = talloc_strdup(state->mem_ctx, "");
2557 if (!buf) {
2558 DEBUG(0, ("Failed to allocate input buffer.\n"));
2559 x_fprintf(x_stderr, "ERR\n");
2560 exit(1);
2563 do {
2565 /* this is not a typo - x_fgets doesn't work too well under
2566 * squid */
2567 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2568 if (ferror(stdin)) {
2569 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2570 "(%s)\n", ferror(stdin),
2571 strerror(ferror(stdin))));
2573 exit(1);
2575 exit(0);
2578 buf = talloc_strdup_append_buffer(buf, tmp);
2579 buf_size += INITIAL_BUFFER_SIZE;
2581 if (buf_size > MAX_BUFFER_SIZE) {
2582 DEBUG(2, ("Oversized message\n"));
2583 x_fprintf(x_stderr, "ERR\n");
2584 talloc_free(buf);
2585 return;
2588 c = strchr(buf, '\n');
2589 } while (c == NULL);
2591 *c = '\0';
2592 length = c-buf;
2594 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2596 if (buf[0] == '\0') {
2597 DEBUG(2, ("Invalid Request\n"));
2598 x_fprintf(x_stderr, "ERR\n");
2599 talloc_free(buf);
2600 return;
2603 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2604 talloc_free(buf);
2608 static void squid_stream(enum stdio_helper_mode stdio_mode,
2609 struct loadparm_context *lp_ctx,
2610 stdio_helper_function fn) {
2611 TALLOC_CTX *mem_ctx;
2612 struct ntlm_auth_state *state;
2614 /* initialize FDescs */
2615 x_setbuf(x_stdout, NULL);
2616 x_setbuf(x_stderr, NULL);
2618 mem_ctx = talloc_init("ntlm_auth");
2619 if (!mem_ctx) {
2620 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2621 x_fprintf(x_stderr, "ERR\n");
2622 exit(1);
2625 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2626 if (!state) {
2627 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2628 x_fprintf(x_stderr, "ERR\n");
2629 exit(1);
2632 state->mem_ctx = mem_ctx;
2633 state->helper_mode = stdio_mode;
2635 while(1) {
2636 TALLOC_CTX *frame = talloc_stackframe();
2637 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2638 TALLOC_FREE(frame);
2643 /* Authenticate a user with a challenge/response */
2645 static bool check_auth_crap(void)
2647 NTSTATUS nt_status;
2648 uint32 flags = 0;
2649 char lm_key[8];
2650 char user_session_key[16];
2651 char *hex_lm_key;
2652 char *hex_user_session_key;
2653 char *error_string;
2654 static uint8 zeros[16];
2656 x_setbuf(x_stdout, NULL);
2658 if (request_lm_key)
2659 flags |= WBFLAG_PAM_LMKEY;
2661 if (request_user_session_key)
2662 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2664 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2666 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2667 opt_workstation,
2668 &opt_challenge,
2669 &opt_lm_response,
2670 &opt_nt_response,
2671 flags, 0,
2672 (unsigned char *)lm_key,
2673 (unsigned char *)user_session_key,
2674 &error_string, NULL);
2676 if (!NT_STATUS_IS_OK(nt_status)) {
2677 x_fprintf(x_stdout, "%s (0x%x)\n",
2678 error_string,
2679 NT_STATUS_V(nt_status));
2680 SAFE_FREE(error_string);
2681 return False;
2684 if (request_lm_key
2685 && (memcmp(zeros, lm_key,
2686 sizeof(lm_key)) != 0)) {
2687 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2688 sizeof(lm_key));
2689 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2690 TALLOC_FREE(hex_lm_key);
2692 if (request_user_session_key
2693 && (memcmp(zeros, user_session_key,
2694 sizeof(user_session_key)) != 0)) {
2695 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2696 sizeof(user_session_key));
2697 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2698 TALLOC_FREE(hex_user_session_key);
2701 return True;
2704 /* Main program */
2706 enum {
2707 OPT_USERNAME = 1000,
2708 OPT_DOMAIN,
2709 OPT_WORKSTATION,
2710 OPT_CHALLENGE,
2711 OPT_RESPONSE,
2712 OPT_LM,
2713 OPT_NT,
2714 OPT_PASSWORD,
2715 OPT_LM_KEY,
2716 OPT_USER_SESSION_KEY,
2717 OPT_DIAGNOSTICS,
2718 OPT_REQUIRE_MEMBERSHIP,
2719 OPT_USE_CACHED_CREDS,
2720 OPT_PAM_WINBIND_CONF,
2721 OPT_TARGET_SERVICE,
2722 OPT_TARGET_HOSTNAME
2725 int main(int argc, const char **argv)
2727 TALLOC_CTX *frame = talloc_stackframe();
2728 int opt;
2729 static const char *helper_protocol;
2730 static int diagnostics;
2732 static const char *hex_challenge;
2733 static const char *hex_lm_response;
2734 static const char *hex_nt_response;
2735 struct loadparm_context *lp_ctx;
2736 poptContext pc;
2738 /* NOTE: DO NOT change this interface without considering the implications!
2739 This is an external interface, which other programs will use to interact
2740 with this helper.
2743 /* We do not use single-letter command abbreviations, because they harm future
2744 interface stability. */
2746 struct poptOption long_options[] = {
2747 POPT_AUTOHELP
2748 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2749 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2750 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2751 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2752 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2753 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2754 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2755 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2756 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2757 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2758 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2759 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2760 OPT_DIAGNOSTICS,
2761 "Perform diagnostics on the authentication chain"},
2762 { "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" },
2763 { "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" },
2764 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2765 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2766 POPT_COMMON_CONFIGFILE
2767 POPT_COMMON_VERSION
2768 POPT_TABLEEND
2771 /* Samba client initialisation */
2772 load_case_tables();
2774 setup_logging("ntlm_auth", DEBUG_STDERR);
2776 /* Parse options */
2778 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2780 /* Parse command line options */
2782 if (argc == 1) {
2783 poptPrintHelp(pc, stderr, 0);
2784 return 1;
2787 while((opt = poptGetNextOpt(pc)) != -1) {
2788 /* Get generic config options like --configfile */
2791 poptFreeContext(pc);
2793 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2794 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2795 get_dyn_CONFIGFILE(), strerror(errno));
2796 exit(1);
2799 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2800 POPT_CONTEXT_KEEP_FIRST);
2802 while((opt = poptGetNextOpt(pc)) != -1) {
2803 switch (opt) {
2804 case OPT_CHALLENGE:
2805 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2806 if (opt_challenge.length != 8) {
2807 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2808 hex_challenge,
2809 (int)opt_challenge.length);
2810 exit(1);
2812 break;
2813 case OPT_LM:
2814 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2815 if (opt_lm_response.length != 24) {
2816 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2817 hex_lm_response,
2818 (int)opt_lm_response.length);
2819 exit(1);
2821 break;
2823 case OPT_NT:
2824 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2825 if (opt_nt_response.length < 24) {
2826 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2827 hex_nt_response,
2828 (int)opt_nt_response.length);
2829 exit(1);
2831 break;
2833 case OPT_REQUIRE_MEMBERSHIP:
2834 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2835 require_membership_of_sid = require_membership_of;
2837 break;
2841 if (opt_username) {
2842 char *domain = SMB_STRDUP(opt_username);
2843 char *p = strchr_m(domain, *lp_winbind_separator());
2844 if (p) {
2845 opt_username = p+1;
2846 *p = '\0';
2847 if (opt_domain && !strequal(opt_domain, domain)) {
2848 x_fprintf(x_stderr, "Domain specified in username (%s) "
2849 "doesn't match specified domain (%s)!\n\n",
2850 domain, opt_domain);
2851 poptPrintHelp(pc, stderr, 0);
2852 exit(1);
2854 opt_domain = domain;
2855 } else {
2856 SAFE_FREE(domain);
2860 /* Note: if opt_domain is "" then send no domain */
2861 if (opt_domain == NULL) {
2862 opt_domain = get_winbind_domain();
2865 if (opt_workstation == NULL) {
2866 opt_workstation = "";
2869 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2870 if (lp_ctx == NULL) {
2871 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2872 exit(1);
2875 if (helper_protocol) {
2876 int i;
2877 for (i=0; i<NUM_HELPER_MODES; i++) {
2878 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2879 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2880 exit(0);
2883 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2885 for (i=0; i<NUM_HELPER_MODES; i++) {
2886 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2889 exit(1);
2892 if (!opt_username || !*opt_username) {
2893 x_fprintf(x_stderr, "username must be specified!\n\n");
2894 poptPrintHelp(pc, stderr, 0);
2895 exit(1);
2898 if (opt_challenge.length) {
2899 if (!check_auth_crap()) {
2900 exit(1);
2902 exit(0);
2905 if (!opt_password) {
2906 char pwd[256] = {0};
2907 int rc;
2909 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2910 if (rc == 0) {
2911 opt_password = SMB_STRDUP(pwd);
2915 if (diagnostics) {
2916 if (!diagnose_ntlm_auth()) {
2917 return 1;
2919 } else {
2920 fstring user;
2922 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2923 if (!check_plaintext_auth(user, opt_password, True)) {
2924 return 1;
2928 /* Exit code */
2930 poptFreeContext(pc);
2931 TALLOC_FREE(frame);
2932 return 0;