Make krb5 wrapper library common so they can be used all over
[Samba.git] / source3 / utils / ntlm_auth.c
blobdb9e4d0ff1c15fde978113e42c779d212df46b1a
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 "auth/kerberos/pac_utils.h"
50 #ifndef PAM_WINBIND_CONFIG_FILE
51 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
52 #endif
54 #define WINBIND_KRB5_AUTH 0x00000080
56 #undef DBGC_CLASS
57 #define DBGC_CLASS DBGC_WINBIND
59 #define INITIAL_BUFFER_SIZE 300
60 #define MAX_BUFFER_SIZE 630000
62 enum stdio_helper_mode {
63 SQUID_2_4_BASIC,
64 SQUID_2_5_BASIC,
65 SQUID_2_5_NTLMSSP,
66 NTLMSSP_CLIENT_1,
67 GSS_SPNEGO_SERVER,
68 GSS_SPNEGO_CLIENT,
69 NTLM_SERVER_1,
70 NTLM_CHANGE_PASSWORD_1,
71 NUM_HELPER_MODES
74 enum ntlm_auth_cli_state {
75 CLIENT_INITIAL = 0,
76 CLIENT_RESPONSE,
77 CLIENT_FINISHED,
78 CLIENT_ERROR
81 struct ntlm_auth_state {
82 TALLOC_CTX *mem_ctx;
83 enum stdio_helper_mode helper_mode;
84 enum ntlm_auth_cli_state cli_state;
85 struct ntlmssp_state *ntlmssp_state;
86 uint32_t neg_flags;
87 char *want_feature_list;
88 bool have_session_key;
89 DATA_BLOB session_key;
90 DATA_BLOB initial_message;
91 void *gensec_private_1;
93 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
94 struct loadparm_context *lp_ctx,
95 struct ntlm_auth_state *state, char *buf,
96 int length, void **private2);
98 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
99 struct loadparm_context *lp_ctx,
100 struct ntlm_auth_state *state,
101 stdio_helper_function fn, void **private2);
103 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
104 struct loadparm_context *lp_ctx,
105 struct ntlm_auth_state *state,
106 char *buf, int length, void **private2);
108 static void manage_squid_ntlmssp_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_client_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_gss_spnego_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_client_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_ntlm_server_1_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_change_password_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 const struct {
139 enum stdio_helper_mode mode;
140 const char *name;
141 stdio_helper_function fn;
142 } stdio_helper_protocols[] = {
143 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
144 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
145 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
146 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
147 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
148 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
149 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
150 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
151 { NUM_HELPER_MODES, NULL, NULL}
154 const char *opt_username;
155 const char *opt_domain;
156 const char *opt_workstation;
157 const char *opt_password;
158 static DATA_BLOB opt_challenge;
159 static DATA_BLOB opt_lm_response;
160 static DATA_BLOB opt_nt_response;
161 static int request_lm_key;
162 static int request_user_session_key;
163 static int use_cached_creds;
165 static const char *require_membership_of;
166 static const char *require_membership_of_sid;
167 static const char *opt_pam_winbind_conf;
169 const char *opt_target_service;
170 const char *opt_target_hostname;
173 /* This is a bit hairy, but the basic idea is to do a password callback
174 to the calling application. The callback comes from within gensec */
176 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
177 struct loadparm_context *lp_ctx,
178 struct ntlm_auth_state *state, char *buf, int length,
179 void **password)
181 DATA_BLOB in;
182 if (strlen(buf) < 2) {
183 DEBUG(1, ("query [%s] invalid", buf));
184 x_fprintf(x_stdout, "BH Query invalid\n");
185 return;
188 if (strlen(buf) > 3) {
189 in = base64_decode_data_blob(buf + 3);
190 } else {
191 in = data_blob(NULL, 0);
194 if (strncmp(buf, "PW ", 3) == 0) {
196 *password = talloc_strndup(NULL,
197 (const char *)in.data, in.length);
199 if (*password == NULL) {
200 DEBUG(1, ("Out of memory\n"));
201 x_fprintf(x_stdout, "BH Out of memory\n");
202 data_blob_free(&in);
203 return;
206 x_fprintf(x_stdout, "OK\n");
207 data_blob_free(&in);
208 return;
210 DEBUG(1, ("Asked for (and expected) a password\n"));
211 x_fprintf(x_stdout, "BH Expected a password\n");
212 data_blob_free(&in);
216 * Callback for password credentials. This is not async, and when
217 * GENSEC and the credentials code is made async, it will look rather
218 * different.
221 static const char *get_password(struct cli_credentials *credentials)
223 char *password = NULL;
225 /* Ask for a password */
226 x_fprintf(x_stdout, "PW\n");
227 credentials->priv_data = NULL;
229 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, NULL, manage_gensec_get_pw_request, (void **)&password);
230 talloc_steal(credentials, password);
231 return password;
235 * A limited set of features are defined with text strings as needed
236 * by ntlm_auth
239 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
241 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
242 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
243 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
245 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
246 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
247 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
249 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
250 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
251 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
255 static char winbind_separator(void)
257 struct winbindd_response response;
258 static bool got_sep;
259 static char sep;
261 if (got_sep)
262 return sep;
264 ZERO_STRUCT(response);
266 /* Send off request */
268 if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
269 NSS_STATUS_SUCCESS) {
270 d_printf("could not obtain winbind separator!\n");
271 return *lp_winbind_separator();
274 sep = response.data.info.winbind_separator;
275 got_sep = True;
277 if (!sep) {
278 d_printf("winbind separator was NULL!\n");
279 return *lp_winbind_separator();
282 return sep;
285 const char *get_winbind_domain(void)
287 struct winbindd_response response;
289 static fstring winbind_domain;
290 if (*winbind_domain) {
291 return winbind_domain;
294 ZERO_STRUCT(response);
296 /* Send off request */
298 if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
299 NSS_STATUS_SUCCESS) {
300 DEBUG(0, ("could not obtain winbind domain name!\n"));
301 return lp_workgroup();
304 fstrcpy(winbind_domain, response.data.domain_name);
306 return winbind_domain;
310 const char *get_winbind_netbios_name(void)
312 struct winbindd_response response;
314 static fstring winbind_netbios_name;
316 if (*winbind_netbios_name) {
317 return winbind_netbios_name;
320 ZERO_STRUCT(response);
322 /* Send off request */
324 if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) !=
325 NSS_STATUS_SUCCESS) {
326 DEBUG(0, ("could not obtain winbind netbios name!\n"));
327 return lp_netbios_name();
330 fstrcpy(winbind_netbios_name, response.data.netbios_name);
332 return winbind_netbios_name;
336 DATA_BLOB get_challenge(void)
338 static DATA_BLOB chal;
339 if (opt_challenge.length)
340 return opt_challenge;
342 chal = data_blob(NULL, 8);
344 generate_random_buffer(chal.data, chal.length);
345 return chal;
348 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
349 form DOMAIN/user into a domain and a user */
351 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
352 fstring user)
355 char *p = strchr(domuser,winbind_separator());
357 if (!p) {
358 return False;
361 fstrcpy(user, p+1);
362 fstrcpy(domain, domuser);
363 domain[PTR_DIFF(p, domuser)] = 0;
364 strupper_m(domain);
366 return True;
369 static bool get_require_membership_sid(void) {
370 struct winbindd_request request;
371 struct winbindd_response response;
373 if (!require_membership_of) {
374 return True;
377 if (require_membership_of_sid) {
378 return True;
381 /* Otherwise, ask winbindd for the name->sid request */
383 ZERO_STRUCT(request);
384 ZERO_STRUCT(response);
386 if (!parse_ntlm_auth_domain_user(require_membership_of,
387 request.data.name.dom_name,
388 request.data.name.name)) {
389 DEBUG(0, ("Could not parse %s into seperate domain/name parts!\n",
390 require_membership_of));
391 return False;
394 if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
395 NSS_STATUS_SUCCESS) {
396 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
397 require_membership_of));
398 return False;
401 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
403 if (require_membership_of_sid)
404 return True;
406 return False;
410 * Get some configuration from pam_winbind.conf to see if we
411 * need to contact trusted domain
414 int get_pam_winbind_config()
416 int ctrl = 0;
417 dictionary *d = NULL;
419 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
420 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
423 d = iniparser_load(discard_const_p(char, opt_pam_winbind_conf));
425 if (!d) {
426 return 0;
429 if (iniparser_getboolean(d, discard_const_p(char, "global:krb5_auth"), false)) {
430 ctrl |= WINBIND_KRB5_AUTH;
433 iniparser_freedict(d);
435 return ctrl;
438 /* Authenticate a user with a plaintext password */
440 static bool check_plaintext_auth(const char *user, const char *pass,
441 bool stdout_diagnostics)
443 struct winbindd_request request;
444 struct winbindd_response response;
445 NSS_STATUS result;
447 if (!get_require_membership_sid()) {
448 return False;
451 /* Send off request */
453 ZERO_STRUCT(request);
454 ZERO_STRUCT(response);
456 fstrcpy(request.data.auth.user, user);
457 fstrcpy(request.data.auth.pass, pass);
458 if (require_membership_of_sid) {
459 strlcpy(request.data.auth.require_membership_of_sid,
460 require_membership_of_sid,
461 sizeof(request.data.auth.require_membership_of_sid));
464 result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
466 /* Display response */
468 if (stdout_diagnostics) {
469 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
470 d_printf("Reading winbind reply failed! (0x01)\n");
473 d_printf("%s: %s (0x%x)\n",
474 response.data.auth.nt_status_string,
475 response.data.auth.error_string,
476 response.data.auth.nt_status);
477 } else {
478 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
479 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
482 DEBUG(3, ("%s: %s (0x%x)\n",
483 response.data.auth.nt_status_string,
484 response.data.auth.error_string,
485 response.data.auth.nt_status));
488 return (result == NSS_STATUS_SUCCESS);
491 /* authenticate a user with an encrypted username/password */
493 NTSTATUS contact_winbind_auth_crap(const char *username,
494 const char *domain,
495 const char *workstation,
496 const DATA_BLOB *challenge,
497 const DATA_BLOB *lm_response,
498 const DATA_BLOB *nt_response,
499 uint32 flags,
500 uint32 extra_logon_parameters,
501 uint8 lm_key[8],
502 uint8 user_session_key[16],
503 char **error_string,
504 char **unix_name)
506 NTSTATUS nt_status;
507 NSS_STATUS result;
508 struct winbindd_request request;
509 struct winbindd_response response;
511 if (!get_require_membership_sid()) {
512 return NT_STATUS_INVALID_PARAMETER;
515 ZERO_STRUCT(request);
516 ZERO_STRUCT(response);
518 request.flags = flags;
520 request.data.auth_crap.logon_parameters = extra_logon_parameters
521 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
523 if (require_membership_of_sid)
524 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
526 fstrcpy(request.data.auth_crap.user, username);
527 fstrcpy(request.data.auth_crap.domain, domain);
529 fstrcpy(request.data.auth_crap.workstation,
530 workstation);
532 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
534 if (lm_response && lm_response->length) {
535 memcpy(request.data.auth_crap.lm_resp,
536 lm_response->data,
537 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
538 request.data.auth_crap.lm_resp_len = lm_response->length;
541 if (nt_response && nt_response->length) {
542 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
543 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
544 request.extra_len = nt_response->length;
545 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
546 if (request.extra_data.data == NULL) {
547 return NT_STATUS_NO_MEMORY;
549 memcpy(request.extra_data.data, nt_response->data,
550 nt_response->length);
552 } else {
553 memcpy(request.data.auth_crap.nt_resp,
554 nt_response->data, nt_response->length);
556 request.data.auth_crap.nt_resp_len = nt_response->length;
559 result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
560 SAFE_FREE(request.extra_data.data);
562 /* Display response */
564 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
565 nt_status = NT_STATUS_UNSUCCESSFUL;
566 if (error_string)
567 *error_string = smb_xstrdup("Reading winbind reply failed!");
568 winbindd_free_response(&response);
569 return nt_status;
572 nt_status = (NT_STATUS(response.data.auth.nt_status));
573 if (!NT_STATUS_IS_OK(nt_status)) {
574 if (error_string)
575 *error_string = smb_xstrdup(response.data.auth.error_string);
576 winbindd_free_response(&response);
577 return nt_status;
580 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
581 memcpy(lm_key, response.data.auth.first_8_lm_hash,
582 sizeof(response.data.auth.first_8_lm_hash));
584 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
585 memcpy(user_session_key, response.data.auth.user_session_key,
586 sizeof(response.data.auth.user_session_key));
589 if (flags & WBFLAG_PAM_UNIX_NAME) {
590 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
591 if (!*unix_name) {
592 winbindd_free_response(&response);
593 return NT_STATUS_NO_MEMORY;
597 winbindd_free_response(&response);
598 return nt_status;
601 /* contact server to change user password using auth crap */
602 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
603 const char *domain,
604 const DATA_BLOB new_nt_pswd,
605 const DATA_BLOB old_nt_hash_enc,
606 const DATA_BLOB new_lm_pswd,
607 const DATA_BLOB old_lm_hash_enc,
608 char **error_string)
610 NTSTATUS nt_status;
611 NSS_STATUS result;
612 struct winbindd_request request;
613 struct winbindd_response response;
615 if (!get_require_membership_sid())
617 if(error_string)
618 *error_string = smb_xstrdup("Can't get membership sid.");
619 return NT_STATUS_INVALID_PARAMETER;
622 ZERO_STRUCT(request);
623 ZERO_STRUCT(response);
625 if(username != NULL)
626 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
627 if(domain != NULL)
628 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
630 if(new_nt_pswd.length)
632 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
633 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
636 if(old_nt_hash_enc.length)
638 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));
639 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
642 if(new_lm_pswd.length)
644 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
645 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
648 if(old_lm_hash_enc.length)
650 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));
651 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
654 result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
656 /* Display response */
658 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
660 nt_status = NT_STATUS_UNSUCCESSFUL;
661 if (error_string)
662 *error_string = smb_xstrdup("Reading winbind reply failed!");
663 winbindd_free_response(&response);
664 return nt_status;
667 nt_status = (NT_STATUS(response.data.auth.nt_status));
668 if (!NT_STATUS_IS_OK(nt_status))
670 if (error_string)
671 *error_string = smb_xstrdup(response.data.auth.error_string);
672 winbindd_free_response(&response);
673 return nt_status;
676 winbindd_free_response(&response);
678 return nt_status;
681 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
682 TALLOC_CTX *mem_ctx,
683 void *server_returned_info,
684 const char *original_user_name,
685 uint32_t session_info_flags,
686 struct auth_session_info **session_info_out)
688 char *unix_username = (char *)server_returned_info;
689 struct auth_session_info *session_info = talloc_zero(mem_ctx, struct auth_session_info);
690 if (!session_info) {
691 return NT_STATUS_NO_MEMORY;
694 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
695 if (!session_info->unix_info) {
696 TALLOC_FREE(session_info);
697 return NT_STATUS_NO_MEMORY;
699 session_info->unix_info->unix_name = talloc_steal(session_info->unix_info, unix_username);
701 *session_info_out = session_info;
703 return NT_STATUS_OK;
706 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
707 TALLOC_CTX *mem_ctx,
708 struct smb_krb5_context *smb_krb5_context,
709 DATA_BLOB *pac_blob,
710 const char *princ_name,
711 const struct tsocket_address *remote_address,
712 uint32_t session_info_flags,
713 struct auth_session_info **session_info)
715 TALLOC_CTX *tmp_ctx;
716 struct PAC_DATA *pac_data = NULL;
717 struct PAC_LOGON_INFO *logon_info = NULL;
718 unsigned int i;
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_decode_pac(tmp_ctx,
734 *pac_blob,
735 NULL, NULL, NULL, NULL, 0, &pac_data);
736 #else
737 status = NT_STATUS_ACCESS_DENIED;
738 #endif
739 if (!NT_STATUS_IS_OK(status)) {
740 goto done;
743 /* get logon name and logon info */
744 for (i = 0; i < pac_data->num_buffers; i++) {
745 struct PAC_BUFFER *data_buf = &pac_data->buffers[i];
747 switch (data_buf->type) {
748 case PAC_TYPE_LOGON_INFO:
749 if (!data_buf->info) {
750 break;
752 logon_info = data_buf->info->logon_info.info;
753 break;
754 default:
755 break;
758 if (!logon_info) {
759 DEBUG(1, ("Invalid PAC data, missing logon info!\n"));
760 status = NT_STATUS_NOT_FOUND;
761 goto done;
765 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
767 p = strchr_m(princ_name, '@');
768 if (!p) {
769 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
770 princ_name));
771 return NT_STATUS_LOGON_FAILURE;
774 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
775 if (!user) {
776 return NT_STATUS_NO_MEMORY;
779 realm = talloc_strdup(talloc_tos(), p + 1);
780 if (!realm) {
781 return NT_STATUS_NO_MEMORY;
784 if (!strequal(realm, lp_realm())) {
785 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
786 if (!lp_allow_trusted_domains()) {
787 return NT_STATUS_LOGON_FAILURE;
791 if (logon_info && logon_info->info3.base.logon_domain.string) {
792 domain = talloc_strdup(mem_ctx,
793 logon_info->info3.base.logon_domain.string);
794 if (!domain) {
795 return NT_STATUS_NO_MEMORY;
797 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
798 } else {
800 /* If we have winbind running, we can (and must) shorten the
801 username by using the short netbios name. Otherwise we will
802 have inconsistent user names. With Kerberos, we get the
803 fully qualified realm, with ntlmssp we get the short
804 name. And even w2k3 does use ntlmssp if you for example
805 connect to an ip address. */
807 wbcErr wbc_status;
808 struct wbcDomainInfo *info = NULL;
810 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
811 realm));
813 wbc_status = wbcDomainInfo(realm, &info);
815 if (WBC_ERROR_IS_OK(wbc_status)) {
816 domain = talloc_strdup(mem_ctx,
817 info->short_name);
818 wbcFreeMemory(info);
819 } else {
820 DEBUG(3, ("Could not find short name: %s\n",
821 wbcErrorString(wbc_status)));
822 domain = talloc_strdup(mem_ctx, realm);
824 if (!domain) {
825 return NT_STATUS_NO_MEMORY;
827 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
830 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
831 if (!unixuser) {
832 status = NT_STATUS_NO_MEMORY;
833 goto done;
836 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
838 done:
839 TALLOC_FREE(tmp_ctx);
840 return status;
846 * Return the challenge as determined by the authentication subsystem
847 * @return an 8 byte random challenge
850 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
851 uint8_t chal[8])
853 if (auth_ctx->challenge.data.length == 8) {
854 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
855 auth_ctx->challenge.set_by));
856 memcpy(chal, auth_ctx->challenge.data.data, 8);
857 return NT_STATUS_OK;
860 if (!auth_ctx->challenge.set_by) {
861 generate_random_buffer(chal, 8);
863 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
864 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
865 auth_ctx->challenge.set_by = "random";
867 auth_ctx->challenge.may_be_modified = true;
870 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
871 auth_ctx->challenge.set_by));
873 return NT_STATUS_OK;
877 * Some authentication methods 'fix' the challenge, so we may not be able to set it
879 * @return If the effective challenge used by the auth subsystem may be modified
881 static bool ntlm_auth_may_set_challenge(struct auth4_context *auth_ctx)
883 return auth_ctx->challenge.may_be_modified;
887 * NTLM2 authentication modifies the effective challenge,
888 * @param challenge The new challenge value
890 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
892 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
893 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
895 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
896 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
898 return NT_STATUS_OK;
902 * Check the password on an NTLMSSP login.
904 * Return the session keys used on the connection.
907 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
908 TALLOC_CTX *mem_ctx,
909 const struct auth_usersupplied_info *user_info,
910 void **server_returned_info,
911 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
913 static const char zeros[16] = { 0, };
914 NTSTATUS nt_status;
915 char *error_string = NULL;
916 uint8 lm_key[8];
917 uint8 user_sess_key[16];
918 char *unix_name = NULL;
920 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
921 user_info->workstation_name,
922 &auth4_context->challenge.data,
923 &user_info->password.response.lanman,
924 &user_info->password.response.nt,
925 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
927 lm_key, user_sess_key,
928 &error_string, &unix_name);
930 if (NT_STATUS_IS_OK(nt_status)) {
931 if (memcmp(lm_key, zeros, 8) != 0) {
932 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
933 memcpy(lm_session_key->data, lm_key, 8);
934 memset(lm_session_key->data+8, '\0', 8);
937 if (memcmp(user_sess_key, zeros, 16) != 0) {
938 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
940 *server_returned_info = talloc_strdup(mem_ctx,
941 unix_name);
942 } else {
943 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
944 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
945 user_info->client.domain_name, user_info->client.account_name,
946 user_info->workstation_name,
947 error_string ? error_string : "unknown error (NULL)"));
950 SAFE_FREE(error_string);
951 SAFE_FREE(unix_name);
952 return nt_status;
955 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
956 TALLOC_CTX *mem_ctx,
957 const struct auth_usersupplied_info *user_info,
958 void **server_returned_info,
959 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
961 NTSTATUS nt_status;
962 struct samr_Password lm_pw, nt_pw;
964 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
966 nt_status = ntlm_password_check(mem_ctx,
967 true, true, 0,
968 &auth4_context->challenge.data,
969 &user_info->password.response.lanman,
970 &user_info->password.response.nt,
971 user_info->client.account_name,
972 user_info->client.account_name,
973 user_info->client.domain_name,
974 &lm_pw, &nt_pw, session_key, lm_session_key);
976 if (NT_STATUS_IS_OK(nt_status)) {
977 *server_returned_info = talloc_asprintf(mem_ctx,
978 "%s%c%s", user_info->client.domain_name,
979 *lp_winbind_separator(),
980 user_info->client.account_name);
981 } else {
982 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
983 user_info->client.domain_name, user_info->client.account_name,
984 user_info->workstation_name,
985 nt_errstr(nt_status)));
987 return nt_status;
990 static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state)
992 NTSTATUS status;
993 if ( (opt_username == NULL) || (opt_domain == NULL) ) {
994 status = NT_STATUS_UNSUCCESSFUL;
995 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
996 return NT_STATUS_INVALID_PARAMETER;
999 status = ntlmssp_client_start(NULL,
1000 lp_netbios_name(),
1001 lp_workgroup(),
1002 lp_client_ntlmv2_auth(),
1003 client_ntlmssp_state);
1005 if (!NT_STATUS_IS_OK(status)) {
1006 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
1007 nt_errstr(status)));
1008 TALLOC_FREE(*client_ntlmssp_state);
1009 return status;
1012 status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
1014 if (!NT_STATUS_IS_OK(status)) {
1015 DEBUG(1, ("Could not set username: %s\n",
1016 nt_errstr(status)));
1017 TALLOC_FREE(*client_ntlmssp_state);
1018 return status;
1021 status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
1023 if (!NT_STATUS_IS_OK(status)) {
1024 DEBUG(1, ("Could not set domain: %s\n",
1025 nt_errstr(status)));
1026 TALLOC_FREE(*client_ntlmssp_state);
1027 return status;
1030 if (opt_password) {
1031 status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
1033 if (!NT_STATUS_IS_OK(status)) {
1034 DEBUG(1, ("Could not set password: %s\n",
1035 nt_errstr(status)));
1036 TALLOC_FREE(*client_ntlmssp_state);
1037 return status;
1041 return NT_STATUS_OK;
1044 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1046 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1047 if (auth4_context == NULL) {
1048 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1049 return NULL;
1051 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1052 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1053 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1054 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1055 auth4_context->challenge_may_be_modified = ntlm_auth_may_set_challenge;
1056 if (local_pw) {
1057 auth4_context->check_ntlm_password = local_pw_check;
1058 } else {
1059 auth4_context->check_ntlm_password = winbind_pw_check;
1061 auth4_context->private_data = NULL;
1062 return auth4_context;
1065 static NTSTATUS ntlm_auth_start_ntlmssp_server(TALLOC_CTX *mem_ctx,
1066 struct loadparm_context *lp_ctx,
1067 struct gensec_security **gensec_security_out)
1069 struct gensec_security *gensec_security;
1070 NTSTATUS nt_status;
1072 TALLOC_CTX *tmp_ctx;
1074 struct gensec_settings *gensec_settings;
1075 size_t idx = 0;
1076 struct cli_credentials *server_credentials;
1078 struct auth4_context *auth4_context;
1080 tmp_ctx = talloc_new(mem_ctx);
1081 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1083 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1084 if (auth4_context == NULL) {
1085 TALLOC_FREE(tmp_ctx);
1086 return NT_STATUS_NO_MEMORY;
1089 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1090 if (lp_ctx == NULL) {
1091 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1092 TALLOC_FREE(tmp_ctx);
1093 return NT_STATUS_NO_MEMORY;
1097 * This should be a 'netbios domain -> DNS domain'
1098 * mapping, and can currently validly return NULL on
1099 * poorly configured systems.
1101 * This is used for the NTLMSSP server
1104 if (opt_password) {
1105 gensec_settings->server_netbios_name = lp_netbios_name();
1106 gensec_settings->server_netbios_domain = lp_workgroup();
1107 } else {
1108 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1109 gensec_settings->server_netbios_domain = get_winbind_domain();
1112 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1113 get_mydnsdomname(talloc_tos()));
1114 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1115 get_mydnsfullname());
1117 gensec_settings->backends = talloc_zero_array(gensec_settings,
1118 struct gensec_security_ops *, 4);
1120 if (gensec_settings->backends == NULL) {
1121 TALLOC_FREE(tmp_ctx);
1122 return NT_STATUS_NO_MEMORY;
1125 gensec_init();
1127 /* These need to be in priority order, krb5 before NTLMSSP */
1128 #if defined(HAVE_KRB5)
1129 gensec_settings->backends[idx++] = &gensec_gse_krb5_security_ops;
1130 #endif
1132 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1134 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL,
1135 GENSEC_OID_SPNEGO);
1138 * This is anonymous for now, because we just use it
1139 * to set the kerberos state at the moment
1141 server_credentials = cli_credentials_init_anon(tmp_ctx);
1142 if (!server_credentials) {
1143 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1144 return NT_STATUS_NO_MEMORY;
1147 cli_credentials_set_conf(server_credentials, lp_ctx);
1149 if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1150 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1151 } else {
1152 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1155 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1156 auth4_context, &gensec_security);
1158 if (!NT_STATUS_IS_OK(nt_status)) {
1159 TALLOC_FREE(tmp_ctx);
1160 return nt_status;
1163 gensec_set_credentials(gensec_security, server_credentials);
1165 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
1166 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
1168 talloc_unlink(tmp_ctx, lp_ctx);
1169 talloc_unlink(tmp_ctx, server_credentials);
1170 talloc_unlink(tmp_ctx, gensec_settings);
1171 talloc_unlink(tmp_ctx, auth4_context);
1173 nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP);
1174 if (!NT_STATUS_IS_OK(nt_status)) {
1175 TALLOC_FREE(tmp_ctx);
1176 return nt_status;
1179 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1180 TALLOC_FREE(tmp_ctx);
1181 return NT_STATUS_OK;
1184 /*******************************************************************
1185 Used by firefox to drive NTLM auth to IIS servers.
1186 *******************************************************************/
1188 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
1189 DATA_BLOB *reply)
1191 struct winbindd_request wb_request;
1192 struct winbindd_response wb_response;
1193 int ctrl = 0;
1194 NSS_STATUS result;
1196 /* get winbindd to do the ntlmssp step on our behalf */
1197 ZERO_STRUCT(wb_request);
1198 ZERO_STRUCT(wb_response);
1201 * This is tricky here. If we set krb5_auth in pam_winbind.conf
1202 * creds for users in trusted domain will be stored the winbindd
1203 * child of the trusted domain. If we ask the primary domain for
1204 * ntlm_ccache_auth, it will fail. So, we have to ask the trusted
1205 * domain's child for ccache_ntlm_auth. that is to say, we have to
1206 * set WBFLAG_PAM_CONTACT_TRUSTDOM in request.flags.
1208 ctrl = get_pam_winbind_config();
1210 if (ctrl & WINBIND_KRB5_AUTH) {
1211 wb_request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1214 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
1215 "%s%c%s", opt_domain, winbind_separator(), opt_username);
1216 wb_request.data.ccache_ntlm_auth.uid = geteuid();
1217 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
1218 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
1219 wb_request.extra_len = initial_msg.length + challenge_msg.length;
1221 if (wb_request.extra_len > 0) {
1222 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
1223 if (wb_request.extra_data.data == NULL) {
1224 return NT_STATUS_NO_MEMORY;
1227 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
1228 memcpy(wb_request.extra_data.data + initial_msg.length,
1229 challenge_msg.data, challenge_msg.length);
1232 result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
1233 SAFE_FREE(wb_request.extra_data.data);
1235 if (result != NSS_STATUS_SUCCESS) {
1236 winbindd_free_response(&wb_response);
1237 return NT_STATUS_UNSUCCESSFUL;
1240 if (reply) {
1241 *reply = data_blob(wb_response.extra_data.data,
1242 wb_response.data.ccache_ntlm_auth.auth_blob_len);
1243 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
1244 reply->data == NULL) {
1245 winbindd_free_response(&wb_response);
1246 return NT_STATUS_NO_MEMORY;
1250 winbindd_free_response(&wb_response);
1251 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1254 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1255 struct loadparm_context *lp_ctx,
1256 struct ntlm_auth_state *state,
1257 char *buf, int length, void **private2)
1259 DATA_BLOB request, reply;
1260 NTSTATUS nt_status;
1262 if (!opt_username || !*opt_username) {
1263 x_fprintf(x_stderr, "username must be specified!\n\n");
1264 exit(1);
1267 if (strlen(buf) < 2) {
1268 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1269 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1270 return;
1273 if (strlen(buf) > 3) {
1274 if(strncmp(buf, "SF ", 3) == 0) {
1275 DEBUG(10, ("Looking for flags to negotiate\n"));
1276 talloc_free(state->want_feature_list);
1277 state->want_feature_list = talloc_strdup(state->mem_ctx,
1278 buf+3);
1279 x_fprintf(x_stdout, "OK\n");
1280 return;
1282 request = base64_decode_data_blob(buf + 3);
1283 } else {
1284 request = data_blob_null;
1287 if (strncmp(buf, "PW ", 3) == 0) {
1288 /* We asked for a password and obviously got it :-) */
1290 opt_password = SMB_STRNDUP((const char *)request.data,
1291 request.length);
1293 if (opt_password == NULL) {
1294 DEBUG(1, ("Out of memory\n"));
1295 x_fprintf(x_stdout, "BH Out of memory\n");
1296 data_blob_free(&request);
1297 return;
1300 x_fprintf(x_stdout, "OK\n");
1301 data_blob_free(&request);
1302 return;
1305 if (!state->ntlmssp_state && use_cached_creds) {
1306 /* check whether cached credentials are usable. */
1307 DATA_BLOB empty_blob = data_blob_null;
1309 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
1310 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1311 /* failed to use cached creds */
1312 use_cached_creds = False;
1316 if (opt_password == NULL && !use_cached_creds) {
1317 /* Request a password from the calling process. After
1318 sending it, the calling process should retry asking for the
1319 negotiate. */
1321 DEBUG(10, ("Requesting password\n"));
1322 x_fprintf(x_stdout, "PW\n");
1323 return;
1326 if (strncmp(buf, "YR", 2) == 0) {
1327 TALLOC_FREE(state->ntlmssp_state);
1328 state->cli_state = CLIENT_INITIAL;
1329 } else if (strncmp(buf, "TT", 2) == 0) {
1330 /* No special preprocessing required */
1331 } else if (strncmp(buf, "GF", 2) == 0) {
1332 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
1334 if(state->cli_state == CLIENT_FINISHED) {
1335 x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
1337 else {
1338 x_fprintf(x_stdout, "BH\n");
1341 data_blob_free(&request);
1342 return;
1343 } else if (strncmp(buf, "GK", 2) == 0 ) {
1344 DEBUG(10, ("Requested session key\n"));
1346 if(state->cli_state == CLIENT_FINISHED) {
1347 char *key64 = base64_encode_data_blob(state->mem_ctx,
1348 state->session_key);
1349 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
1350 TALLOC_FREE(key64);
1352 else {
1353 x_fprintf(x_stdout, "BH\n");
1356 data_blob_free(&request);
1357 return;
1358 } else {
1359 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1360 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1361 return;
1364 if (!state->ntlmssp_state) {
1365 nt_status = ntlm_auth_start_ntlmssp_client(
1366 &state->ntlmssp_state);
1367 if (!NT_STATUS_IS_OK(nt_status)) {
1368 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1369 return;
1371 ntlmssp_want_feature_list(state->ntlmssp_state,
1372 state->want_feature_list);
1373 state->initial_message = data_blob_null;
1376 DEBUG(10, ("got NTLMSSP packet:\n"));
1377 dump_data(10, request.data, request.length);
1379 if (use_cached_creds && !opt_password &&
1380 (state->cli_state == CLIENT_RESPONSE)) {
1381 nt_status = do_ccache_ntlm_auth(state->initial_message, request,
1382 &reply);
1383 } else {
1384 nt_status = ntlmssp_update(state->ntlmssp_state, request,
1385 &reply);
1388 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1389 char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
1390 reply);
1391 if (state->cli_state == CLIENT_INITIAL) {
1392 x_fprintf(x_stdout, "YR %s\n", reply_base64);
1393 state->initial_message = reply;
1394 state->cli_state = CLIENT_RESPONSE;
1395 } else {
1396 x_fprintf(x_stdout, "KK %s\n", reply_base64);
1397 data_blob_free(&reply);
1399 TALLOC_FREE(reply_base64);
1400 DEBUG(10, ("NTLMSSP challenge\n"));
1401 } else if (NT_STATUS_IS_OK(nt_status)) {
1402 char *reply_base64 = base64_encode_data_blob(talloc_tos(),
1403 reply);
1404 x_fprintf(x_stdout, "AF %s\n", reply_base64);
1405 TALLOC_FREE(reply_base64);
1407 if(state->have_session_key)
1408 data_blob_free(&state->session_key);
1410 state->session_key = data_blob(
1411 state->ntlmssp_state->session_key.data,
1412 state->ntlmssp_state->session_key.length);
1413 state->neg_flags = state->ntlmssp_state->neg_flags;
1414 state->have_session_key = true;
1416 DEBUG(10, ("NTLMSSP OK!\n"));
1417 state->cli_state = CLIENT_FINISHED;
1418 TALLOC_FREE(state->ntlmssp_state);
1419 } else {
1420 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1421 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
1422 state->cli_state = CLIENT_ERROR;
1423 TALLOC_FREE(state->ntlmssp_state);
1426 data_blob_free(&request);
1429 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1430 struct loadparm_context *lp_ctx,
1431 struct ntlm_auth_state *state,
1432 char *buf, int length, void **private2)
1434 char *user, *pass;
1435 user=buf;
1437 pass=(char *)memchr(buf,' ',length);
1438 if (!pass) {
1439 DEBUG(2, ("Password not found. Denying access\n"));
1440 x_fprintf(x_stdout, "ERR\n");
1441 return;
1443 *pass='\0';
1444 pass++;
1446 if (state->helper_mode == SQUID_2_5_BASIC) {
1447 rfc1738_unescape(user);
1448 rfc1738_unescape(pass);
1451 if (check_plaintext_auth(user, pass, False)) {
1452 x_fprintf(x_stdout, "OK\n");
1453 } else {
1454 x_fprintf(x_stdout, "ERR\n");
1458 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1459 struct loadparm_context *lp_ctx,
1460 char *buf, int length, void **private1)
1462 DATA_BLOB in;
1463 DATA_BLOB out = data_blob(NULL, 0);
1464 char *out_base64 = NULL;
1465 const char *reply_arg = NULL;
1466 struct gensec_ntlm_state {
1467 struct gensec_security *gensec_state;
1468 const char *set_password;
1470 struct gensec_ntlm_state *state;
1472 NTSTATUS nt_status;
1473 bool first = false;
1474 const char *reply_code;
1475 struct cli_credentials *creds;
1477 static char *want_feature_list = NULL;
1478 static DATA_BLOB session_key;
1480 TALLOC_CTX *mem_ctx;
1482 if (*private1) {
1483 state = (struct gensec_ntlm_state *)*private1;
1484 } else {
1485 state = talloc_zero(NULL, struct gensec_ntlm_state);
1486 if (!state) {
1487 x_fprintf(x_stdout, "BH No Memory\n");
1488 exit(1);
1490 *private1 = state;
1491 if (opt_password) {
1492 state->set_password = opt_password;
1496 if (strlen(buf) < 2) {
1497 DEBUG(1, ("query [%s] invalid", buf));
1498 x_fprintf(x_stdout, "BH Query invalid\n");
1499 return;
1502 if (strlen(buf) > 3) {
1503 if(strncmp(buf, "SF ", 3) == 0) {
1504 DEBUG(10, ("Setting flags to negotiate\n"));
1505 talloc_free(want_feature_list);
1506 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1507 x_fprintf(x_stdout, "OK\n");
1508 return;
1510 in = base64_decode_data_blob(buf + 3);
1511 } else {
1512 in = data_blob(NULL, 0);
1515 if (strncmp(buf, "YR", 2) == 0) {
1516 if (state->gensec_state) {
1517 talloc_free(state->gensec_state);
1518 state->gensec_state = NULL;
1520 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1521 /* Just return BH, like ntlm_auth from Samba 3 does. */
1522 x_fprintf(x_stdout, "BH Command expected\n");
1523 data_blob_free(&in);
1524 return;
1525 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1526 (strncmp(buf, "KK ", 3) != 0) &&
1527 (strncmp(buf, "AF ", 3) != 0) &&
1528 (strncmp(buf, "NA ", 3) != 0) &&
1529 (strncmp(buf, "UG", 2) != 0) &&
1530 (strncmp(buf, "PW ", 3) != 0) &&
1531 (strncmp(buf, "GK", 2) != 0) &&
1532 (strncmp(buf, "GF", 2) != 0)) {
1533 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1534 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1535 data_blob_free(&in);
1536 return;
1539 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1541 /* setup gensec */
1542 if (!(state->gensec_state)) {
1543 switch (stdio_helper_mode) {
1544 case GSS_SPNEGO_CLIENT:
1545 case NTLMSSP_CLIENT_1:
1546 /* setup the client side */
1548 nt_status = gensec_client_start(NULL, &state->gensec_state,
1549 lpcfg_gensec_settings(NULL, lp_ctx));
1550 if (!NT_STATUS_IS_OK(nt_status)) {
1551 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1552 talloc_free(mem_ctx);
1553 return;
1556 creds = cli_credentials_init(state->gensec_state);
1557 cli_credentials_set_conf(creds, lp_ctx);
1558 if (opt_username) {
1559 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1561 if (opt_domain) {
1562 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1564 if (state->set_password) {
1565 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1566 } else {
1567 cli_credentials_set_password_callback(creds, get_password);
1569 if (opt_workstation) {
1570 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1573 gensec_set_credentials(state->gensec_state, creds);
1575 break;
1576 case GSS_SPNEGO_SERVER:
1577 case SQUID_2_5_NTLMSSP:
1579 nt_status = ntlm_auth_start_ntlmssp_server(state, lp_ctx,
1580 &state->gensec_state);
1581 if (!NT_STATUS_IS_OK(nt_status)) {
1582 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1583 talloc_free(mem_ctx);
1584 return;
1586 break;
1588 default:
1589 talloc_free(mem_ctx);
1590 abort();
1593 gensec_want_feature_list(state->gensec_state, want_feature_list);
1595 switch (stdio_helper_mode) {
1596 case GSS_SPNEGO_CLIENT:
1597 case GSS_SPNEGO_SERVER:
1598 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1599 if (!in.length) {
1600 first = true;
1602 break;
1603 case NTLMSSP_CLIENT_1:
1604 if (!in.length) {
1605 first = true;
1607 /* fall through */
1608 case SQUID_2_5_NTLMSSP:
1609 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1610 break;
1611 default:
1612 talloc_free(mem_ctx);
1613 abort();
1616 if (!NT_STATUS_IS_OK(nt_status)) {
1617 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1618 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1619 talloc_free(mem_ctx);
1620 return;
1625 /* update */
1627 if (strncmp(buf, "PW ", 3) == 0) {
1628 state->set_password = talloc_strndup(state,
1629 (const char *)in.data,
1630 in.length);
1632 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1633 state->set_password,
1634 CRED_SPECIFIED);
1635 x_fprintf(x_stdout, "OK\n");
1636 data_blob_free(&in);
1637 talloc_free(mem_ctx);
1638 return;
1641 if (strncmp(buf, "GK", 2) == 0) {
1642 char *base64_key;
1643 DEBUG(10, ("Requested session key\n"));
1644 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1645 if(!NT_STATUS_IS_OK(nt_status)) {
1646 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1647 x_fprintf(x_stdout, "BH No session key\n");
1648 talloc_free(mem_ctx);
1649 return;
1650 } else {
1651 base64_key = base64_encode_data_blob(state, session_key);
1652 x_fprintf(x_stdout, "GK %s\n", base64_key);
1653 talloc_free(base64_key);
1655 talloc_free(mem_ctx);
1656 return;
1659 if (stdio_helper_mode == SQUID_2_5_NTLMSSP && strncmp(buf, "GF", 2) == 0) {
1660 uint32_t neg_flags;
1662 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1664 DEBUG(10, ("Requested negotiated feature flags\n"));
1665 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1666 return;
1669 nt_status = gensec_update(state->gensec_state, mem_ctx, NULL, in, &out);
1671 /* don't leak 'bad password'/'no such user' info to the network client */
1672 nt_status = nt_status_squash(nt_status);
1674 if (out.length) {
1675 out_base64 = base64_encode_data_blob(mem_ctx, out);
1676 } else {
1677 out_base64 = NULL;
1680 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1681 reply_arg = "*";
1682 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1683 reply_code = "YR";
1684 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1685 reply_code = "KK";
1686 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1687 reply_code = "TT";
1688 } else {
1689 abort();
1693 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1694 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1695 reply_arg = nt_errstr(nt_status);
1696 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1697 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1698 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1699 reply_arg = nt_errstr(nt_status);
1700 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1701 } else if (!NT_STATUS_IS_OK(nt_status)) {
1702 reply_code = "NA";
1703 reply_arg = nt_errstr(nt_status);
1704 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1705 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1706 struct auth_session_info *session_info;
1708 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1709 if (!NT_STATUS_IS_OK(nt_status)) {
1710 reply_code = "BH Failed to retrive session info";
1711 reply_arg = nt_errstr(nt_status);
1712 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1713 } else {
1715 reply_code = "AF";
1716 reply_arg = session_info->unix_info->unix_name;
1717 talloc_free(session_info);
1719 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1720 reply_code = "AF";
1721 reply_arg = out_base64;
1722 } else {
1723 abort();
1726 switch (stdio_helper_mode) {
1727 case GSS_SPNEGO_SERVER:
1728 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1729 out_base64 ? out_base64 : "*",
1730 reply_arg ? reply_arg : "*");
1731 break;
1732 default:
1733 if (out_base64) {
1734 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1735 } else if (reply_arg) {
1736 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1737 } else {
1738 x_fprintf(x_stdout, "%s\n", reply_code);
1742 talloc_free(mem_ctx);
1743 return;
1746 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1747 struct loadparm_context *lp_ctx,
1748 struct ntlm_auth_state *state,
1749 char *buf, int length, void **private2)
1751 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1752 return;
1755 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1756 struct loadparm_context *lp_ctx,
1757 struct ntlm_auth_state *state,
1758 char *buf, int length, void **private2)
1760 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1761 return;
1764 static struct ntlmssp_state *client_ntlmssp_state = NULL;
1766 static bool manage_client_ntlmssp_init(struct spnego_data spnego)
1768 NTSTATUS status;
1769 DATA_BLOB null_blob = data_blob_null;
1770 DATA_BLOB to_server;
1771 char *to_server_base64;
1772 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1773 TALLOC_CTX *ctx = talloc_tos();
1775 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1777 if (client_ntlmssp_state != NULL) {
1778 DEBUG(1, ("Request for initial SPNEGO request where "
1779 "we already have a state\n"));
1780 return False;
1783 if (!client_ntlmssp_state) {
1784 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1785 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1786 return False;
1791 if (opt_password == NULL) {
1793 /* Request a password from the calling process. After
1794 sending it, the calling process should retry with
1795 the negTokenInit. */
1797 DEBUG(10, ("Requesting password\n"));
1798 x_fprintf(x_stdout, "PW\n");
1799 return True;
1802 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1803 spnego.negTokenInit.mechTypes = my_mechs;
1804 spnego.negTokenInit.reqFlags = data_blob_null;
1805 spnego.negTokenInit.reqFlagsPadding = 0;
1806 spnego.negTokenInit.mechListMIC = null_blob;
1808 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1809 &spnego.negTokenInit.mechToken);
1811 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1812 NT_STATUS_IS_OK(status)) ) {
1813 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1814 nt_errstr(status)));
1815 TALLOC_FREE(client_ntlmssp_state);
1816 return False;
1819 spnego_write_data(ctx, &to_server, &spnego);
1820 data_blob_free(&spnego.negTokenInit.mechToken);
1822 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1823 data_blob_free(&to_server);
1824 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1825 TALLOC_FREE(to_server_base64);
1826 return True;
1829 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
1831 NTSTATUS status;
1832 DATA_BLOB null_blob = data_blob_null;
1833 DATA_BLOB request;
1834 DATA_BLOB to_server;
1835 char *to_server_base64;
1836 TALLOC_CTX *ctx = talloc_tos();
1838 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1840 if (client_ntlmssp_state == NULL) {
1841 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1842 x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
1843 return;
1846 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1847 x_fprintf(x_stdout, "NA\n");
1848 TALLOC_FREE(client_ntlmssp_state);
1849 return;
1852 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1853 x_fprintf(x_stdout, "AF\n");
1854 TALLOC_FREE(client_ntlmssp_state);
1855 return;
1858 status = ntlmssp_update(client_ntlmssp_state,
1859 spnego.negTokenTarg.responseToken,
1860 &request);
1862 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
1863 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED or OK from "
1864 "ntlmssp_client_update, got: %s\n",
1865 nt_errstr(status)));
1866 x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
1867 "ntlmssp_client_update\n");
1868 data_blob_free(&request);
1869 TALLOC_FREE(client_ntlmssp_state);
1870 return;
1873 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1874 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1875 spnego.negTokenTarg.supportedMech = (const char *)OID_NTLMSSP;
1876 spnego.negTokenTarg.responseToken = request;
1877 spnego.negTokenTarg.mechListMIC = null_blob;
1879 spnego_write_data(ctx, &to_server, &spnego);
1880 data_blob_free(&request);
1882 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1883 data_blob_free(&to_server);
1884 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1885 TALLOC_FREE(to_server_base64);
1886 return;
1889 #ifdef HAVE_KRB5
1891 static bool manage_client_krb5_init(struct spnego_data spnego)
1893 char *principal;
1894 DATA_BLOB tkt, tkt_wrapped, to_server;
1895 DATA_BLOB session_key_krb5 = data_blob_null;
1896 struct spnego_data reply;
1897 char *reply_base64;
1898 int retval;
1900 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1901 ssize_t len;
1902 TALLOC_CTX *ctx = talloc_tos();
1904 principal = spnego.negTokenInit.targetPrincipal;
1906 /* We may not be allowed to use the server-supplied SPNEGO principal, or it may not have been supplied to us
1908 if (!lp_client_use_spnego_principal() || strequal(principal, ADS_IGNORE_PRINCIPAL)) {
1909 principal = NULL;
1912 if (principal == NULL &&
1913 opt_target_service && opt_target_hostname && !is_ipaddress(opt_target_hostname)) {
1914 DEBUG(3,("manage_client_krb5_init: using target "
1915 "hostname not SPNEGO principal\n"));
1917 principal = kerberos_get_principal_from_service_hostname(talloc_tos(),
1918 opt_target_service,
1919 opt_target_hostname,
1920 lp_realm());
1922 if (!principal) {
1923 return false;
1926 DEBUG(3,("manage_client_krb5_init: guessed "
1927 "server principal=%s\n",
1928 principal ? principal : "<null>"));
1931 if (principal == NULL) {
1932 DEBUG(3,("manage_client_krb5_init: could not guess server principal\n"));
1933 return false;
1936 retval = cli_krb5_get_ticket(ctx, principal, 0,
1937 &tkt, &session_key_krb5,
1938 0, NULL, NULL, NULL);
1939 if (retval) {
1940 char *user = NULL;
1942 /* Let's try to first get the TGT, for that we need a
1943 password. */
1945 if (opt_password == NULL) {
1946 DEBUG(10, ("Requesting password\n"));
1947 x_fprintf(x_stdout, "PW\n");
1948 return True;
1951 user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
1952 if (!user) {
1953 return false;
1956 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1957 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1958 return False;
1961 retval = cli_krb5_get_ticket(ctx, principal, 0,
1962 &tkt, &session_key_krb5,
1963 0, NULL, NULL, NULL);
1964 if (retval) {
1965 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1966 return False;
1971 /* wrap that up in a nice GSS-API wrapping */
1972 tkt_wrapped = spnego_gen_krb5_wrap(ctx, tkt, TOK_ID_KRB_AP_REQ);
1974 data_blob_free(&session_key_krb5);
1976 ZERO_STRUCT(reply);
1978 reply.type = SPNEGO_NEG_TOKEN_INIT;
1979 reply.negTokenInit.mechTypes = my_mechs;
1980 reply.negTokenInit.reqFlags = data_blob_null;
1981 reply.negTokenInit.reqFlagsPadding = 0;
1982 reply.negTokenInit.mechToken = tkt_wrapped;
1983 reply.negTokenInit.mechListMIC = data_blob_null;
1985 len = spnego_write_data(ctx, &to_server, &reply);
1986 data_blob_free(&tkt);
1988 if (len == -1) {
1989 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1990 return False;
1993 reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1994 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1996 TALLOC_FREE(reply_base64);
1997 data_blob_free(&to_server);
1998 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1999 return True;
2002 static void manage_client_krb5_targ(struct spnego_data spnego)
2004 switch (spnego.negTokenTarg.negResult) {
2005 case SPNEGO_ACCEPT_INCOMPLETE:
2006 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
2007 x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
2008 "ACCEPT_INCOMPLETE\n");
2009 break;
2010 case SPNEGO_ACCEPT_COMPLETED:
2011 DEBUG(10, ("Accept completed\n"));
2012 x_fprintf(x_stdout, "AF\n");
2013 break;
2014 case SPNEGO_REJECT:
2015 DEBUG(10, ("Rejected\n"));
2016 x_fprintf(x_stdout, "NA\n");
2017 break;
2018 default:
2019 DEBUG(1, ("Got an invalid negTokenTarg\n"));
2020 x_fprintf(x_stdout, "AF\n");
2024 #endif
2026 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
2027 struct loadparm_context *lp_ctx,
2028 struct ntlm_auth_state *state,
2029 char *buf, int length, void **private2)
2031 DATA_BLOB request;
2032 struct spnego_data spnego;
2033 ssize_t len;
2034 TALLOC_CTX *ctx = talloc_tos();
2036 if (!opt_username || !*opt_username) {
2037 x_fprintf(x_stderr, "username must be specified!\n\n");
2038 exit(1);
2041 if (strlen(buf) <= 3) {
2042 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
2043 x_fprintf(x_stdout, "BH SPNEGO query too short\n");
2044 return;
2047 request = base64_decode_data_blob(buf+3);
2049 if (strncmp(buf, "PW ", 3) == 0) {
2051 /* We asked for a password and obviously got it :-) */
2053 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
2055 if (opt_password == NULL) {
2056 DEBUG(1, ("Out of memory\n"));
2057 x_fprintf(x_stdout, "BH Out of memory\n");
2058 data_blob_free(&request);
2059 return;
2062 x_fprintf(x_stdout, "OK\n");
2063 data_blob_free(&request);
2064 return;
2067 if ( (strncmp(buf, "TT ", 3) != 0) &&
2068 (strncmp(buf, "AF ", 3) != 0) &&
2069 (strncmp(buf, "NA ", 3) != 0) ) {
2070 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
2071 x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
2072 data_blob_free(&request);
2073 return;
2076 /* So we got a server challenge to generate a SPNEGO
2077 client-to-server request... */
2079 len = spnego_read_data(ctx, request, &spnego);
2080 data_blob_free(&request);
2082 if (len == -1) {
2083 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
2084 x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
2085 return;
2088 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
2090 /* The server offers a list of mechanisms */
2092 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
2094 while (*mechType != NULL) {
2096 #ifdef HAVE_KRB5
2097 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
2098 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
2099 if (manage_client_krb5_init(spnego))
2100 goto out;
2102 #endif
2104 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
2105 if (manage_client_ntlmssp_init(spnego))
2106 goto out;
2109 mechType++;
2112 DEBUG(1, ("Server offered no compatible mechanism\n"));
2113 x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
2114 return;
2117 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
2119 if (spnego.negTokenTarg.supportedMech == NULL) {
2120 /* On accept/reject Windows does not send the
2121 mechanism anymore. Handle that here and
2122 shut down the mechanisms. */
2124 switch (spnego.negTokenTarg.negResult) {
2125 case SPNEGO_ACCEPT_COMPLETED:
2126 x_fprintf(x_stdout, "AF\n");
2127 break;
2128 case SPNEGO_REJECT:
2129 x_fprintf(x_stdout, "NA\n");
2130 break;
2131 default:
2132 DEBUG(1, ("Got a negTokenTarg with no mech and an "
2133 "unknown negResult: %d\n",
2134 spnego.negTokenTarg.negResult));
2135 x_fprintf(x_stdout, "BH Got a negTokenTarg with"
2136 " no mech and an unknown "
2137 "negResult\n");
2140 TALLOC_FREE(client_ntlmssp_state);
2141 goto out;
2144 if (strcmp(spnego.negTokenTarg.supportedMech,
2145 OID_NTLMSSP) == 0) {
2146 manage_client_ntlmssp_targ(spnego);
2147 goto out;
2150 #if HAVE_KRB5
2151 if (strcmp(spnego.negTokenTarg.supportedMech,
2152 OID_KERBEROS5_OLD) == 0) {
2153 manage_client_krb5_targ(spnego);
2154 goto out;
2156 #endif
2160 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
2161 x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
2162 return;
2164 out:
2165 spnego_free_data(&spnego);
2166 return;
2169 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
2170 struct loadparm_context *lp_ctx,
2171 struct ntlm_auth_state *state,
2172 char *buf, int length, void **private2)
2174 char *request, *parameter;
2175 static DATA_BLOB challenge;
2176 static DATA_BLOB lm_response;
2177 static DATA_BLOB nt_response;
2178 static char *full_username;
2179 static char *username;
2180 static char *domain;
2181 static char *plaintext_password;
2182 static bool ntlm_server_1_user_session_key;
2183 static bool ntlm_server_1_lm_session_key;
2185 if (strequal(buf, ".")) {
2186 if (!full_username && !username) {
2187 x_fprintf(x_stdout, "Error: No username supplied!\n");
2188 } else if (plaintext_password) {
2189 /* handle this request as plaintext */
2190 if (!full_username) {
2191 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
2192 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
2193 return;
2196 if (check_plaintext_auth(full_username, plaintext_password, False)) {
2197 x_fprintf(x_stdout, "Authenticated: Yes\n");
2198 } else {
2199 x_fprintf(x_stdout, "Authenticated: No\n");
2201 } else if (!lm_response.data && !nt_response.data) {
2202 x_fprintf(x_stdout, "Error: No password supplied!\n");
2203 } else if (!challenge.data) {
2204 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
2205 } else {
2206 char *error_string = NULL;
2207 uchar lm_key[8];
2208 uchar user_session_key[16];
2209 uint32 flags = 0;
2211 if (full_username && !username) {
2212 fstring fstr_user;
2213 fstring fstr_domain;
2215 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
2216 /* username might be 'tainted', don't print into our new-line deleimianted stream */
2217 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
2219 SAFE_FREE(username);
2220 SAFE_FREE(domain);
2221 username = smb_xstrdup(fstr_user);
2222 domain = smb_xstrdup(fstr_domain);
2225 if (!domain) {
2226 domain = smb_xstrdup(get_winbind_domain());
2229 if (ntlm_server_1_lm_session_key)
2230 flags |= WBFLAG_PAM_LMKEY;
2232 if (ntlm_server_1_user_session_key)
2233 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2235 if (!NT_STATUS_IS_OK(
2236 contact_winbind_auth_crap(username,
2237 domain,
2238 lp_netbios_name(),
2239 &challenge,
2240 &lm_response,
2241 &nt_response,
2242 flags, 0,
2243 lm_key,
2244 user_session_key,
2245 &error_string,
2246 NULL))) {
2248 x_fprintf(x_stdout, "Authenticated: No\n");
2249 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
2250 } else {
2251 static char zeros[16];
2252 char *hex_lm_key;
2253 char *hex_user_session_key;
2255 x_fprintf(x_stdout, "Authenticated: Yes\n");
2257 if (ntlm_server_1_lm_session_key
2258 && (memcmp(zeros, lm_key,
2259 sizeof(lm_key)) != 0)) {
2260 hex_lm_key = hex_encode_talloc(NULL,
2261 (const unsigned char *)lm_key,
2262 sizeof(lm_key));
2263 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
2264 TALLOC_FREE(hex_lm_key);
2267 if (ntlm_server_1_user_session_key
2268 && (memcmp(zeros, user_session_key,
2269 sizeof(user_session_key)) != 0)) {
2270 hex_user_session_key = hex_encode_talloc(NULL,
2271 (const unsigned char *)user_session_key,
2272 sizeof(user_session_key));
2273 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
2274 TALLOC_FREE(hex_user_session_key);
2277 SAFE_FREE(error_string);
2279 /* clear out the state */
2280 challenge = data_blob_null;
2281 nt_response = data_blob_null;
2282 lm_response = data_blob_null;
2283 SAFE_FREE(full_username);
2284 SAFE_FREE(username);
2285 SAFE_FREE(domain);
2286 SAFE_FREE(plaintext_password);
2287 ntlm_server_1_user_session_key = False;
2288 ntlm_server_1_lm_session_key = False;
2289 x_fprintf(x_stdout, ".\n");
2291 return;
2294 request = buf;
2296 /* Indicates a base64 encoded structure */
2297 parameter = strstr_m(request, ":: ");
2298 if (!parameter) {
2299 parameter = strstr_m(request, ": ");
2301 if (!parameter) {
2302 DEBUG(0, ("Parameter not found!\n"));
2303 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2304 return;
2307 parameter[0] ='\0';
2308 parameter++;
2309 parameter[0] ='\0';
2310 parameter++;
2312 } else {
2313 parameter[0] ='\0';
2314 parameter++;
2315 parameter[0] ='\0';
2316 parameter++;
2317 parameter[0] ='\0';
2318 parameter++;
2320 base64_decode_inplace(parameter);
2323 if (strequal(request, "LANMAN-Challenge")) {
2324 challenge = strhex_to_data_blob(NULL, parameter);
2325 if (challenge.length != 8) {
2326 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
2327 parameter,
2328 (int)challenge.length);
2329 challenge = data_blob_null;
2331 } else if (strequal(request, "NT-Response")) {
2332 nt_response = strhex_to_data_blob(NULL, parameter);
2333 if (nt_response.length < 24) {
2334 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
2335 parameter,
2336 (int)nt_response.length);
2337 nt_response = data_blob_null;
2339 } else if (strequal(request, "LANMAN-Response")) {
2340 lm_response = strhex_to_data_blob(NULL, parameter);
2341 if (lm_response.length != 24) {
2342 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
2343 parameter,
2344 (int)lm_response.length);
2345 lm_response = data_blob_null;
2347 } else if (strequal(request, "Password")) {
2348 plaintext_password = smb_xstrdup(parameter);
2349 } else if (strequal(request, "NT-Domain")) {
2350 domain = smb_xstrdup(parameter);
2351 } else if (strequal(request, "Username")) {
2352 username = smb_xstrdup(parameter);
2353 } else if (strequal(request, "Full-Username")) {
2354 full_username = smb_xstrdup(parameter);
2355 } else if (strequal(request, "Request-User-Session-Key")) {
2356 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2357 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2358 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2359 } else {
2360 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2364 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2365 struct loadparm_context *lp_ctx,
2366 struct ntlm_auth_state *state,
2367 char *buf, int length, void **private2)
2369 char *request, *parameter;
2370 static DATA_BLOB new_nt_pswd;
2371 static DATA_BLOB old_nt_hash_enc;
2372 static DATA_BLOB new_lm_pswd;
2373 static DATA_BLOB old_lm_hash_enc;
2374 static char *full_username = NULL;
2375 static char *username = NULL;
2376 static char *domain = NULL;
2377 static char *newpswd = NULL;
2378 static char *oldpswd = NULL;
2380 if (strequal(buf, ".")) {
2381 if(newpswd && oldpswd) {
2382 uchar old_nt_hash[16];
2383 uchar old_lm_hash[16];
2384 uchar new_nt_hash[16];
2385 uchar new_lm_hash[16];
2387 new_nt_pswd = data_blob(NULL, 516);
2388 old_nt_hash_enc = data_blob(NULL, 16);
2390 /* Calculate the MD4 hash (NT compatible) of the
2391 * password */
2392 E_md4hash(oldpswd, old_nt_hash);
2393 E_md4hash(newpswd, new_nt_hash);
2395 /* E_deshash returns false for 'long'
2396 passwords (> 14 DOS chars).
2398 Therefore, don't send a buffer
2399 encrypted with the truncated hash
2400 (it could allow an even easier
2401 attack on the password)
2403 Likewise, obey the admin's restriction
2406 if (lp_client_lanman_auth() &&
2407 E_deshash(newpswd, new_lm_hash) &&
2408 E_deshash(oldpswd, old_lm_hash)) {
2409 new_lm_pswd = data_blob(NULL, 516);
2410 old_lm_hash_enc = data_blob(NULL, 16);
2411 encode_pw_buffer(new_lm_pswd.data, newpswd,
2412 STR_UNICODE);
2414 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
2415 E_old_pw_hash(new_nt_hash, old_lm_hash,
2416 old_lm_hash_enc.data);
2417 } else {
2418 new_lm_pswd.data = NULL;
2419 new_lm_pswd.length = 0;
2420 old_lm_hash_enc.data = NULL;
2421 old_lm_hash_enc.length = 0;
2424 encode_pw_buffer(new_nt_pswd.data, newpswd,
2425 STR_UNICODE);
2427 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
2428 E_old_pw_hash(new_nt_hash, old_nt_hash,
2429 old_nt_hash_enc.data);
2432 if (!full_username && !username) {
2433 x_fprintf(x_stdout, "Error: No username supplied!\n");
2434 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2435 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2436 x_fprintf(x_stdout, "Error: No NT or LM password "
2437 "blobs supplied!\n");
2438 } else {
2439 char *error_string = NULL;
2441 if (full_username && !username) {
2442 fstring fstr_user;
2443 fstring fstr_domain;
2445 if (!parse_ntlm_auth_domain_user(full_username,
2446 fstr_user,
2447 fstr_domain)) {
2448 /* username might be 'tainted', don't
2449 * print into our new-line
2450 * deleimianted stream */
2451 x_fprintf(x_stdout, "Error: Could not "
2452 "parse into domain and "
2453 "username\n");
2454 SAFE_FREE(username);
2455 username = smb_xstrdup(full_username);
2456 } else {
2457 SAFE_FREE(username);
2458 SAFE_FREE(domain);
2459 username = smb_xstrdup(fstr_user);
2460 domain = smb_xstrdup(fstr_domain);
2465 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2466 username, domain,
2467 new_nt_pswd,
2468 old_nt_hash_enc,
2469 new_lm_pswd,
2470 old_lm_hash_enc,
2471 &error_string))) {
2472 x_fprintf(x_stdout, "Password-Change: No\n");
2473 x_fprintf(x_stdout, "Password-Change-Error: "
2474 "%s\n.\n", error_string);
2475 } else {
2476 x_fprintf(x_stdout, "Password-Change: Yes\n");
2479 SAFE_FREE(error_string);
2481 /* clear out the state */
2482 new_nt_pswd = data_blob_null;
2483 old_nt_hash_enc = data_blob_null;
2484 new_lm_pswd = data_blob_null;
2485 old_nt_hash_enc = data_blob_null;
2486 SAFE_FREE(full_username);
2487 SAFE_FREE(username);
2488 SAFE_FREE(domain);
2489 SAFE_FREE(newpswd);
2490 SAFE_FREE(oldpswd);
2491 x_fprintf(x_stdout, ".\n");
2493 return;
2496 request = buf;
2498 /* Indicates a base64 encoded structure */
2499 parameter = strstr_m(request, ":: ");
2500 if (!parameter) {
2501 parameter = strstr_m(request, ": ");
2503 if (!parameter) {
2504 DEBUG(0, ("Parameter not found!\n"));
2505 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2506 return;
2509 parameter[0] ='\0';
2510 parameter++;
2511 parameter[0] ='\0';
2512 parameter++;
2513 } else {
2514 parameter[0] ='\0';
2515 parameter++;
2516 parameter[0] ='\0';
2517 parameter++;
2518 parameter[0] ='\0';
2519 parameter++;
2521 base64_decode_inplace(parameter);
2524 if (strequal(request, "new-nt-password-blob")) {
2525 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2526 if (new_nt_pswd.length != 516) {
2527 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2528 "(got %d bytes, expected 516)\n.\n",
2529 parameter,
2530 (int)new_nt_pswd.length);
2531 new_nt_pswd = data_blob_null;
2533 } else if (strequal(request, "old-nt-hash-blob")) {
2534 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2535 if (old_nt_hash_enc.length != 16) {
2536 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2537 "(got %d bytes, expected 16)\n.\n",
2538 parameter,
2539 (int)old_nt_hash_enc.length);
2540 old_nt_hash_enc = data_blob_null;
2542 } else if (strequal(request, "new-lm-password-blob")) {
2543 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2544 if (new_lm_pswd.length != 516) {
2545 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2546 "(got %d bytes, expected 516)\n.\n",
2547 parameter,
2548 (int)new_lm_pswd.length);
2549 new_lm_pswd = data_blob_null;
2552 else if (strequal(request, "old-lm-hash-blob")) {
2553 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2554 if (old_lm_hash_enc.length != 16)
2556 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2557 "(got %d bytes, expected 16)\n.\n",
2558 parameter,
2559 (int)old_lm_hash_enc.length);
2560 old_lm_hash_enc = data_blob_null;
2562 } else if (strequal(request, "nt-domain")) {
2563 domain = smb_xstrdup(parameter);
2564 } else if(strequal(request, "username")) {
2565 username = smb_xstrdup(parameter);
2566 } else if(strequal(request, "full-username")) {
2567 username = smb_xstrdup(parameter);
2568 } else if(strequal(request, "new-password")) {
2569 newpswd = smb_xstrdup(parameter);
2570 } else if (strequal(request, "old-password")) {
2571 oldpswd = smb_xstrdup(parameter);
2572 } else {
2573 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2577 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2578 struct loadparm_context *lp_ctx,
2579 struct ntlm_auth_state *state,
2580 stdio_helper_function fn, void **private2)
2582 char *buf;
2583 char tmp[INITIAL_BUFFER_SIZE+1];
2584 int length, buf_size = 0;
2585 char *c;
2587 buf = talloc_strdup(state->mem_ctx, "");
2588 if (!buf) {
2589 DEBUG(0, ("Failed to allocate input buffer.\n"));
2590 x_fprintf(x_stderr, "ERR\n");
2591 exit(1);
2594 do {
2596 /* this is not a typo - x_fgets doesn't work too well under
2597 * squid */
2598 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2599 if (ferror(stdin)) {
2600 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2601 "(%s)\n", ferror(stdin),
2602 strerror(ferror(stdin))));
2604 exit(1);
2606 exit(0);
2609 buf = talloc_strdup_append_buffer(buf, tmp);
2610 buf_size += INITIAL_BUFFER_SIZE;
2612 if (buf_size > MAX_BUFFER_SIZE) {
2613 DEBUG(2, ("Oversized message\n"));
2614 x_fprintf(x_stderr, "ERR\n");
2615 talloc_free(buf);
2616 return;
2619 c = strchr(buf, '\n');
2620 } while (c == NULL);
2622 *c = '\0';
2623 length = c-buf;
2625 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2627 if (buf[0] == '\0') {
2628 DEBUG(2, ("Invalid Request\n"));
2629 x_fprintf(x_stderr, "ERR\n");
2630 talloc_free(buf);
2631 return;
2634 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2635 talloc_free(buf);
2639 static void squid_stream(enum stdio_helper_mode stdio_mode,
2640 struct loadparm_context *lp_ctx,
2641 stdio_helper_function fn) {
2642 TALLOC_CTX *mem_ctx;
2643 struct ntlm_auth_state *state;
2645 /* initialize FDescs */
2646 x_setbuf(x_stdout, NULL);
2647 x_setbuf(x_stderr, NULL);
2649 mem_ctx = talloc_init("ntlm_auth");
2650 if (!mem_ctx) {
2651 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2652 x_fprintf(x_stderr, "ERR\n");
2653 exit(1);
2656 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2657 if (!state) {
2658 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2659 x_fprintf(x_stderr, "ERR\n");
2660 exit(1);
2663 state->mem_ctx = mem_ctx;
2664 state->helper_mode = stdio_mode;
2666 while(1) {
2667 TALLOC_CTX *frame = talloc_stackframe();
2668 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2669 TALLOC_FREE(frame);
2674 /* Authenticate a user with a challenge/response */
2676 static bool check_auth_crap(void)
2678 NTSTATUS nt_status;
2679 uint32 flags = 0;
2680 char lm_key[8];
2681 char user_session_key[16];
2682 char *hex_lm_key;
2683 char *hex_user_session_key;
2684 char *error_string;
2685 static uint8 zeros[16];
2687 x_setbuf(x_stdout, NULL);
2689 if (request_lm_key)
2690 flags |= WBFLAG_PAM_LMKEY;
2692 if (request_user_session_key)
2693 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2695 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2697 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2698 opt_workstation,
2699 &opt_challenge,
2700 &opt_lm_response,
2701 &opt_nt_response,
2702 flags, 0,
2703 (unsigned char *)lm_key,
2704 (unsigned char *)user_session_key,
2705 &error_string, NULL);
2707 if (!NT_STATUS_IS_OK(nt_status)) {
2708 x_fprintf(x_stdout, "%s (0x%x)\n",
2709 error_string,
2710 NT_STATUS_V(nt_status));
2711 SAFE_FREE(error_string);
2712 return False;
2715 if (request_lm_key
2716 && (memcmp(zeros, lm_key,
2717 sizeof(lm_key)) != 0)) {
2718 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2719 sizeof(lm_key));
2720 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2721 TALLOC_FREE(hex_lm_key);
2723 if (request_user_session_key
2724 && (memcmp(zeros, user_session_key,
2725 sizeof(user_session_key)) != 0)) {
2726 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2727 sizeof(user_session_key));
2728 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2729 TALLOC_FREE(hex_user_session_key);
2732 return True;
2735 /* Main program */
2737 enum {
2738 OPT_USERNAME = 1000,
2739 OPT_DOMAIN,
2740 OPT_WORKSTATION,
2741 OPT_CHALLENGE,
2742 OPT_RESPONSE,
2743 OPT_LM,
2744 OPT_NT,
2745 OPT_PASSWORD,
2746 OPT_LM_KEY,
2747 OPT_USER_SESSION_KEY,
2748 OPT_DIAGNOSTICS,
2749 OPT_REQUIRE_MEMBERSHIP,
2750 OPT_USE_CACHED_CREDS,
2751 OPT_PAM_WINBIND_CONF,
2752 OPT_TARGET_SERVICE,
2753 OPT_TARGET_HOSTNAME
2756 int main(int argc, const char **argv)
2758 TALLOC_CTX *frame = talloc_stackframe();
2759 int opt;
2760 static const char *helper_protocol;
2761 static int diagnostics;
2763 static const char *hex_challenge;
2764 static const char *hex_lm_response;
2765 static const char *hex_nt_response;
2766 struct loadparm_context *lp_ctx;
2767 poptContext pc;
2769 /* NOTE: DO NOT change this interface without considering the implications!
2770 This is an external interface, which other programs will use to interact
2771 with this helper.
2774 /* We do not use single-letter command abbreviations, because they harm future
2775 interface stability. */
2777 struct poptOption long_options[] = {
2778 POPT_AUTOHELP
2779 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2780 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2781 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2782 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2783 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2784 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2785 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2786 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2787 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2788 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2789 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2790 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2791 OPT_DIAGNOSTICS,
2792 "Perform diagnostics on the authentication chain"},
2793 { "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" },
2794 { "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" },
2795 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2796 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2797 POPT_COMMON_CONFIGFILE
2798 POPT_COMMON_VERSION
2799 POPT_TABLEEND
2802 /* Samba client initialisation */
2803 load_case_tables();
2805 setup_logging("ntlm_auth", DEBUG_STDERR);
2807 /* Parse options */
2809 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2811 /* Parse command line options */
2813 if (argc == 1) {
2814 poptPrintHelp(pc, stderr, 0);
2815 return 1;
2818 while((opt = poptGetNextOpt(pc)) != -1) {
2819 /* Get generic config options like --configfile */
2822 poptFreeContext(pc);
2824 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2825 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2826 get_dyn_CONFIGFILE(), strerror(errno));
2827 exit(1);
2830 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2831 POPT_CONTEXT_KEEP_FIRST);
2833 while((opt = poptGetNextOpt(pc)) != -1) {
2834 switch (opt) {
2835 case OPT_CHALLENGE:
2836 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2837 if (opt_challenge.length != 8) {
2838 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2839 hex_challenge,
2840 (int)opt_challenge.length);
2841 exit(1);
2843 break;
2844 case OPT_LM:
2845 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2846 if (opt_lm_response.length != 24) {
2847 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2848 hex_lm_response,
2849 (int)opt_lm_response.length);
2850 exit(1);
2852 break;
2854 case OPT_NT:
2855 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2856 if (opt_nt_response.length < 24) {
2857 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2858 hex_nt_response,
2859 (int)opt_nt_response.length);
2860 exit(1);
2862 break;
2864 case OPT_REQUIRE_MEMBERSHIP:
2865 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2866 require_membership_of_sid = require_membership_of;
2868 break;
2872 if (opt_username) {
2873 char *domain = SMB_STRDUP(opt_username);
2874 char *p = strchr_m(domain, *lp_winbind_separator());
2875 if (p) {
2876 opt_username = p+1;
2877 *p = '\0';
2878 if (opt_domain && !strequal(opt_domain, domain)) {
2879 x_fprintf(x_stderr, "Domain specified in username (%s) "
2880 "doesn't match specified domain (%s)!\n\n",
2881 domain, opt_domain);
2882 poptPrintHelp(pc, stderr, 0);
2883 exit(1);
2885 opt_domain = domain;
2886 } else {
2887 SAFE_FREE(domain);
2891 /* Note: if opt_domain is "" then send no domain */
2892 if (opt_domain == NULL) {
2893 opt_domain = get_winbind_domain();
2896 if (opt_workstation == NULL) {
2897 opt_workstation = "";
2900 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_context());
2901 if (lp_ctx == NULL) {
2902 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2903 exit(1);
2906 if (helper_protocol) {
2907 int i;
2908 for (i=0; i<NUM_HELPER_MODES; i++) {
2909 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2910 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2911 exit(0);
2914 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2916 for (i=0; i<NUM_HELPER_MODES; i++) {
2917 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2920 exit(1);
2923 if (!opt_username || !*opt_username) {
2924 x_fprintf(x_stderr, "username must be specified!\n\n");
2925 poptPrintHelp(pc, stderr, 0);
2926 exit(1);
2929 if (opt_challenge.length) {
2930 if (!check_auth_crap()) {
2931 exit(1);
2933 exit(0);
2936 if (!opt_password) {
2937 opt_password = getpass("password: ");
2940 if (diagnostics) {
2941 if (!diagnose_ntlm_auth()) {
2942 return 1;
2944 } else {
2945 fstring user;
2947 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2948 if (!check_plaintext_auth(user, opt_password, True)) {
2949 return 1;
2953 /* Exit code */
2955 poptFreeContext(pc);
2956 TALLOC_FREE(frame);
2957 return 0;