s3:ntlm_auth: don't start gensec backend twice
[Samba.git] / source3 / utils / ntlm_auth.c
bloba5fd249784e6054f002ea3644f62434ff66ea24e
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(NULL, 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(NULL, 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(NULL, 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(NULL, 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(NULL, 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_t flags,
502 uint32_t extra_logon_parameters,
503 uint8_t lm_key[8],
504 uint8_t 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(NULL, 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(NULL, 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_t lm_key[8];
883 uint8_t 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_prepare_gensec_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_server_role() == ROLE_ACTIVE_DIRECTORY_DC || 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 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1139 TALLOC_FREE(tmp_ctx);
1140 return NT_STATUS_OK;
1143 /*******************************************************************
1144 Used by firefox to drive NTLM auth to IIS servers.
1145 *******************************************************************/
1147 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
1148 DATA_BLOB *reply)
1150 struct winbindd_request wb_request;
1151 struct winbindd_response wb_response;
1152 int ctrl = 0;
1153 NSS_STATUS result;
1155 /* get winbindd to do the ntlmssp step on our behalf */
1156 ZERO_STRUCT(wb_request);
1157 ZERO_STRUCT(wb_response);
1160 * This is tricky here. If we set krb5_auth in pam_winbind.conf
1161 * creds for users in trusted domain will be stored the winbindd
1162 * child of the trusted domain. If we ask the primary domain for
1163 * ntlm_ccache_auth, it will fail. So, we have to ask the trusted
1164 * domain's child for ccache_ntlm_auth. that is to say, we have to
1165 * set WBFLAG_PAM_CONTACT_TRUSTDOM in request.flags.
1167 ctrl = get_pam_winbind_config();
1169 if (ctrl & WINBIND_KRB5_AUTH) {
1170 wb_request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1173 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
1174 "%s%c%s", opt_domain, winbind_separator(), opt_username);
1175 wb_request.data.ccache_ntlm_auth.uid = geteuid();
1176 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
1177 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
1178 wb_request.extra_len = initial_msg.length + challenge_msg.length;
1180 if (wb_request.extra_len > 0) {
1181 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
1182 if (wb_request.extra_data.data == NULL) {
1183 return NT_STATUS_NO_MEMORY;
1186 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
1187 memcpy(wb_request.extra_data.data + initial_msg.length,
1188 challenge_msg.data, challenge_msg.length);
1191 result = winbindd_request_response(NULL, WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
1192 SAFE_FREE(wb_request.extra_data.data);
1194 if (result != NSS_STATUS_SUCCESS) {
1195 winbindd_free_response(&wb_response);
1196 return NT_STATUS_UNSUCCESSFUL;
1199 if (reply) {
1200 *reply = data_blob(wb_response.extra_data.data,
1201 wb_response.data.ccache_ntlm_auth.auth_blob_len);
1202 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
1203 reply->data == NULL) {
1204 winbindd_free_response(&wb_response);
1205 return NT_STATUS_NO_MEMORY;
1209 winbindd_free_response(&wb_response);
1210 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1213 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1214 struct loadparm_context *lp_ctx,
1215 struct ntlm_auth_state *state,
1216 char *buf, int length, void **private2)
1218 DATA_BLOB request, reply;
1219 NTSTATUS nt_status;
1221 if (!opt_username || !*opt_username) {
1222 x_fprintf(x_stderr, "username must be specified!\n\n");
1223 exit(1);
1226 if (strlen(buf) < 2) {
1227 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1228 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1229 return;
1232 if (strlen(buf) > 3) {
1233 if(strncmp(buf, "SF ", 3) == 0) {
1234 DEBUG(10, ("Looking for flags to negotiate\n"));
1235 talloc_free(state->want_feature_list);
1236 state->want_feature_list = talloc_strdup(state->mem_ctx,
1237 buf+3);
1238 x_fprintf(x_stdout, "OK\n");
1239 return;
1241 request = base64_decode_data_blob(buf + 3);
1242 } else {
1243 request = data_blob_null;
1246 if (strncmp(buf, "PW ", 3) == 0) {
1247 /* We asked for a password and obviously got it :-) */
1249 opt_password = SMB_STRNDUP((const char *)request.data,
1250 request.length);
1252 if (opt_password == NULL) {
1253 DEBUG(1, ("Out of memory\n"));
1254 x_fprintf(x_stdout, "BH Out of memory\n");
1255 data_blob_free(&request);
1256 return;
1259 x_fprintf(x_stdout, "OK\n");
1260 data_blob_free(&request);
1261 return;
1264 if (!state->ntlmssp_state && use_cached_creds) {
1265 /* check whether cached credentials are usable. */
1266 DATA_BLOB empty_blob = data_blob_null;
1268 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
1269 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1270 /* failed to use cached creds */
1271 use_cached_creds = False;
1275 if (opt_password == NULL && !use_cached_creds) {
1276 /* Request a password from the calling process. After
1277 sending it, the calling process should retry asking for the
1278 negotiate. */
1280 DEBUG(10, ("Requesting password\n"));
1281 x_fprintf(x_stdout, "PW\n");
1282 return;
1285 if (strncmp(buf, "YR", 2) == 0) {
1286 TALLOC_FREE(state->ntlmssp_state);
1287 state->cli_state = CLIENT_INITIAL;
1288 } else if (strncmp(buf, "TT", 2) == 0) {
1289 /* No special preprocessing required */
1290 } else if (strncmp(buf, "GF", 2) == 0) {
1291 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
1293 if(state->cli_state == CLIENT_FINISHED) {
1294 x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
1296 else {
1297 x_fprintf(x_stdout, "BH\n");
1300 data_blob_free(&request);
1301 return;
1302 } else if (strncmp(buf, "GK", 2) == 0 ) {
1303 DEBUG(10, ("Requested session key\n"));
1305 if(state->cli_state == CLIENT_FINISHED) {
1306 char *key64 = base64_encode_data_blob(state->mem_ctx,
1307 state->session_key);
1308 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
1309 TALLOC_FREE(key64);
1311 else {
1312 x_fprintf(x_stdout, "BH\n");
1315 data_blob_free(&request);
1316 return;
1317 } else {
1318 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1319 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1320 return;
1323 if (!state->ntlmssp_state) {
1324 nt_status = ntlm_auth_start_ntlmssp_client(
1325 &state->ntlmssp_state);
1326 if (!NT_STATUS_IS_OK(nt_status)) {
1327 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1328 return;
1330 ntlmssp_want_feature_list(state->ntlmssp_state,
1331 state->want_feature_list);
1332 state->initial_message = data_blob_null;
1335 DEBUG(10, ("got NTLMSSP packet:\n"));
1336 dump_data(10, request.data, request.length);
1338 if (use_cached_creds && !opt_password &&
1339 (state->cli_state == CLIENT_RESPONSE)) {
1340 nt_status = do_ccache_ntlm_auth(state->initial_message, request,
1341 &reply);
1342 } else {
1343 nt_status = ntlmssp_update(state->ntlmssp_state, request,
1344 &reply);
1347 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1348 char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
1349 reply);
1350 if (state->cli_state == CLIENT_INITIAL) {
1351 x_fprintf(x_stdout, "YR %s\n", reply_base64);
1352 state->initial_message = reply;
1353 state->cli_state = CLIENT_RESPONSE;
1354 } else {
1355 x_fprintf(x_stdout, "KK %s\n", reply_base64);
1356 data_blob_free(&reply);
1358 TALLOC_FREE(reply_base64);
1359 DEBUG(10, ("NTLMSSP challenge\n"));
1360 } else if (NT_STATUS_IS_OK(nt_status)) {
1361 char *reply_base64 = base64_encode_data_blob(talloc_tos(),
1362 reply);
1363 x_fprintf(x_stdout, "AF %s\n", reply_base64);
1364 TALLOC_FREE(reply_base64);
1366 if(state->have_session_key)
1367 data_blob_free(&state->session_key);
1369 state->session_key = data_blob(
1370 state->ntlmssp_state->session_key.data,
1371 state->ntlmssp_state->session_key.length);
1372 state->neg_flags = state->ntlmssp_state->neg_flags;
1373 state->have_session_key = true;
1375 DEBUG(10, ("NTLMSSP OK!\n"));
1376 state->cli_state = CLIENT_FINISHED;
1377 TALLOC_FREE(state->ntlmssp_state);
1378 } else {
1379 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1380 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
1381 state->cli_state = CLIENT_ERROR;
1382 TALLOC_FREE(state->ntlmssp_state);
1385 data_blob_free(&request);
1388 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1389 struct loadparm_context *lp_ctx,
1390 struct ntlm_auth_state *state,
1391 char *buf, int length, void **private2)
1393 char *user, *pass;
1394 user=buf;
1396 pass=(char *)memchr(buf,' ',length);
1397 if (!pass) {
1398 DEBUG(2, ("Password not found. Denying access\n"));
1399 x_fprintf(x_stdout, "ERR\n");
1400 return;
1402 *pass='\0';
1403 pass++;
1405 if (state->helper_mode == SQUID_2_5_BASIC) {
1406 rfc1738_unescape(user);
1407 rfc1738_unescape(pass);
1410 if (check_plaintext_auth(user, pass, False)) {
1411 x_fprintf(x_stdout, "OK\n");
1412 } else {
1413 x_fprintf(x_stdout, "ERR\n");
1417 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1418 struct loadparm_context *lp_ctx,
1419 char *buf, int length, void **private1)
1421 DATA_BLOB in;
1422 DATA_BLOB out = data_blob(NULL, 0);
1423 char *out_base64 = NULL;
1424 const char *reply_arg = NULL;
1425 struct gensec_ntlm_state {
1426 struct gensec_security *gensec_state;
1427 const char *set_password;
1429 struct gensec_ntlm_state *state;
1431 NTSTATUS nt_status;
1432 bool first = false;
1433 const char *reply_code;
1434 struct cli_credentials *creds;
1436 static char *want_feature_list = NULL;
1437 static DATA_BLOB session_key;
1439 TALLOC_CTX *mem_ctx;
1441 if (*private1) {
1442 state = (struct gensec_ntlm_state *)*private1;
1443 } else {
1444 state = talloc_zero(NULL, struct gensec_ntlm_state);
1445 if (!state) {
1446 x_fprintf(x_stdout, "BH No Memory\n");
1447 exit(1);
1449 *private1 = state;
1450 if (opt_password) {
1451 state->set_password = opt_password;
1455 if (strlen(buf) < 2) {
1456 DEBUG(1, ("query [%s] invalid", buf));
1457 x_fprintf(x_stdout, "BH Query invalid\n");
1458 return;
1461 if (strlen(buf) > 3) {
1462 if(strncmp(buf, "SF ", 3) == 0) {
1463 DEBUG(10, ("Setting flags to negotiate\n"));
1464 talloc_free(want_feature_list);
1465 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1466 x_fprintf(x_stdout, "OK\n");
1467 return;
1469 in = base64_decode_data_blob(buf + 3);
1470 } else {
1471 in = data_blob(NULL, 0);
1474 if (strncmp(buf, "YR", 2) == 0) {
1475 if (state->gensec_state) {
1476 talloc_free(state->gensec_state);
1477 state->gensec_state = NULL;
1479 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1480 /* Just return BH, like ntlm_auth from Samba 3 does. */
1481 x_fprintf(x_stdout, "BH Command expected\n");
1482 data_blob_free(&in);
1483 return;
1484 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1485 (strncmp(buf, "KK ", 3) != 0) &&
1486 (strncmp(buf, "AF ", 3) != 0) &&
1487 (strncmp(buf, "NA ", 3) != 0) &&
1488 (strncmp(buf, "UG", 2) != 0) &&
1489 (strncmp(buf, "PW ", 3) != 0) &&
1490 (strncmp(buf, "GK", 2) != 0) &&
1491 (strncmp(buf, "GF", 2) != 0)) {
1492 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1493 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1494 data_blob_free(&in);
1495 return;
1498 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1500 /* setup gensec */
1501 if (!(state->gensec_state)) {
1502 switch (stdio_helper_mode) {
1503 case GSS_SPNEGO_CLIENT:
1504 case NTLMSSP_CLIENT_1:
1505 /* setup the client side */
1507 nt_status = gensec_client_start(NULL, &state->gensec_state,
1508 lpcfg_gensec_settings(NULL, lp_ctx));
1509 if (!NT_STATUS_IS_OK(nt_status)) {
1510 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1511 talloc_free(mem_ctx);
1512 return;
1515 creds = cli_credentials_init(state->gensec_state);
1516 cli_credentials_set_conf(creds, lp_ctx);
1517 if (opt_username) {
1518 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1520 if (opt_domain) {
1521 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1523 if (state->set_password) {
1524 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1525 } else {
1526 cli_credentials_set_password_callback(creds, get_password);
1528 if (opt_workstation) {
1529 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1532 gensec_set_credentials(state->gensec_state, creds);
1534 break;
1535 case GSS_SPNEGO_SERVER:
1536 case SQUID_2_5_NTLMSSP:
1538 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1539 &state->gensec_state);
1540 if (!NT_STATUS_IS_OK(nt_status)) {
1541 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1542 talloc_free(mem_ctx);
1543 return;
1545 break;
1547 default:
1548 talloc_free(mem_ctx);
1549 abort();
1552 gensec_want_feature_list(state->gensec_state, want_feature_list);
1554 switch (stdio_helper_mode) {
1555 case GSS_SPNEGO_CLIENT:
1556 case GSS_SPNEGO_SERVER:
1557 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1558 if (!in.length) {
1559 first = true;
1561 break;
1562 case NTLMSSP_CLIENT_1:
1563 if (!in.length) {
1564 first = true;
1566 /* fall through */
1567 case SQUID_2_5_NTLMSSP:
1568 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1569 break;
1570 default:
1571 talloc_free(mem_ctx);
1572 abort();
1575 if (!NT_STATUS_IS_OK(nt_status)) {
1576 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1577 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1578 talloc_free(mem_ctx);
1579 return;
1584 /* update */
1586 if (strncmp(buf, "PW ", 3) == 0) {
1587 state->set_password = talloc_strndup(state,
1588 (const char *)in.data,
1589 in.length);
1591 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1592 state->set_password,
1593 CRED_SPECIFIED);
1594 x_fprintf(x_stdout, "OK\n");
1595 data_blob_free(&in);
1596 talloc_free(mem_ctx);
1597 return;
1600 if (strncmp(buf, "GK", 2) == 0) {
1601 char *base64_key;
1602 DEBUG(10, ("Requested session key\n"));
1603 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1604 if(!NT_STATUS_IS_OK(nt_status)) {
1605 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1606 x_fprintf(x_stdout, "BH No session key\n");
1607 talloc_free(mem_ctx);
1608 return;
1609 } else {
1610 base64_key = base64_encode_data_blob(state, session_key);
1611 x_fprintf(x_stdout, "GK %s\n", base64_key);
1612 talloc_free(base64_key);
1614 talloc_free(mem_ctx);
1615 return;
1618 if (stdio_helper_mode == SQUID_2_5_NTLMSSP && strncmp(buf, "GF", 2) == 0) {
1619 uint32_t neg_flags;
1621 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1623 DEBUG(10, ("Requested negotiated feature flags\n"));
1624 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1625 return;
1628 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1630 /* don't leak 'bad password'/'no such user' info to the network client */
1631 nt_status = nt_status_squash(nt_status);
1633 if (out.length) {
1634 out_base64 = base64_encode_data_blob(mem_ctx, out);
1635 } else {
1636 out_base64 = NULL;
1639 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1640 reply_arg = "*";
1641 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1642 reply_code = "YR";
1643 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1644 reply_code = "KK";
1645 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1646 reply_code = "TT";
1647 } else {
1648 abort();
1652 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1653 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1654 reply_arg = nt_errstr(nt_status);
1655 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1656 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1657 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1658 reply_arg = nt_errstr(nt_status);
1659 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1660 } else if (!NT_STATUS_IS_OK(nt_status)) {
1661 reply_code = "NA";
1662 reply_arg = nt_errstr(nt_status);
1663 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1664 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1665 struct auth_session_info *session_info;
1667 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1668 if (!NT_STATUS_IS_OK(nt_status)) {
1669 reply_code = "BH Failed to retrive session info";
1670 reply_arg = nt_errstr(nt_status);
1671 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1672 } else {
1674 reply_code = "AF";
1675 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1676 if (reply_arg == NULL) {
1677 reply_code = "BH out of memory";
1678 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1680 talloc_free(session_info);
1682 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1683 reply_code = "AF";
1684 reply_arg = out_base64;
1685 } else {
1686 abort();
1689 switch (stdio_helper_mode) {
1690 case GSS_SPNEGO_SERVER:
1691 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1692 out_base64 ? out_base64 : "*",
1693 reply_arg ? reply_arg : "*");
1694 break;
1695 default:
1696 if (out_base64) {
1697 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1698 } else if (reply_arg) {
1699 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1700 } else {
1701 x_fprintf(x_stdout, "%s\n", reply_code);
1705 talloc_free(mem_ctx);
1706 return;
1709 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1710 struct loadparm_context *lp_ctx,
1711 struct ntlm_auth_state *state,
1712 char *buf, int length, void **private2)
1714 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1715 return;
1718 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1719 struct loadparm_context *lp_ctx,
1720 struct ntlm_auth_state *state,
1721 char *buf, int length, void **private2)
1723 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1724 return;
1727 static struct ntlmssp_state *client_ntlmssp_state = NULL;
1729 static bool manage_client_ntlmssp_init(struct spnego_data spnego)
1731 NTSTATUS status;
1732 DATA_BLOB null_blob = data_blob_null;
1733 DATA_BLOB to_server;
1734 char *to_server_base64;
1735 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1736 TALLOC_CTX *ctx = talloc_tos();
1738 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1740 if (client_ntlmssp_state != NULL) {
1741 DEBUG(1, ("Request for initial SPNEGO request where "
1742 "we already have a state\n"));
1743 return False;
1746 if (!client_ntlmssp_state) {
1747 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1748 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1749 return False;
1754 if (opt_password == NULL) {
1756 /* Request a password from the calling process. After
1757 sending it, the calling process should retry with
1758 the negTokenInit. */
1760 DEBUG(10, ("Requesting password\n"));
1761 x_fprintf(x_stdout, "PW\n");
1762 return True;
1765 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1766 spnego.negTokenInit.mechTypes = my_mechs;
1767 spnego.negTokenInit.reqFlags = data_blob_null;
1768 spnego.negTokenInit.reqFlagsPadding = 0;
1769 spnego.negTokenInit.mechListMIC = null_blob;
1771 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1772 &spnego.negTokenInit.mechToken);
1774 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1775 NT_STATUS_IS_OK(status)) ) {
1776 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1777 nt_errstr(status)));
1778 TALLOC_FREE(client_ntlmssp_state);
1779 return False;
1782 spnego_write_data(ctx, &to_server, &spnego);
1783 data_blob_free(&spnego.negTokenInit.mechToken);
1785 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1786 data_blob_free(&to_server);
1787 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1788 TALLOC_FREE(to_server_base64);
1789 return True;
1792 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
1794 NTSTATUS status;
1795 DATA_BLOB null_blob = data_blob_null;
1796 DATA_BLOB request;
1797 DATA_BLOB to_server;
1798 char *to_server_base64;
1799 TALLOC_CTX *ctx = talloc_tos();
1801 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1803 if (client_ntlmssp_state == NULL) {
1804 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1805 x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
1806 return;
1809 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1810 x_fprintf(x_stdout, "NA\n");
1811 TALLOC_FREE(client_ntlmssp_state);
1812 return;
1815 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1816 x_fprintf(x_stdout, "AF\n");
1817 TALLOC_FREE(client_ntlmssp_state);
1818 return;
1821 status = ntlmssp_update(client_ntlmssp_state,
1822 spnego.negTokenTarg.responseToken,
1823 &request);
1825 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
1826 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED or OK from "
1827 "ntlmssp_client_update, got: %s\n",
1828 nt_errstr(status)));
1829 x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
1830 "ntlmssp_client_update\n");
1831 data_blob_free(&request);
1832 TALLOC_FREE(client_ntlmssp_state);
1833 return;
1836 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1837 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1838 spnego.negTokenTarg.supportedMech = (const char *)OID_NTLMSSP;
1839 spnego.negTokenTarg.responseToken = request;
1840 spnego.negTokenTarg.mechListMIC = null_blob;
1842 spnego_write_data(ctx, &to_server, &spnego);
1843 data_blob_free(&request);
1845 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1846 data_blob_free(&to_server);
1847 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1848 TALLOC_FREE(to_server_base64);
1849 return;
1852 #ifdef HAVE_KRB5
1854 static bool manage_client_krb5_init(struct spnego_data spnego)
1856 char *principal;
1857 DATA_BLOB tkt, tkt_wrapped, to_server;
1858 DATA_BLOB session_key_krb5 = data_blob_null;
1859 struct spnego_data reply;
1860 char *reply_base64;
1861 int retval;
1863 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1864 ssize_t len;
1865 TALLOC_CTX *ctx = talloc_tos();
1867 principal = spnego.negTokenInit.targetPrincipal;
1869 /* We may not be allowed to use the server-supplied SPNEGO principal, or it may not have been supplied to us
1871 if (!lp_client_use_spnego_principal() || strequal(principal, ADS_IGNORE_PRINCIPAL)) {
1872 principal = NULL;
1875 if (principal == NULL &&
1876 opt_target_service && opt_target_hostname && !is_ipaddress(opt_target_hostname)) {
1877 DEBUG(3,("manage_client_krb5_init: using target "
1878 "hostname not SPNEGO principal\n"));
1880 principal = kerberos_get_principal_from_service_hostname(talloc_tos(),
1881 opt_target_service,
1882 opt_target_hostname,
1883 lp_realm());
1885 if (!principal) {
1886 return false;
1889 DEBUG(3,("manage_client_krb5_init: guessed "
1890 "server principal=%s\n",
1891 principal ? principal : "<null>"));
1894 if (principal == NULL) {
1895 DEBUG(3,("manage_client_krb5_init: could not guess server principal\n"));
1896 return false;
1899 retval = cli_krb5_get_ticket(ctx, principal, 0,
1900 &tkt, &session_key_krb5,
1901 0, NULL, NULL, NULL);
1902 if (retval) {
1903 char *user = NULL;
1905 /* Let's try to first get the TGT, for that we need a
1906 password. */
1908 if (opt_password == NULL) {
1909 DEBUG(10, ("Requesting password\n"));
1910 x_fprintf(x_stdout, "PW\n");
1911 return True;
1914 user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
1915 if (!user) {
1916 return false;
1919 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1920 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1921 return False;
1924 retval = cli_krb5_get_ticket(ctx, principal, 0,
1925 &tkt, &session_key_krb5,
1926 0, NULL, NULL, NULL);
1927 if (retval) {
1928 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1929 return False;
1934 /* wrap that up in a nice GSS-API wrapping */
1935 tkt_wrapped = spnego_gen_krb5_wrap(ctx, tkt, TOK_ID_KRB_AP_REQ);
1937 data_blob_free(&session_key_krb5);
1939 ZERO_STRUCT(reply);
1941 reply.type = SPNEGO_NEG_TOKEN_INIT;
1942 reply.negTokenInit.mechTypes = my_mechs;
1943 reply.negTokenInit.reqFlags = data_blob_null;
1944 reply.negTokenInit.reqFlagsPadding = 0;
1945 reply.negTokenInit.mechToken = tkt_wrapped;
1946 reply.negTokenInit.mechListMIC = data_blob_null;
1948 len = spnego_write_data(ctx, &to_server, &reply);
1949 data_blob_free(&tkt);
1951 if (len == -1) {
1952 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1953 return False;
1956 reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1957 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1959 TALLOC_FREE(reply_base64);
1960 data_blob_free(&to_server);
1961 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1962 return True;
1965 static void manage_client_krb5_targ(struct spnego_data spnego)
1967 switch (spnego.negTokenTarg.negResult) {
1968 case SPNEGO_ACCEPT_INCOMPLETE:
1969 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1970 x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
1971 "ACCEPT_INCOMPLETE\n");
1972 break;
1973 case SPNEGO_ACCEPT_COMPLETED:
1974 DEBUG(10, ("Accept completed\n"));
1975 x_fprintf(x_stdout, "AF\n");
1976 break;
1977 case SPNEGO_REJECT:
1978 DEBUG(10, ("Rejected\n"));
1979 x_fprintf(x_stdout, "NA\n");
1980 break;
1981 default:
1982 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1983 x_fprintf(x_stdout, "AF\n");
1987 #endif
1989 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1990 struct loadparm_context *lp_ctx,
1991 struct ntlm_auth_state *state,
1992 char *buf, int length, void **private2)
1994 DATA_BLOB request;
1995 struct spnego_data spnego;
1996 ssize_t len;
1997 TALLOC_CTX *ctx = talloc_tos();
1999 if (!opt_username || !*opt_username) {
2000 x_fprintf(x_stderr, "username must be specified!\n\n");
2001 exit(1);
2004 if (strlen(buf) <= 3) {
2005 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
2006 x_fprintf(x_stdout, "BH SPNEGO query too short\n");
2007 return;
2010 request = base64_decode_data_blob(buf+3);
2012 if (strncmp(buf, "PW ", 3) == 0) {
2014 /* We asked for a password and obviously got it :-) */
2016 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
2018 if (opt_password == NULL) {
2019 DEBUG(1, ("Out of memory\n"));
2020 x_fprintf(x_stdout, "BH Out of memory\n");
2021 data_blob_free(&request);
2022 return;
2025 x_fprintf(x_stdout, "OK\n");
2026 data_blob_free(&request);
2027 return;
2030 if ( (strncmp(buf, "TT ", 3) != 0) &&
2031 (strncmp(buf, "AF ", 3) != 0) &&
2032 (strncmp(buf, "NA ", 3) != 0) ) {
2033 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
2034 x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
2035 data_blob_free(&request);
2036 return;
2039 /* So we got a server challenge to generate a SPNEGO
2040 client-to-server request... */
2042 len = spnego_read_data(ctx, request, &spnego);
2043 data_blob_free(&request);
2045 if (len == -1) {
2046 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
2047 x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
2048 return;
2051 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
2053 /* The server offers a list of mechanisms */
2055 const char *const *mechType = spnego.negTokenInit.mechTypes;
2057 while (*mechType != NULL) {
2059 #ifdef HAVE_KRB5
2060 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
2061 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
2062 if (manage_client_krb5_init(spnego))
2063 goto out;
2065 #endif
2067 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
2068 if (manage_client_ntlmssp_init(spnego))
2069 goto out;
2072 mechType++;
2075 DEBUG(1, ("Server offered no compatible mechanism\n"));
2076 x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
2077 return;
2080 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
2082 if (spnego.negTokenTarg.supportedMech == NULL) {
2083 /* On accept/reject Windows does not send the
2084 mechanism anymore. Handle that here and
2085 shut down the mechanisms. */
2087 switch (spnego.negTokenTarg.negResult) {
2088 case SPNEGO_ACCEPT_COMPLETED:
2089 x_fprintf(x_stdout, "AF\n");
2090 break;
2091 case SPNEGO_REJECT:
2092 x_fprintf(x_stdout, "NA\n");
2093 break;
2094 default:
2095 DEBUG(1, ("Got a negTokenTarg with no mech and an "
2096 "unknown negResult: %d\n",
2097 spnego.negTokenTarg.negResult));
2098 x_fprintf(x_stdout, "BH Got a negTokenTarg with"
2099 " no mech and an unknown "
2100 "negResult\n");
2103 TALLOC_FREE(client_ntlmssp_state);
2104 goto out;
2107 if (strcmp(spnego.negTokenTarg.supportedMech,
2108 OID_NTLMSSP) == 0) {
2109 manage_client_ntlmssp_targ(spnego);
2110 goto out;
2113 #if HAVE_KRB5
2114 if (strcmp(spnego.negTokenTarg.supportedMech,
2115 OID_KERBEROS5_OLD) == 0) {
2116 manage_client_krb5_targ(spnego);
2117 goto out;
2119 #endif
2123 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
2124 x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
2125 return;
2127 out:
2128 spnego_free_data(&spnego);
2129 return;
2132 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
2133 struct loadparm_context *lp_ctx,
2134 struct ntlm_auth_state *state,
2135 char *buf, int length, void **private2)
2137 char *request, *parameter;
2138 static DATA_BLOB challenge;
2139 static DATA_BLOB lm_response;
2140 static DATA_BLOB nt_response;
2141 static char *full_username;
2142 static char *username;
2143 static char *domain;
2144 static char *plaintext_password;
2145 static bool ntlm_server_1_user_session_key;
2146 static bool ntlm_server_1_lm_session_key;
2148 if (strequal(buf, ".")) {
2149 if (!full_username && !username) {
2150 x_fprintf(x_stdout, "Error: No username supplied!\n");
2151 } else if (plaintext_password) {
2152 /* handle this request as plaintext */
2153 if (!full_username) {
2154 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
2155 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
2156 return;
2159 if (check_plaintext_auth(full_username, plaintext_password, False)) {
2160 x_fprintf(x_stdout, "Authenticated: Yes\n");
2161 } else {
2162 x_fprintf(x_stdout, "Authenticated: No\n");
2164 } else if (!lm_response.data && !nt_response.data) {
2165 x_fprintf(x_stdout, "Error: No password supplied!\n");
2166 } else if (!challenge.data) {
2167 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
2168 } else {
2169 char *error_string = NULL;
2170 uchar lm_key[8];
2171 uchar user_session_key[16];
2172 uint32_t flags = 0;
2174 if (full_username && !username) {
2175 fstring fstr_user;
2176 fstring fstr_domain;
2178 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
2179 /* username might be 'tainted', don't print into our new-line deleimianted stream */
2180 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
2182 SAFE_FREE(username);
2183 SAFE_FREE(domain);
2184 username = smb_xstrdup(fstr_user);
2185 domain = smb_xstrdup(fstr_domain);
2188 if (!domain) {
2189 domain = smb_xstrdup(get_winbind_domain());
2192 if (ntlm_server_1_lm_session_key)
2193 flags |= WBFLAG_PAM_LMKEY;
2195 if (ntlm_server_1_user_session_key)
2196 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2198 if (!NT_STATUS_IS_OK(
2199 contact_winbind_auth_crap(username,
2200 domain,
2201 lp_netbios_name(),
2202 &challenge,
2203 &lm_response,
2204 &nt_response,
2205 flags, 0,
2206 lm_key,
2207 user_session_key,
2208 &error_string,
2209 NULL))) {
2211 x_fprintf(x_stdout, "Authenticated: No\n");
2212 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
2213 } else {
2214 static char zeros[16];
2215 char *hex_lm_key;
2216 char *hex_user_session_key;
2218 x_fprintf(x_stdout, "Authenticated: Yes\n");
2220 if (ntlm_server_1_lm_session_key
2221 && (memcmp(zeros, lm_key,
2222 sizeof(lm_key)) != 0)) {
2223 hex_lm_key = hex_encode_talloc(NULL,
2224 (const unsigned char *)lm_key,
2225 sizeof(lm_key));
2226 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
2227 TALLOC_FREE(hex_lm_key);
2230 if (ntlm_server_1_user_session_key
2231 && (memcmp(zeros, user_session_key,
2232 sizeof(user_session_key)) != 0)) {
2233 hex_user_session_key = hex_encode_talloc(NULL,
2234 (const unsigned char *)user_session_key,
2235 sizeof(user_session_key));
2236 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
2237 TALLOC_FREE(hex_user_session_key);
2240 SAFE_FREE(error_string);
2242 /* clear out the state */
2243 challenge = data_blob_null;
2244 nt_response = data_blob_null;
2245 lm_response = data_blob_null;
2246 SAFE_FREE(full_username);
2247 SAFE_FREE(username);
2248 SAFE_FREE(domain);
2249 SAFE_FREE(plaintext_password);
2250 ntlm_server_1_user_session_key = False;
2251 ntlm_server_1_lm_session_key = False;
2252 x_fprintf(x_stdout, ".\n");
2254 return;
2257 request = buf;
2259 /* Indicates a base64 encoded structure */
2260 parameter = strstr_m(request, ":: ");
2261 if (!parameter) {
2262 parameter = strstr_m(request, ": ");
2264 if (!parameter) {
2265 DEBUG(0, ("Parameter not found!\n"));
2266 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2267 return;
2270 parameter[0] ='\0';
2271 parameter++;
2272 parameter[0] ='\0';
2273 parameter++;
2275 } else {
2276 parameter[0] ='\0';
2277 parameter++;
2278 parameter[0] ='\0';
2279 parameter++;
2280 parameter[0] ='\0';
2281 parameter++;
2283 base64_decode_inplace(parameter);
2286 if (strequal(request, "LANMAN-Challenge")) {
2287 challenge = strhex_to_data_blob(NULL, parameter);
2288 if (challenge.length != 8) {
2289 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
2290 parameter,
2291 (int)challenge.length);
2292 challenge = data_blob_null;
2294 } else if (strequal(request, "NT-Response")) {
2295 nt_response = strhex_to_data_blob(NULL, parameter);
2296 if (nt_response.length < 24) {
2297 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
2298 parameter,
2299 (int)nt_response.length);
2300 nt_response = data_blob_null;
2302 } else if (strequal(request, "LANMAN-Response")) {
2303 lm_response = strhex_to_data_blob(NULL, parameter);
2304 if (lm_response.length != 24) {
2305 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
2306 parameter,
2307 (int)lm_response.length);
2308 lm_response = data_blob_null;
2310 } else if (strequal(request, "Password")) {
2311 plaintext_password = smb_xstrdup(parameter);
2312 } else if (strequal(request, "NT-Domain")) {
2313 domain = smb_xstrdup(parameter);
2314 } else if (strequal(request, "Username")) {
2315 username = smb_xstrdup(parameter);
2316 } else if (strequal(request, "Full-Username")) {
2317 full_username = smb_xstrdup(parameter);
2318 } else if (strequal(request, "Request-User-Session-Key")) {
2319 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2320 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2321 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2322 } else {
2323 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2327 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2328 struct loadparm_context *lp_ctx,
2329 struct ntlm_auth_state *state,
2330 char *buf, int length, void **private2)
2332 char *request, *parameter;
2333 static DATA_BLOB new_nt_pswd;
2334 static DATA_BLOB old_nt_hash_enc;
2335 static DATA_BLOB new_lm_pswd;
2336 static DATA_BLOB old_lm_hash_enc;
2337 static char *full_username = NULL;
2338 static char *username = NULL;
2339 static char *domain = NULL;
2340 static char *newpswd = NULL;
2341 static char *oldpswd = NULL;
2343 if (strequal(buf, ".")) {
2344 if(newpswd && oldpswd) {
2345 uchar old_nt_hash[16];
2346 uchar old_lm_hash[16];
2347 uchar new_nt_hash[16];
2348 uchar new_lm_hash[16];
2350 new_nt_pswd = data_blob(NULL, 516);
2351 old_nt_hash_enc = data_blob(NULL, 16);
2353 /* Calculate the MD4 hash (NT compatible) of the
2354 * password */
2355 E_md4hash(oldpswd, old_nt_hash);
2356 E_md4hash(newpswd, new_nt_hash);
2358 /* E_deshash returns false for 'long'
2359 passwords (> 14 DOS chars).
2361 Therefore, don't send a buffer
2362 encrypted with the truncated hash
2363 (it could allow an even easier
2364 attack on the password)
2366 Likewise, obey the admin's restriction
2369 if (lp_client_lanman_auth() &&
2370 E_deshash(newpswd, new_lm_hash) &&
2371 E_deshash(oldpswd, old_lm_hash)) {
2372 new_lm_pswd = data_blob(NULL, 516);
2373 old_lm_hash_enc = data_blob(NULL, 16);
2374 encode_pw_buffer(new_lm_pswd.data, newpswd,
2375 STR_UNICODE);
2377 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
2378 E_old_pw_hash(new_nt_hash, old_lm_hash,
2379 old_lm_hash_enc.data);
2380 } else {
2381 new_lm_pswd.data = NULL;
2382 new_lm_pswd.length = 0;
2383 old_lm_hash_enc.data = NULL;
2384 old_lm_hash_enc.length = 0;
2387 encode_pw_buffer(new_nt_pswd.data, newpswd,
2388 STR_UNICODE);
2390 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
2391 E_old_pw_hash(new_nt_hash, old_nt_hash,
2392 old_nt_hash_enc.data);
2395 if (!full_username && !username) {
2396 x_fprintf(x_stdout, "Error: No username supplied!\n");
2397 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2398 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2399 x_fprintf(x_stdout, "Error: No NT or LM password "
2400 "blobs supplied!\n");
2401 } else {
2402 char *error_string = NULL;
2404 if (full_username && !username) {
2405 fstring fstr_user;
2406 fstring fstr_domain;
2408 if (!parse_ntlm_auth_domain_user(full_username,
2409 fstr_user,
2410 fstr_domain)) {
2411 /* username might be 'tainted', don't
2412 * print into our new-line
2413 * deleimianted stream */
2414 x_fprintf(x_stdout, "Error: Could not "
2415 "parse into domain and "
2416 "username\n");
2417 SAFE_FREE(username);
2418 username = smb_xstrdup(full_username);
2419 } else {
2420 SAFE_FREE(username);
2421 SAFE_FREE(domain);
2422 username = smb_xstrdup(fstr_user);
2423 domain = smb_xstrdup(fstr_domain);
2428 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2429 username, domain,
2430 new_nt_pswd,
2431 old_nt_hash_enc,
2432 new_lm_pswd,
2433 old_lm_hash_enc,
2434 &error_string))) {
2435 x_fprintf(x_stdout, "Password-Change: No\n");
2436 x_fprintf(x_stdout, "Password-Change-Error: "
2437 "%s\n.\n", error_string);
2438 } else {
2439 x_fprintf(x_stdout, "Password-Change: Yes\n");
2442 SAFE_FREE(error_string);
2444 /* clear out the state */
2445 new_nt_pswd = data_blob_null;
2446 old_nt_hash_enc = data_blob_null;
2447 new_lm_pswd = data_blob_null;
2448 old_nt_hash_enc = data_blob_null;
2449 SAFE_FREE(full_username);
2450 SAFE_FREE(username);
2451 SAFE_FREE(domain);
2452 SAFE_FREE(newpswd);
2453 SAFE_FREE(oldpswd);
2454 x_fprintf(x_stdout, ".\n");
2456 return;
2459 request = buf;
2461 /* Indicates a base64 encoded structure */
2462 parameter = strstr_m(request, ":: ");
2463 if (!parameter) {
2464 parameter = strstr_m(request, ": ");
2466 if (!parameter) {
2467 DEBUG(0, ("Parameter not found!\n"));
2468 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2469 return;
2472 parameter[0] ='\0';
2473 parameter++;
2474 parameter[0] ='\0';
2475 parameter++;
2476 } else {
2477 parameter[0] ='\0';
2478 parameter++;
2479 parameter[0] ='\0';
2480 parameter++;
2481 parameter[0] ='\0';
2482 parameter++;
2484 base64_decode_inplace(parameter);
2487 if (strequal(request, "new-nt-password-blob")) {
2488 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2489 if (new_nt_pswd.length != 516) {
2490 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2491 "(got %d bytes, expected 516)\n.\n",
2492 parameter,
2493 (int)new_nt_pswd.length);
2494 new_nt_pswd = data_blob_null;
2496 } else if (strequal(request, "old-nt-hash-blob")) {
2497 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2498 if (old_nt_hash_enc.length != 16) {
2499 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2500 "(got %d bytes, expected 16)\n.\n",
2501 parameter,
2502 (int)old_nt_hash_enc.length);
2503 old_nt_hash_enc = data_blob_null;
2505 } else if (strequal(request, "new-lm-password-blob")) {
2506 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2507 if (new_lm_pswd.length != 516) {
2508 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2509 "(got %d bytes, expected 516)\n.\n",
2510 parameter,
2511 (int)new_lm_pswd.length);
2512 new_lm_pswd = data_blob_null;
2515 else if (strequal(request, "old-lm-hash-blob")) {
2516 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2517 if (old_lm_hash_enc.length != 16)
2519 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2520 "(got %d bytes, expected 16)\n.\n",
2521 parameter,
2522 (int)old_lm_hash_enc.length);
2523 old_lm_hash_enc = data_blob_null;
2525 } else if (strequal(request, "nt-domain")) {
2526 domain = smb_xstrdup(parameter);
2527 } else if(strequal(request, "username")) {
2528 username = smb_xstrdup(parameter);
2529 } else if(strequal(request, "full-username")) {
2530 username = smb_xstrdup(parameter);
2531 } else if(strequal(request, "new-password")) {
2532 newpswd = smb_xstrdup(parameter);
2533 } else if (strequal(request, "old-password")) {
2534 oldpswd = smb_xstrdup(parameter);
2535 } else {
2536 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2540 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2541 struct loadparm_context *lp_ctx,
2542 struct ntlm_auth_state *state,
2543 stdio_helper_function fn, void **private2)
2545 char *buf;
2546 char tmp[INITIAL_BUFFER_SIZE+1];
2547 int length, buf_size = 0;
2548 char *c;
2550 buf = talloc_strdup(state->mem_ctx, "");
2551 if (!buf) {
2552 DEBUG(0, ("Failed to allocate input buffer.\n"));
2553 x_fprintf(x_stderr, "ERR\n");
2554 exit(1);
2557 do {
2559 /* this is not a typo - x_fgets doesn't work too well under
2560 * squid */
2561 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2562 if (ferror(stdin)) {
2563 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2564 "(%s)\n", ferror(stdin),
2565 strerror(ferror(stdin))));
2567 exit(1);
2569 exit(0);
2572 buf = talloc_strdup_append_buffer(buf, tmp);
2573 buf_size += INITIAL_BUFFER_SIZE;
2575 if (buf_size > MAX_BUFFER_SIZE) {
2576 DEBUG(2, ("Oversized message\n"));
2577 x_fprintf(x_stderr, "ERR\n");
2578 talloc_free(buf);
2579 return;
2582 c = strchr(buf, '\n');
2583 } while (c == NULL);
2585 *c = '\0';
2586 length = c-buf;
2588 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2590 if (buf[0] == '\0') {
2591 DEBUG(2, ("Invalid Request\n"));
2592 x_fprintf(x_stderr, "ERR\n");
2593 talloc_free(buf);
2594 return;
2597 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2598 talloc_free(buf);
2602 static void squid_stream(enum stdio_helper_mode stdio_mode,
2603 struct loadparm_context *lp_ctx,
2604 stdio_helper_function fn) {
2605 TALLOC_CTX *mem_ctx;
2606 struct ntlm_auth_state *state;
2608 /* initialize FDescs */
2609 x_setbuf(x_stdout, NULL);
2610 x_setbuf(x_stderr, NULL);
2612 mem_ctx = talloc_init("ntlm_auth");
2613 if (!mem_ctx) {
2614 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2615 x_fprintf(x_stderr, "ERR\n");
2616 exit(1);
2619 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2620 if (!state) {
2621 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2622 x_fprintf(x_stderr, "ERR\n");
2623 exit(1);
2626 state->mem_ctx = mem_ctx;
2627 state->helper_mode = stdio_mode;
2629 while(1) {
2630 TALLOC_CTX *frame = talloc_stackframe();
2631 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2632 TALLOC_FREE(frame);
2637 /* Authenticate a user with a challenge/response */
2639 static bool check_auth_crap(void)
2641 NTSTATUS nt_status;
2642 uint32_t flags = 0;
2643 char lm_key[8];
2644 char user_session_key[16];
2645 char *hex_lm_key;
2646 char *hex_user_session_key;
2647 char *error_string;
2648 static uint8_t zeros[16];
2650 x_setbuf(x_stdout, NULL);
2652 if (request_lm_key)
2653 flags |= WBFLAG_PAM_LMKEY;
2655 if (request_user_session_key)
2656 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2658 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2660 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2661 opt_workstation,
2662 &opt_challenge,
2663 &opt_lm_response,
2664 &opt_nt_response,
2665 flags, 0,
2666 (unsigned char *)lm_key,
2667 (unsigned char *)user_session_key,
2668 &error_string, NULL);
2670 if (!NT_STATUS_IS_OK(nt_status)) {
2671 x_fprintf(x_stdout, "%s (0x%x)\n",
2672 error_string,
2673 NT_STATUS_V(nt_status));
2674 SAFE_FREE(error_string);
2675 return False;
2678 if (request_lm_key
2679 && (memcmp(zeros, lm_key,
2680 sizeof(lm_key)) != 0)) {
2681 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2682 sizeof(lm_key));
2683 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2684 TALLOC_FREE(hex_lm_key);
2686 if (request_user_session_key
2687 && (memcmp(zeros, user_session_key,
2688 sizeof(user_session_key)) != 0)) {
2689 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2690 sizeof(user_session_key));
2691 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2692 TALLOC_FREE(hex_user_session_key);
2695 return True;
2698 /* Main program */
2700 enum {
2701 OPT_USERNAME = 1000,
2702 OPT_DOMAIN,
2703 OPT_WORKSTATION,
2704 OPT_CHALLENGE,
2705 OPT_RESPONSE,
2706 OPT_LM,
2707 OPT_NT,
2708 OPT_PASSWORD,
2709 OPT_LM_KEY,
2710 OPT_USER_SESSION_KEY,
2711 OPT_DIAGNOSTICS,
2712 OPT_REQUIRE_MEMBERSHIP,
2713 OPT_USE_CACHED_CREDS,
2714 OPT_PAM_WINBIND_CONF,
2715 OPT_TARGET_SERVICE,
2716 OPT_TARGET_HOSTNAME
2719 int main(int argc, const char **argv)
2721 TALLOC_CTX *frame = talloc_stackframe();
2722 int opt;
2723 static const char *helper_protocol;
2724 static int diagnostics;
2726 static const char *hex_challenge;
2727 static const char *hex_lm_response;
2728 static const char *hex_nt_response;
2729 struct loadparm_context *lp_ctx;
2730 poptContext pc;
2732 /* NOTE: DO NOT change this interface without considering the implications!
2733 This is an external interface, which other programs will use to interact
2734 with this helper.
2737 /* We do not use single-letter command abbreviations, because they harm future
2738 interface stability. */
2740 struct poptOption long_options[] = {
2741 POPT_AUTOHELP
2742 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2743 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2744 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2745 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2746 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2747 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2748 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2749 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2750 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2751 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2752 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2753 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2754 OPT_DIAGNOSTICS,
2755 "Perform diagnostics on the authentication chain"},
2756 { "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" },
2757 { "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" },
2758 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2759 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2760 POPT_COMMON_CONFIGFILE
2761 POPT_COMMON_VERSION
2762 POPT_COMMON_OPTION
2763 POPT_TABLEEND
2766 /* Samba client initialisation */
2767 smb_init_locale();
2769 setup_logging("ntlm_auth", DEBUG_STDERR);
2771 /* Parse options */
2773 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2775 /* Parse command line options */
2777 if (argc == 1) {
2778 poptPrintHelp(pc, stderr, 0);
2779 return 1;
2782 while((opt = poptGetNextOpt(pc)) != -1) {
2783 /* Get generic config options like --configfile */
2786 poptFreeContext(pc);
2788 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2789 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2790 get_dyn_CONFIGFILE(), strerror(errno));
2791 exit(1);
2794 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2795 POPT_CONTEXT_KEEP_FIRST);
2797 while((opt = poptGetNextOpt(pc)) != -1) {
2798 switch (opt) {
2799 case OPT_CHALLENGE:
2800 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2801 if (opt_challenge.length != 8) {
2802 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2803 hex_challenge,
2804 (int)opt_challenge.length);
2805 exit(1);
2807 break;
2808 case OPT_LM:
2809 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2810 if (opt_lm_response.length != 24) {
2811 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2812 hex_lm_response,
2813 (int)opt_lm_response.length);
2814 exit(1);
2816 break;
2818 case OPT_NT:
2819 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2820 if (opt_nt_response.length < 24) {
2821 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2822 hex_nt_response,
2823 (int)opt_nt_response.length);
2824 exit(1);
2826 break;
2828 case OPT_REQUIRE_MEMBERSHIP:
2829 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2830 require_membership_of_sid = require_membership_of;
2832 break;
2836 if (opt_username) {
2837 char *domain = SMB_STRDUP(opt_username);
2838 char *p = strchr_m(domain, *lp_winbind_separator());
2839 if (p) {
2840 opt_username = p+1;
2841 *p = '\0';
2842 if (opt_domain && !strequal(opt_domain, domain)) {
2843 x_fprintf(x_stderr, "Domain specified in username (%s) "
2844 "doesn't match specified domain (%s)!\n\n",
2845 domain, opt_domain);
2846 poptPrintHelp(pc, stderr, 0);
2847 exit(1);
2849 opt_domain = domain;
2850 } else {
2851 SAFE_FREE(domain);
2855 /* Note: if opt_domain is "" then send no domain */
2856 if (opt_domain == NULL) {
2857 opt_domain = get_winbind_domain();
2860 if (opt_workstation == NULL) {
2861 opt_workstation = "";
2864 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2865 if (lp_ctx == NULL) {
2866 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2867 exit(1);
2870 if (helper_protocol) {
2871 int i;
2872 for (i=0; i<NUM_HELPER_MODES; i++) {
2873 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2874 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2875 exit(0);
2878 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2880 for (i=0; i<NUM_HELPER_MODES; i++) {
2881 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2884 exit(1);
2887 if (!opt_username || !*opt_username) {
2888 x_fprintf(x_stderr, "username must be specified!\n\n");
2889 poptPrintHelp(pc, stderr, 0);
2890 exit(1);
2893 if (opt_challenge.length) {
2894 if (!check_auth_crap()) {
2895 exit(1);
2897 exit(0);
2900 if (!opt_password) {
2901 char pwd[256] = {0};
2902 int rc;
2904 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2905 if (rc == 0) {
2906 opt_password = SMB_STRDUP(pwd);
2910 if (diagnostics) {
2911 if (!diagnose_ntlm_auth()) {
2912 return 1;
2914 } else {
2915 fstring user;
2917 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2918 if (!check_plaintext_auth(user, opt_password, True)) {
2919 return 1;
2923 /* Exit code */
2925 poptFreeContext(pc);
2926 TALLOC_FREE(frame);
2927 return 0;