s3: Use dbwrap_parse_record in fetch_share_mode_unlocked
[Samba.git] / source3 / utils / ntlm_auth.c
blob2bf9747b2fb68fd583e3245edd2eb8993dd8bd97
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind status program.
6 Copyright (C) Tim Potter 2000-2003
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
8 Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000
9 Copyright (C) Robert O'Callahan 2006 (added cached credential code).
10 Copyright (C) Kai Blin <kai@samba.org> 2008
11 Copyright (C) Simo Sorce 2010
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 3 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "includes.h"
28 #include "lib/param/param.h"
29 #include "popt_common.h"
30 #include "utils/ntlm_auth.h"
31 #include "../libcli/auth/libcli_auth.h"
32 #include "../libcli/auth/spnego.h"
33 #include "auth/ntlmssp/ntlmssp.h"
34 #include "auth/gensec/gensec.h"
35 #include "auth/credentials/credentials.h"
36 #include "librpc/crypto/gse.h"
37 #include "smb_krb5.h"
38 #include <iniparser.h>
39 #include "../lib/crypto/arcfour.h"
40 #include "libads/kerberos_proto.h"
41 #include "nsswitch/winbind_client.h"
42 #include "librpc/gen_ndr/krb5pac.h"
43 #include "../lib/util/asn1.h"
44 #include "auth/common_auth.h"
45 #include "source3/include/auth.h"
46 #include "source3/auth/proto.h"
47 #include "nsswitch/libwbclient/wbclient.h"
48 #include "lib/param/loadparm.h"
50 #if HAVE_KRB5
51 #include "auth/kerberos/pac_utils.h"
52 #endif
54 #ifndef PAM_WINBIND_CONFIG_FILE
55 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
56 #endif
58 #define WINBIND_KRB5_AUTH 0x00000080
60 #undef DBGC_CLASS
61 #define DBGC_CLASS DBGC_WINBIND
63 #define INITIAL_BUFFER_SIZE 300
64 #define MAX_BUFFER_SIZE 630000
66 enum stdio_helper_mode {
67 SQUID_2_4_BASIC,
68 SQUID_2_5_BASIC,
69 SQUID_2_5_NTLMSSP,
70 NTLMSSP_CLIENT_1,
71 GSS_SPNEGO_SERVER,
72 GSS_SPNEGO_CLIENT,
73 NTLM_SERVER_1,
74 NTLM_CHANGE_PASSWORD_1,
75 NUM_HELPER_MODES
78 enum ntlm_auth_cli_state {
79 CLIENT_INITIAL = 0,
80 CLIENT_RESPONSE,
81 CLIENT_FINISHED,
82 CLIENT_ERROR
85 struct ntlm_auth_state {
86 TALLOC_CTX *mem_ctx;
87 enum stdio_helper_mode helper_mode;
88 enum ntlm_auth_cli_state cli_state;
89 struct ntlmssp_state *ntlmssp_state;
90 uint32_t neg_flags;
91 char *want_feature_list;
92 bool have_session_key;
93 DATA_BLOB session_key;
94 DATA_BLOB initial_message;
95 void *gensec_private_1;
97 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
98 struct loadparm_context *lp_ctx,
99 struct ntlm_auth_state *state, char *buf,
100 int length, void **private2);
102 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
103 struct loadparm_context *lp_ctx,
104 struct ntlm_auth_state *state,
105 stdio_helper_function fn, void **private2);
107 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
108 struct loadparm_context *lp_ctx,
109 struct ntlm_auth_state *state,
110 char *buf, int length, void **private2);
112 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
113 struct loadparm_context *lp_ctx,
114 struct ntlm_auth_state *state,
115 char *buf, int length, void **private2);
117 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
118 struct loadparm_context *lp_ctx,
119 struct ntlm_auth_state *state,
120 char *buf, int length, void **private2);
122 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
123 struct loadparm_context *lp_ctx,
124 struct ntlm_auth_state *state,
125 char *buf, int length, void **private2);
127 static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
128 struct loadparm_context *lp_ctx,
129 struct ntlm_auth_state *state,
130 char *buf, int length, void **private2);
132 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
133 struct loadparm_context *lp_ctx,
134 struct ntlm_auth_state *state,
135 char *buf, int length, void **private2);
137 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
138 struct loadparm_context *lp_ctx,
139 struct ntlm_auth_state *state,
140 char *buf, int length, void **private2);
142 static const struct {
143 enum stdio_helper_mode mode;
144 const char *name;
145 stdio_helper_function fn;
146 } stdio_helper_protocols[] = {
147 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
148 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
149 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
150 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
151 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
152 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
153 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
154 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
155 { NUM_HELPER_MODES, NULL, NULL}
158 const char *opt_username;
159 const char *opt_domain;
160 const char *opt_workstation;
161 const char *opt_password;
162 static DATA_BLOB opt_challenge;
163 static DATA_BLOB opt_lm_response;
164 static DATA_BLOB opt_nt_response;
165 static int request_lm_key;
166 static int request_user_session_key;
167 static int use_cached_creds;
169 static const char *require_membership_of;
170 static const char *require_membership_of_sid;
171 static const char *opt_pam_winbind_conf;
173 const char *opt_target_service;
174 const char *opt_target_hostname;
177 /* This is a bit hairy, but the basic idea is to do a password callback
178 to the calling application. The callback comes from within gensec */
180 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
181 struct loadparm_context *lp_ctx,
182 struct ntlm_auth_state *state, char *buf, int length,
183 void **password)
185 DATA_BLOB in;
186 if (strlen(buf) < 2) {
187 DEBUG(1, ("query [%s] invalid", buf));
188 x_fprintf(x_stdout, "BH Query invalid\n");
189 return;
192 if (strlen(buf) > 3) {
193 in = base64_decode_data_blob(buf + 3);
194 } else {
195 in = data_blob(NULL, 0);
198 if (strncmp(buf, "PW ", 3) == 0) {
200 *password = talloc_strndup(NULL,
201 (const char *)in.data, in.length);
203 if (*password == NULL) {
204 DEBUG(1, ("Out of memory\n"));
205 x_fprintf(x_stdout, "BH Out of memory\n");
206 data_blob_free(&in);
207 return;
210 x_fprintf(x_stdout, "OK\n");
211 data_blob_free(&in);
212 return;
214 DEBUG(1, ("Asked for (and expected) a password\n"));
215 x_fprintf(x_stdout, "BH Expected a password\n");
216 data_blob_free(&in);
220 * Callback for password credentials. This is not async, and when
221 * GENSEC and the credentials code is made async, it will look rather
222 * different.
225 static const char *get_password(struct cli_credentials *credentials)
227 char *password = NULL;
229 /* Ask for a password */
230 x_fprintf(x_stdout, "PW\n");
231 credentials->priv_data = NULL;
233 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, NULL, manage_gensec_get_pw_request, (void **)&password);
234 talloc_steal(credentials, password);
235 return password;
239 * A limited set of features are defined with text strings as needed
240 * by ntlm_auth
243 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
245 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
246 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
247 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
249 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
250 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
251 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
253 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
254 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
255 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
259 static char winbind_separator(void)
261 struct winbindd_response response;
262 static bool got_sep;
263 static char sep;
265 if (got_sep)
266 return sep;
268 ZERO_STRUCT(response);
270 /* Send off request */
272 if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
273 NSS_STATUS_SUCCESS) {
274 d_printf("could not obtain winbind separator!\n");
275 return *lp_winbind_separator();
278 sep = response.data.info.winbind_separator;
279 got_sep = True;
281 if (!sep) {
282 d_printf("winbind separator was NULL!\n");
283 return *lp_winbind_separator();
286 return sep;
289 const char *get_winbind_domain(void)
291 struct winbindd_response response;
293 static fstring winbind_domain;
294 if (*winbind_domain) {
295 return winbind_domain;
298 ZERO_STRUCT(response);
300 /* Send off request */
302 if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
303 NSS_STATUS_SUCCESS) {
304 DEBUG(1, ("could not obtain winbind domain name!\n"));
305 return lp_workgroup();
308 fstrcpy(winbind_domain, response.data.domain_name);
310 return winbind_domain;
314 const char *get_winbind_netbios_name(void)
316 struct winbindd_response response;
318 static fstring winbind_netbios_name;
320 if (*winbind_netbios_name) {
321 return winbind_netbios_name;
324 ZERO_STRUCT(response);
326 /* Send off request */
328 if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) !=
329 NSS_STATUS_SUCCESS) {
330 DEBUG(1, ("could not obtain winbind netbios name!\n"));
331 return lp_netbios_name();
334 fstrcpy(winbind_netbios_name, response.data.netbios_name);
336 return winbind_netbios_name;
340 DATA_BLOB get_challenge(void)
342 static DATA_BLOB chal;
343 if (opt_challenge.length)
344 return opt_challenge;
346 chal = data_blob(NULL, 8);
348 generate_random_buffer(chal.data, chal.length);
349 return chal;
352 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
353 form DOMAIN/user into a domain and a user */
355 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
356 fstring user)
359 char *p = strchr(domuser,winbind_separator());
361 if (!p) {
362 return False;
365 fstrcpy(user, p+1);
366 fstrcpy(domain, domuser);
367 domain[PTR_DIFF(p, domuser)] = 0;
368 return strupper_m(domain);
371 static bool get_require_membership_sid(void) {
372 struct winbindd_request request;
373 struct winbindd_response response;
375 if (!require_membership_of) {
376 return True;
379 if (require_membership_of_sid) {
380 return True;
383 /* Otherwise, ask winbindd for the name->sid request */
385 ZERO_STRUCT(request);
386 ZERO_STRUCT(response);
388 if (!parse_ntlm_auth_domain_user(require_membership_of,
389 request.data.name.dom_name,
390 request.data.name.name)) {
391 DEBUG(0, ("Could not parse %s into seperate domain/name parts!\n",
392 require_membership_of));
393 return False;
396 if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
397 NSS_STATUS_SUCCESS) {
398 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
399 require_membership_of));
400 return False;
403 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
405 if (require_membership_of_sid)
406 return True;
408 return False;
412 * Get some configuration from pam_winbind.conf to see if we
413 * need to contact trusted domain
416 int get_pam_winbind_config()
418 int ctrl = 0;
419 dictionary *d = NULL;
421 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
422 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
425 d = iniparser_load(discard_const_p(char, opt_pam_winbind_conf));
427 if (!d) {
428 return 0;
431 if (iniparser_getboolean(d, discard_const_p(char, "global:krb5_auth"), false)) {
432 ctrl |= WINBIND_KRB5_AUTH;
435 iniparser_freedict(d);
437 return ctrl;
440 /* Authenticate a user with a plaintext password */
442 static bool check_plaintext_auth(const char *user, const char *pass,
443 bool stdout_diagnostics)
445 struct winbindd_request request;
446 struct winbindd_response response;
447 NSS_STATUS result;
449 if (!get_require_membership_sid()) {
450 return False;
453 /* Send off request */
455 ZERO_STRUCT(request);
456 ZERO_STRUCT(response);
458 fstrcpy(request.data.auth.user, user);
459 fstrcpy(request.data.auth.pass, pass);
460 if (require_membership_of_sid) {
461 strlcpy(request.data.auth.require_membership_of_sid,
462 require_membership_of_sid,
463 sizeof(request.data.auth.require_membership_of_sid));
466 result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
468 /* Display response */
470 if (stdout_diagnostics) {
471 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
472 d_printf("Reading winbind reply failed! (0x01)\n");
475 d_printf("%s: %s (0x%x)\n",
476 response.data.auth.nt_status_string,
477 response.data.auth.error_string,
478 response.data.auth.nt_status);
479 } else {
480 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
481 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
484 DEBUG(3, ("%s: %s (0x%x)\n",
485 response.data.auth.nt_status_string,
486 response.data.auth.error_string,
487 response.data.auth.nt_status));
490 return (result == NSS_STATUS_SUCCESS);
493 /* authenticate a user with an encrypted username/password */
495 NTSTATUS contact_winbind_auth_crap(const char *username,
496 const char *domain,
497 const char *workstation,
498 const DATA_BLOB *challenge,
499 const DATA_BLOB *lm_response,
500 const DATA_BLOB *nt_response,
501 uint32 flags,
502 uint32 extra_logon_parameters,
503 uint8 lm_key[8],
504 uint8 user_session_key[16],
505 char **error_string,
506 char **unix_name)
508 NTSTATUS nt_status;
509 NSS_STATUS result;
510 struct winbindd_request request;
511 struct winbindd_response response;
513 if (!get_require_membership_sid()) {
514 return NT_STATUS_INVALID_PARAMETER;
517 ZERO_STRUCT(request);
518 ZERO_STRUCT(response);
520 request.flags = flags;
522 request.data.auth_crap.logon_parameters = extra_logon_parameters
523 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
525 if (require_membership_of_sid)
526 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
528 fstrcpy(request.data.auth_crap.user, username);
529 fstrcpy(request.data.auth_crap.domain, domain);
531 fstrcpy(request.data.auth_crap.workstation,
532 workstation);
534 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
536 if (lm_response && lm_response->length) {
537 memcpy(request.data.auth_crap.lm_resp,
538 lm_response->data,
539 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
540 request.data.auth_crap.lm_resp_len = lm_response->length;
543 if (nt_response && nt_response->length) {
544 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
545 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
546 request.extra_len = nt_response->length;
547 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
548 if (request.extra_data.data == NULL) {
549 return NT_STATUS_NO_MEMORY;
551 memcpy(request.extra_data.data, nt_response->data,
552 nt_response->length);
554 } else {
555 memcpy(request.data.auth_crap.nt_resp,
556 nt_response->data, nt_response->length);
558 request.data.auth_crap.nt_resp_len = nt_response->length;
561 result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
562 SAFE_FREE(request.extra_data.data);
564 /* Display response */
566 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
567 nt_status = NT_STATUS_UNSUCCESSFUL;
568 if (error_string)
569 *error_string = smb_xstrdup("Reading winbind reply failed!");
570 winbindd_free_response(&response);
571 return nt_status;
574 nt_status = (NT_STATUS(response.data.auth.nt_status));
575 if (!NT_STATUS_IS_OK(nt_status)) {
576 if (error_string)
577 *error_string = smb_xstrdup(response.data.auth.error_string);
578 winbindd_free_response(&response);
579 return nt_status;
582 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
583 memcpy(lm_key, response.data.auth.first_8_lm_hash,
584 sizeof(response.data.auth.first_8_lm_hash));
586 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
587 memcpy(user_session_key, response.data.auth.user_session_key,
588 sizeof(response.data.auth.user_session_key));
591 if (flags & WBFLAG_PAM_UNIX_NAME) {
592 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
593 if (!*unix_name) {
594 winbindd_free_response(&response);
595 return NT_STATUS_NO_MEMORY;
599 winbindd_free_response(&response);
600 return nt_status;
603 /* contact server to change user password using auth crap */
604 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
605 const char *domain,
606 const DATA_BLOB new_nt_pswd,
607 const DATA_BLOB old_nt_hash_enc,
608 const DATA_BLOB new_lm_pswd,
609 const DATA_BLOB old_lm_hash_enc,
610 char **error_string)
612 NTSTATUS nt_status;
613 NSS_STATUS result;
614 struct winbindd_request request;
615 struct winbindd_response response;
617 if (!get_require_membership_sid())
619 if(error_string)
620 *error_string = smb_xstrdup("Can't get membership sid.");
621 return NT_STATUS_INVALID_PARAMETER;
624 ZERO_STRUCT(request);
625 ZERO_STRUCT(response);
627 if(username != NULL)
628 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
629 if(domain != NULL)
630 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
632 if(new_nt_pswd.length)
634 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
635 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
638 if(old_nt_hash_enc.length)
640 memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc, old_nt_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_nt_hash_enc));
641 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
644 if(new_lm_pswd.length)
646 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
647 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
650 if(old_lm_hash_enc.length)
652 memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc, old_lm_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_lm_hash_enc));
653 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
656 result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
658 /* Display response */
660 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
662 nt_status = NT_STATUS_UNSUCCESSFUL;
663 if (error_string)
664 *error_string = smb_xstrdup("Reading winbind reply failed!");
665 winbindd_free_response(&response);
666 return nt_status;
669 nt_status = (NT_STATUS(response.data.auth.nt_status));
670 if (!NT_STATUS_IS_OK(nt_status))
672 if (error_string)
673 *error_string = smb_xstrdup(response.data.auth.error_string);
674 winbindd_free_response(&response);
675 return nt_status;
678 winbindd_free_response(&response);
680 return nt_status;
683 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
684 TALLOC_CTX *mem_ctx,
685 void *server_returned_info,
686 const char *original_user_name,
687 uint32_t session_info_flags,
688 struct auth_session_info **session_info_out)
690 char *unix_username = (char *)server_returned_info;
691 struct auth_session_info *session_info = talloc_zero(mem_ctx, struct auth_session_info);
692 if (!session_info) {
693 return NT_STATUS_NO_MEMORY;
696 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
697 if (!session_info->unix_info) {
698 TALLOC_FREE(session_info);
699 return NT_STATUS_NO_MEMORY;
701 session_info->unix_info->unix_name = talloc_steal(session_info->unix_info, unix_username);
703 *session_info_out = session_info;
705 return NT_STATUS_OK;
708 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
709 TALLOC_CTX *mem_ctx,
710 struct smb_krb5_context *smb_krb5_context,
711 DATA_BLOB *pac_blob,
712 const char *princ_name,
713 const struct tsocket_address *remote_address,
714 uint32_t session_info_flags,
715 struct auth_session_info **session_info)
717 TALLOC_CTX *tmp_ctx;
718 struct PAC_LOGON_INFO *logon_info = NULL;
719 char *unixuser;
720 NTSTATUS status;
721 char *domain = NULL;
722 char *realm = NULL;
723 char *user = NULL;
724 char *p;
726 tmp_ctx = talloc_new(mem_ctx);
727 if (!tmp_ctx) {
728 return NT_STATUS_NO_MEMORY;
731 if (pac_blob) {
732 #ifdef HAVE_KRB5
733 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
734 NULL, NULL, 0, &logon_info);
735 #else
736 status = NT_STATUS_ACCESS_DENIED;
737 #endif
738 if (!NT_STATUS_IS_OK(status)) {
739 goto done;
743 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
745 p = strchr_m(princ_name, '@');
746 if (!p) {
747 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
748 princ_name));
749 return NT_STATUS_LOGON_FAILURE;
752 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
753 if (!user) {
754 return NT_STATUS_NO_MEMORY;
757 realm = talloc_strdup(talloc_tos(), p + 1);
758 if (!realm) {
759 return NT_STATUS_NO_MEMORY;
762 if (!strequal(realm, lp_realm())) {
763 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
764 if (!lp_allow_trusted_domains()) {
765 return NT_STATUS_LOGON_FAILURE;
769 if (logon_info && logon_info->info3.base.logon_domain.string) {
770 domain = talloc_strdup(mem_ctx,
771 logon_info->info3.base.logon_domain.string);
772 if (!domain) {
773 return NT_STATUS_NO_MEMORY;
775 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
776 } else {
778 /* If we have winbind running, we can (and must) shorten the
779 username by using the short netbios name. Otherwise we will
780 have inconsistent user names. With Kerberos, we get the
781 fully qualified realm, with ntlmssp we get the short
782 name. And even w2k3 does use ntlmssp if you for example
783 connect to an ip address. */
785 wbcErr wbc_status;
786 struct wbcDomainInfo *info = NULL;
788 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
789 realm));
791 wbc_status = wbcDomainInfo(realm, &info);
793 if (WBC_ERROR_IS_OK(wbc_status)) {
794 domain = talloc_strdup(mem_ctx,
795 info->short_name);
796 wbcFreeMemory(info);
797 } else {
798 DEBUG(3, ("Could not find short name: %s\n",
799 wbcErrorString(wbc_status)));
800 domain = talloc_strdup(mem_ctx, realm);
802 if (!domain) {
803 return NT_STATUS_NO_MEMORY;
805 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
808 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
809 if (!unixuser) {
810 status = NT_STATUS_NO_MEMORY;
811 goto done;
814 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
816 done:
817 TALLOC_FREE(tmp_ctx);
818 return status;
824 * Return the challenge as determined by the authentication subsystem
825 * @return an 8 byte random challenge
828 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
829 uint8_t chal[8])
831 if (auth_ctx->challenge.data.length == 8) {
832 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
833 auth_ctx->challenge.set_by));
834 memcpy(chal, auth_ctx->challenge.data.data, 8);
835 return NT_STATUS_OK;
838 if (!auth_ctx->challenge.set_by) {
839 generate_random_buffer(chal, 8);
841 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
842 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
843 auth_ctx->challenge.set_by = "random";
846 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
847 auth_ctx->challenge.set_by));
849 return NT_STATUS_OK;
853 * NTLM2 authentication modifies the effective challenge,
854 * @param challenge The new challenge value
856 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
858 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
859 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
861 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
862 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
864 return NT_STATUS_OK;
868 * Check the password on an NTLMSSP login.
870 * Return the session keys used on the connection.
873 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
874 TALLOC_CTX *mem_ctx,
875 const struct auth_usersupplied_info *user_info,
876 void **server_returned_info,
877 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
879 static const char zeros[16] = { 0, };
880 NTSTATUS nt_status;
881 char *error_string = NULL;
882 uint8 lm_key[8];
883 uint8 user_sess_key[16];
884 char *unix_name = NULL;
886 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
887 user_info->workstation_name,
888 &auth4_context->challenge.data,
889 &user_info->password.response.lanman,
890 &user_info->password.response.nt,
891 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
893 lm_key, user_sess_key,
894 &error_string, &unix_name);
896 if (NT_STATUS_IS_OK(nt_status)) {
897 if (memcmp(lm_key, zeros, 8) != 0) {
898 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
899 memcpy(lm_session_key->data, lm_key, 8);
900 memset(lm_session_key->data+8, '\0', 8);
903 if (memcmp(user_sess_key, zeros, 16) != 0) {
904 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
906 *server_returned_info = talloc_strdup(mem_ctx,
907 unix_name);
908 } else {
909 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
910 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
911 user_info->client.domain_name, user_info->client.account_name,
912 user_info->workstation_name,
913 error_string ? error_string : "unknown error (NULL)"));
916 SAFE_FREE(error_string);
917 SAFE_FREE(unix_name);
918 return nt_status;
921 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
922 TALLOC_CTX *mem_ctx,
923 const struct auth_usersupplied_info *user_info,
924 void **server_returned_info,
925 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
927 NTSTATUS nt_status;
928 struct samr_Password lm_pw, nt_pw;
930 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
932 nt_status = ntlm_password_check(mem_ctx,
933 true, true, 0,
934 &auth4_context->challenge.data,
935 &user_info->password.response.lanman,
936 &user_info->password.response.nt,
937 user_info->client.account_name,
938 user_info->client.account_name,
939 user_info->client.domain_name,
940 &lm_pw, &nt_pw, session_key, lm_session_key);
942 if (NT_STATUS_IS_OK(nt_status)) {
943 *server_returned_info = talloc_asprintf(mem_ctx,
944 "%s%c%s", user_info->client.domain_name,
945 *lp_winbind_separator(),
946 user_info->client.account_name);
947 } else {
948 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
949 user_info->client.domain_name, user_info->client.account_name,
950 user_info->workstation_name,
951 nt_errstr(nt_status)));
953 return nt_status;
956 static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state)
958 NTSTATUS status;
959 if ( (opt_username == NULL) || (opt_domain == NULL) ) {
960 status = NT_STATUS_UNSUCCESSFUL;
961 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
962 return NT_STATUS_INVALID_PARAMETER;
965 status = ntlmssp_client_start(NULL,
966 lp_netbios_name(),
967 lp_workgroup(),
968 lp_client_ntlmv2_auth(),
969 client_ntlmssp_state);
971 if (!NT_STATUS_IS_OK(status)) {
972 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
973 nt_errstr(status)));
974 TALLOC_FREE(*client_ntlmssp_state);
975 return status;
978 status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
980 if (!NT_STATUS_IS_OK(status)) {
981 DEBUG(1, ("Could not set username: %s\n",
982 nt_errstr(status)));
983 TALLOC_FREE(*client_ntlmssp_state);
984 return status;
987 status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
989 if (!NT_STATUS_IS_OK(status)) {
990 DEBUG(1, ("Could not set domain: %s\n",
991 nt_errstr(status)));
992 TALLOC_FREE(*client_ntlmssp_state);
993 return status;
996 if (opt_password) {
997 status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
999 if (!NT_STATUS_IS_OK(status)) {
1000 DEBUG(1, ("Could not set password: %s\n",
1001 nt_errstr(status)));
1002 TALLOC_FREE(*client_ntlmssp_state);
1003 return status;
1007 return NT_STATUS_OK;
1010 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1012 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1013 if (auth4_context == NULL) {
1014 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1015 return NULL;
1017 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1018 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1019 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1020 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1021 if (local_pw) {
1022 auth4_context->check_ntlm_password = local_pw_check;
1023 } else {
1024 auth4_context->check_ntlm_password = winbind_pw_check;
1026 auth4_context->private_data = NULL;
1027 return auth4_context;
1030 static NTSTATUS ntlm_auth_start_ntlmssp_server(TALLOC_CTX *mem_ctx,
1031 struct loadparm_context *lp_ctx,
1032 struct gensec_security **gensec_security_out)
1034 struct gensec_security *gensec_security;
1035 NTSTATUS nt_status;
1037 TALLOC_CTX *tmp_ctx;
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 gensec_settings->backends = talloc_zero_array(gensec_settings,
1083 struct gensec_security_ops *, 4);
1085 if (gensec_settings->backends == NULL) {
1086 TALLOC_FREE(tmp_ctx);
1087 return NT_STATUS_NO_MEMORY;
1090 gensec_init();
1092 /* These need to be in priority order, krb5 before NTLMSSP */
1093 #if defined(HAVE_KRB5)
1094 gensec_settings->backends[idx++] = &gensec_gse_krb5_security_ops;
1095 #endif
1097 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1099 gensec_settings->backends[idx++] = gensec_security_by_oid(NULL,
1100 GENSEC_OID_SPNEGO);
1103 * This is anonymous for now, because we just use it
1104 * to set the kerberos state at the moment
1106 server_credentials = cli_credentials_init_anon(tmp_ctx);
1107 if (!server_credentials) {
1108 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1109 return NT_STATUS_NO_MEMORY;
1112 cli_credentials_set_conf(server_credentials, lp_ctx);
1114 if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1115 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1116 } else {
1117 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1120 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1121 auth4_context, &gensec_security);
1123 if (!NT_STATUS_IS_OK(nt_status)) {
1124 TALLOC_FREE(tmp_ctx);
1125 return nt_status;
1128 gensec_set_credentials(gensec_security, server_credentials);
1130 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
1131 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
1133 talloc_unlink(tmp_ctx, lp_ctx);
1134 talloc_unlink(tmp_ctx, server_credentials);
1135 talloc_unlink(tmp_ctx, gensec_settings);
1136 talloc_unlink(tmp_ctx, auth4_context);
1138 nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP);
1139 if (!NT_STATUS_IS_OK(nt_status)) {
1140 TALLOC_FREE(tmp_ctx);
1141 return nt_status;
1144 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1145 TALLOC_FREE(tmp_ctx);
1146 return NT_STATUS_OK;
1149 /*******************************************************************
1150 Used by firefox to drive NTLM auth to IIS servers.
1151 *******************************************************************/
1153 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
1154 DATA_BLOB *reply)
1156 struct winbindd_request wb_request;
1157 struct winbindd_response wb_response;
1158 int ctrl = 0;
1159 NSS_STATUS result;
1161 /* get winbindd to do the ntlmssp step on our behalf */
1162 ZERO_STRUCT(wb_request);
1163 ZERO_STRUCT(wb_response);
1166 * This is tricky here. If we set krb5_auth in pam_winbind.conf
1167 * creds for users in trusted domain will be stored the winbindd
1168 * child of the trusted domain. If we ask the primary domain for
1169 * ntlm_ccache_auth, it will fail. So, we have to ask the trusted
1170 * domain's child for ccache_ntlm_auth. that is to say, we have to
1171 * set WBFLAG_PAM_CONTACT_TRUSTDOM in request.flags.
1173 ctrl = get_pam_winbind_config();
1175 if (ctrl & WINBIND_KRB5_AUTH) {
1176 wb_request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1179 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
1180 "%s%c%s", opt_domain, winbind_separator(), opt_username);
1181 wb_request.data.ccache_ntlm_auth.uid = geteuid();
1182 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
1183 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
1184 wb_request.extra_len = initial_msg.length + challenge_msg.length;
1186 if (wb_request.extra_len > 0) {
1187 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
1188 if (wb_request.extra_data.data == NULL) {
1189 return NT_STATUS_NO_MEMORY;
1192 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
1193 memcpy(wb_request.extra_data.data + initial_msg.length,
1194 challenge_msg.data, challenge_msg.length);
1197 result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
1198 SAFE_FREE(wb_request.extra_data.data);
1200 if (result != NSS_STATUS_SUCCESS) {
1201 winbindd_free_response(&wb_response);
1202 return NT_STATUS_UNSUCCESSFUL;
1205 if (reply) {
1206 *reply = data_blob(wb_response.extra_data.data,
1207 wb_response.data.ccache_ntlm_auth.auth_blob_len);
1208 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
1209 reply->data == NULL) {
1210 winbindd_free_response(&wb_response);
1211 return NT_STATUS_NO_MEMORY;
1215 winbindd_free_response(&wb_response);
1216 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1219 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1220 struct loadparm_context *lp_ctx,
1221 struct ntlm_auth_state *state,
1222 char *buf, int length, void **private2)
1224 DATA_BLOB request, reply;
1225 NTSTATUS nt_status;
1227 if (!opt_username || !*opt_username) {
1228 x_fprintf(x_stderr, "username must be specified!\n\n");
1229 exit(1);
1232 if (strlen(buf) < 2) {
1233 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1234 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1235 return;
1238 if (strlen(buf) > 3) {
1239 if(strncmp(buf, "SF ", 3) == 0) {
1240 DEBUG(10, ("Looking for flags to negotiate\n"));
1241 talloc_free(state->want_feature_list);
1242 state->want_feature_list = talloc_strdup(state->mem_ctx,
1243 buf+3);
1244 x_fprintf(x_stdout, "OK\n");
1245 return;
1247 request = base64_decode_data_blob(buf + 3);
1248 } else {
1249 request = data_blob_null;
1252 if (strncmp(buf, "PW ", 3) == 0) {
1253 /* We asked for a password and obviously got it :-) */
1255 opt_password = SMB_STRNDUP((const char *)request.data,
1256 request.length);
1258 if (opt_password == NULL) {
1259 DEBUG(1, ("Out of memory\n"));
1260 x_fprintf(x_stdout, "BH Out of memory\n");
1261 data_blob_free(&request);
1262 return;
1265 x_fprintf(x_stdout, "OK\n");
1266 data_blob_free(&request);
1267 return;
1270 if (!state->ntlmssp_state && use_cached_creds) {
1271 /* check whether cached credentials are usable. */
1272 DATA_BLOB empty_blob = data_blob_null;
1274 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
1275 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1276 /* failed to use cached creds */
1277 use_cached_creds = False;
1281 if (opt_password == NULL && !use_cached_creds) {
1282 /* Request a password from the calling process. After
1283 sending it, the calling process should retry asking for the
1284 negotiate. */
1286 DEBUG(10, ("Requesting password\n"));
1287 x_fprintf(x_stdout, "PW\n");
1288 return;
1291 if (strncmp(buf, "YR", 2) == 0) {
1292 TALLOC_FREE(state->ntlmssp_state);
1293 state->cli_state = CLIENT_INITIAL;
1294 } else if (strncmp(buf, "TT", 2) == 0) {
1295 /* No special preprocessing required */
1296 } else if (strncmp(buf, "GF", 2) == 0) {
1297 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
1299 if(state->cli_state == CLIENT_FINISHED) {
1300 x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
1302 else {
1303 x_fprintf(x_stdout, "BH\n");
1306 data_blob_free(&request);
1307 return;
1308 } else if (strncmp(buf, "GK", 2) == 0 ) {
1309 DEBUG(10, ("Requested session key\n"));
1311 if(state->cli_state == CLIENT_FINISHED) {
1312 char *key64 = base64_encode_data_blob(state->mem_ctx,
1313 state->session_key);
1314 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
1315 TALLOC_FREE(key64);
1317 else {
1318 x_fprintf(x_stdout, "BH\n");
1321 data_blob_free(&request);
1322 return;
1323 } else {
1324 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1325 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1326 return;
1329 if (!state->ntlmssp_state) {
1330 nt_status = ntlm_auth_start_ntlmssp_client(
1331 &state->ntlmssp_state);
1332 if (!NT_STATUS_IS_OK(nt_status)) {
1333 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1334 return;
1336 ntlmssp_want_feature_list(state->ntlmssp_state,
1337 state->want_feature_list);
1338 state->initial_message = data_blob_null;
1341 DEBUG(10, ("got NTLMSSP packet:\n"));
1342 dump_data(10, request.data, request.length);
1344 if (use_cached_creds && !opt_password &&
1345 (state->cli_state == CLIENT_RESPONSE)) {
1346 nt_status = do_ccache_ntlm_auth(state->initial_message, request,
1347 &reply);
1348 } else {
1349 nt_status = ntlmssp_update(state->ntlmssp_state, request,
1350 &reply);
1353 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1354 char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
1355 reply);
1356 if (state->cli_state == CLIENT_INITIAL) {
1357 x_fprintf(x_stdout, "YR %s\n", reply_base64);
1358 state->initial_message = reply;
1359 state->cli_state = CLIENT_RESPONSE;
1360 } else {
1361 x_fprintf(x_stdout, "KK %s\n", reply_base64);
1362 data_blob_free(&reply);
1364 TALLOC_FREE(reply_base64);
1365 DEBUG(10, ("NTLMSSP challenge\n"));
1366 } else if (NT_STATUS_IS_OK(nt_status)) {
1367 char *reply_base64 = base64_encode_data_blob(talloc_tos(),
1368 reply);
1369 x_fprintf(x_stdout, "AF %s\n", reply_base64);
1370 TALLOC_FREE(reply_base64);
1372 if(state->have_session_key)
1373 data_blob_free(&state->session_key);
1375 state->session_key = data_blob(
1376 state->ntlmssp_state->session_key.data,
1377 state->ntlmssp_state->session_key.length);
1378 state->neg_flags = state->ntlmssp_state->neg_flags;
1379 state->have_session_key = true;
1381 DEBUG(10, ("NTLMSSP OK!\n"));
1382 state->cli_state = CLIENT_FINISHED;
1383 TALLOC_FREE(state->ntlmssp_state);
1384 } else {
1385 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1386 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
1387 state->cli_state = CLIENT_ERROR;
1388 TALLOC_FREE(state->ntlmssp_state);
1391 data_blob_free(&request);
1394 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1395 struct loadparm_context *lp_ctx,
1396 struct ntlm_auth_state *state,
1397 char *buf, int length, void **private2)
1399 char *user, *pass;
1400 user=buf;
1402 pass=(char *)memchr(buf,' ',length);
1403 if (!pass) {
1404 DEBUG(2, ("Password not found. Denying access\n"));
1405 x_fprintf(x_stdout, "ERR\n");
1406 return;
1408 *pass='\0';
1409 pass++;
1411 if (state->helper_mode == SQUID_2_5_BASIC) {
1412 rfc1738_unescape(user);
1413 rfc1738_unescape(pass);
1416 if (check_plaintext_auth(user, pass, False)) {
1417 x_fprintf(x_stdout, "OK\n");
1418 } else {
1419 x_fprintf(x_stdout, "ERR\n");
1423 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1424 struct loadparm_context *lp_ctx,
1425 char *buf, int length, void **private1)
1427 DATA_BLOB in;
1428 DATA_BLOB out = data_blob(NULL, 0);
1429 char *out_base64 = NULL;
1430 const char *reply_arg = NULL;
1431 struct gensec_ntlm_state {
1432 struct gensec_security *gensec_state;
1433 const char *set_password;
1435 struct gensec_ntlm_state *state;
1437 NTSTATUS nt_status;
1438 bool first = false;
1439 const char *reply_code;
1440 struct cli_credentials *creds;
1442 static char *want_feature_list = NULL;
1443 static DATA_BLOB session_key;
1445 TALLOC_CTX *mem_ctx;
1447 if (*private1) {
1448 state = (struct gensec_ntlm_state *)*private1;
1449 } else {
1450 state = talloc_zero(NULL, struct gensec_ntlm_state);
1451 if (!state) {
1452 x_fprintf(x_stdout, "BH No Memory\n");
1453 exit(1);
1455 *private1 = state;
1456 if (opt_password) {
1457 state->set_password = opt_password;
1461 if (strlen(buf) < 2) {
1462 DEBUG(1, ("query [%s] invalid", buf));
1463 x_fprintf(x_stdout, "BH Query invalid\n");
1464 return;
1467 if (strlen(buf) > 3) {
1468 if(strncmp(buf, "SF ", 3) == 0) {
1469 DEBUG(10, ("Setting flags to negotiate\n"));
1470 talloc_free(want_feature_list);
1471 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1472 x_fprintf(x_stdout, "OK\n");
1473 return;
1475 in = base64_decode_data_blob(buf + 3);
1476 } else {
1477 in = data_blob(NULL, 0);
1480 if (strncmp(buf, "YR", 2) == 0) {
1481 if (state->gensec_state) {
1482 talloc_free(state->gensec_state);
1483 state->gensec_state = NULL;
1485 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1486 /* Just return BH, like ntlm_auth from Samba 3 does. */
1487 x_fprintf(x_stdout, "BH Command expected\n");
1488 data_blob_free(&in);
1489 return;
1490 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1491 (strncmp(buf, "KK ", 3) != 0) &&
1492 (strncmp(buf, "AF ", 3) != 0) &&
1493 (strncmp(buf, "NA ", 3) != 0) &&
1494 (strncmp(buf, "UG", 2) != 0) &&
1495 (strncmp(buf, "PW ", 3) != 0) &&
1496 (strncmp(buf, "GK", 2) != 0) &&
1497 (strncmp(buf, "GF", 2) != 0)) {
1498 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1499 x_fprintf(x_stdout, "BH SPNEGO request invalid prefix\n");
1500 data_blob_free(&in);
1501 return;
1504 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1506 /* setup gensec */
1507 if (!(state->gensec_state)) {
1508 switch (stdio_helper_mode) {
1509 case GSS_SPNEGO_CLIENT:
1510 case NTLMSSP_CLIENT_1:
1511 /* setup the client side */
1513 nt_status = gensec_client_start(NULL, &state->gensec_state,
1514 lpcfg_gensec_settings(NULL, lp_ctx));
1515 if (!NT_STATUS_IS_OK(nt_status)) {
1516 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1517 talloc_free(mem_ctx);
1518 return;
1521 creds = cli_credentials_init(state->gensec_state);
1522 cli_credentials_set_conf(creds, lp_ctx);
1523 if (opt_username) {
1524 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1526 if (opt_domain) {
1527 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1529 if (state->set_password) {
1530 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1531 } else {
1532 cli_credentials_set_password_callback(creds, get_password);
1534 if (opt_workstation) {
1535 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1538 gensec_set_credentials(state->gensec_state, creds);
1540 break;
1541 case GSS_SPNEGO_SERVER:
1542 case SQUID_2_5_NTLMSSP:
1544 nt_status = ntlm_auth_start_ntlmssp_server(state, lp_ctx,
1545 &state->gensec_state);
1546 if (!NT_STATUS_IS_OK(nt_status)) {
1547 x_fprintf(x_stdout, "BH GENSEC mech failed to start: %s\n", nt_errstr(nt_status));
1548 talloc_free(mem_ctx);
1549 return;
1551 break;
1553 default:
1554 talloc_free(mem_ctx);
1555 abort();
1558 gensec_want_feature_list(state->gensec_state, want_feature_list);
1560 switch (stdio_helper_mode) {
1561 case GSS_SPNEGO_CLIENT:
1562 case GSS_SPNEGO_SERVER:
1563 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1564 if (!in.length) {
1565 first = true;
1567 break;
1568 case NTLMSSP_CLIENT_1:
1569 if (!in.length) {
1570 first = true;
1572 /* fall through */
1573 case SQUID_2_5_NTLMSSP:
1574 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1575 break;
1576 default:
1577 talloc_free(mem_ctx);
1578 abort();
1581 if (!NT_STATUS_IS_OK(nt_status)) {
1582 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1583 x_fprintf(x_stdout, "BH GENSEC mech failed to start\n");
1584 talloc_free(mem_ctx);
1585 return;
1590 /* update */
1592 if (strncmp(buf, "PW ", 3) == 0) {
1593 state->set_password = talloc_strndup(state,
1594 (const char *)in.data,
1595 in.length);
1597 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1598 state->set_password,
1599 CRED_SPECIFIED);
1600 x_fprintf(x_stdout, "OK\n");
1601 data_blob_free(&in);
1602 talloc_free(mem_ctx);
1603 return;
1606 if (strncmp(buf, "GK", 2) == 0) {
1607 char *base64_key;
1608 DEBUG(10, ("Requested session key\n"));
1609 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1610 if(!NT_STATUS_IS_OK(nt_status)) {
1611 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1612 x_fprintf(x_stdout, "BH No session key\n");
1613 talloc_free(mem_ctx);
1614 return;
1615 } else {
1616 base64_key = base64_encode_data_blob(state, session_key);
1617 x_fprintf(x_stdout, "GK %s\n", base64_key);
1618 talloc_free(base64_key);
1620 talloc_free(mem_ctx);
1621 return;
1624 if (stdio_helper_mode == SQUID_2_5_NTLMSSP && strncmp(buf, "GF", 2) == 0) {
1625 uint32_t neg_flags;
1627 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1629 DEBUG(10, ("Requested negotiated feature flags\n"));
1630 x_fprintf(x_stdout, "GF 0x%08x\n", neg_flags);
1631 return;
1634 nt_status = gensec_update(state->gensec_state, mem_ctx, NULL, in, &out);
1636 /* don't leak 'bad password'/'no such user' info to the network client */
1637 nt_status = nt_status_squash(nt_status);
1639 if (out.length) {
1640 out_base64 = base64_encode_data_blob(mem_ctx, out);
1641 } else {
1642 out_base64 = NULL;
1645 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1646 reply_arg = "*";
1647 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1648 reply_code = "YR";
1649 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1650 reply_code = "KK";
1651 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1652 reply_code = "TT";
1653 } else {
1654 abort();
1658 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1659 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1660 reply_arg = nt_errstr(nt_status);
1661 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1662 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1663 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1664 reply_arg = nt_errstr(nt_status);
1665 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1666 } else if (!NT_STATUS_IS_OK(nt_status)) {
1667 reply_code = "NA";
1668 reply_arg = nt_errstr(nt_status);
1669 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1670 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1671 struct auth_session_info *session_info;
1673 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1674 if (!NT_STATUS_IS_OK(nt_status)) {
1675 reply_code = "BH Failed to retrive session info";
1676 reply_arg = nt_errstr(nt_status);
1677 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1678 } else {
1680 reply_code = "AF";
1681 reply_arg = session_info->unix_info->unix_name;
1682 talloc_free(session_info);
1684 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1685 reply_code = "AF";
1686 reply_arg = out_base64;
1687 } else {
1688 abort();
1691 switch (stdio_helper_mode) {
1692 case GSS_SPNEGO_SERVER:
1693 x_fprintf(x_stdout, "%s %s %s\n", reply_code,
1694 out_base64 ? out_base64 : "*",
1695 reply_arg ? reply_arg : "*");
1696 break;
1697 default:
1698 if (out_base64) {
1699 x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
1700 } else if (reply_arg) {
1701 x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
1702 } else {
1703 x_fprintf(x_stdout, "%s\n", reply_code);
1707 talloc_free(mem_ctx);
1708 return;
1711 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1712 struct loadparm_context *lp_ctx,
1713 struct ntlm_auth_state *state,
1714 char *buf, int length, void **private2)
1716 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1717 return;
1720 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1721 struct loadparm_context *lp_ctx,
1722 struct ntlm_auth_state *state,
1723 char *buf, int length, void **private2)
1725 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1726 return;
1729 static struct ntlmssp_state *client_ntlmssp_state = NULL;
1731 static bool manage_client_ntlmssp_init(struct spnego_data spnego)
1733 NTSTATUS status;
1734 DATA_BLOB null_blob = data_blob_null;
1735 DATA_BLOB to_server;
1736 char *to_server_base64;
1737 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1738 TALLOC_CTX *ctx = talloc_tos();
1740 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1742 if (client_ntlmssp_state != NULL) {
1743 DEBUG(1, ("Request for initial SPNEGO request where "
1744 "we already have a state\n"));
1745 return False;
1748 if (!client_ntlmssp_state) {
1749 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1750 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1751 return False;
1756 if (opt_password == NULL) {
1758 /* Request a password from the calling process. After
1759 sending it, the calling process should retry with
1760 the negTokenInit. */
1762 DEBUG(10, ("Requesting password\n"));
1763 x_fprintf(x_stdout, "PW\n");
1764 return True;
1767 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1768 spnego.negTokenInit.mechTypes = my_mechs;
1769 spnego.negTokenInit.reqFlags = data_blob_null;
1770 spnego.negTokenInit.reqFlagsPadding = 0;
1771 spnego.negTokenInit.mechListMIC = null_blob;
1773 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1774 &spnego.negTokenInit.mechToken);
1776 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1777 NT_STATUS_IS_OK(status)) ) {
1778 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1779 nt_errstr(status)));
1780 TALLOC_FREE(client_ntlmssp_state);
1781 return False;
1784 spnego_write_data(ctx, &to_server, &spnego);
1785 data_blob_free(&spnego.negTokenInit.mechToken);
1787 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1788 data_blob_free(&to_server);
1789 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1790 TALLOC_FREE(to_server_base64);
1791 return True;
1794 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
1796 NTSTATUS status;
1797 DATA_BLOB null_blob = data_blob_null;
1798 DATA_BLOB request;
1799 DATA_BLOB to_server;
1800 char *to_server_base64;
1801 TALLOC_CTX *ctx = talloc_tos();
1803 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1805 if (client_ntlmssp_state == NULL) {
1806 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1807 x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
1808 return;
1811 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1812 x_fprintf(x_stdout, "NA\n");
1813 TALLOC_FREE(client_ntlmssp_state);
1814 return;
1817 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1818 x_fprintf(x_stdout, "AF\n");
1819 TALLOC_FREE(client_ntlmssp_state);
1820 return;
1823 status = ntlmssp_update(client_ntlmssp_state,
1824 spnego.negTokenTarg.responseToken,
1825 &request);
1827 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
1828 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED or OK from "
1829 "ntlmssp_client_update, got: %s\n",
1830 nt_errstr(status)));
1831 x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
1832 "ntlmssp_client_update\n");
1833 data_blob_free(&request);
1834 TALLOC_FREE(client_ntlmssp_state);
1835 return;
1838 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1839 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1840 spnego.negTokenTarg.supportedMech = (const char *)OID_NTLMSSP;
1841 spnego.negTokenTarg.responseToken = request;
1842 spnego.negTokenTarg.mechListMIC = null_blob;
1844 spnego_write_data(ctx, &to_server, &spnego);
1845 data_blob_free(&request);
1847 to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1848 data_blob_free(&to_server);
1849 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1850 TALLOC_FREE(to_server_base64);
1851 return;
1854 #ifdef HAVE_KRB5
1856 static bool manage_client_krb5_init(struct spnego_data spnego)
1858 char *principal;
1859 DATA_BLOB tkt, tkt_wrapped, to_server;
1860 DATA_BLOB session_key_krb5 = data_blob_null;
1861 struct spnego_data reply;
1862 char *reply_base64;
1863 int retval;
1865 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1866 ssize_t len;
1867 TALLOC_CTX *ctx = talloc_tos();
1869 principal = spnego.negTokenInit.targetPrincipal;
1871 /* We may not be allowed to use the server-supplied SPNEGO principal, or it may not have been supplied to us
1873 if (!lp_client_use_spnego_principal() || strequal(principal, ADS_IGNORE_PRINCIPAL)) {
1874 principal = NULL;
1877 if (principal == NULL &&
1878 opt_target_service && opt_target_hostname && !is_ipaddress(opt_target_hostname)) {
1879 DEBUG(3,("manage_client_krb5_init: using target "
1880 "hostname not SPNEGO principal\n"));
1882 principal = kerberos_get_principal_from_service_hostname(talloc_tos(),
1883 opt_target_service,
1884 opt_target_hostname,
1885 lp_realm());
1887 if (!principal) {
1888 return false;
1891 DEBUG(3,("manage_client_krb5_init: guessed "
1892 "server principal=%s\n",
1893 principal ? principal : "<null>"));
1896 if (principal == NULL) {
1897 DEBUG(3,("manage_client_krb5_init: could not guess server principal\n"));
1898 return false;
1901 retval = cli_krb5_get_ticket(ctx, principal, 0,
1902 &tkt, &session_key_krb5,
1903 0, NULL, NULL, NULL);
1904 if (retval) {
1905 char *user = NULL;
1907 /* Let's try to first get the TGT, for that we need a
1908 password. */
1910 if (opt_password == NULL) {
1911 DEBUG(10, ("Requesting password\n"));
1912 x_fprintf(x_stdout, "PW\n");
1913 return True;
1916 user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
1917 if (!user) {
1918 return false;
1921 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1922 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1923 return False;
1926 retval = cli_krb5_get_ticket(ctx, principal, 0,
1927 &tkt, &session_key_krb5,
1928 0, NULL, NULL, NULL);
1929 if (retval) {
1930 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1931 return False;
1936 /* wrap that up in a nice GSS-API wrapping */
1937 tkt_wrapped = spnego_gen_krb5_wrap(ctx, tkt, TOK_ID_KRB_AP_REQ);
1939 data_blob_free(&session_key_krb5);
1941 ZERO_STRUCT(reply);
1943 reply.type = SPNEGO_NEG_TOKEN_INIT;
1944 reply.negTokenInit.mechTypes = my_mechs;
1945 reply.negTokenInit.reqFlags = data_blob_null;
1946 reply.negTokenInit.reqFlagsPadding = 0;
1947 reply.negTokenInit.mechToken = tkt_wrapped;
1948 reply.negTokenInit.mechListMIC = data_blob_null;
1950 len = spnego_write_data(ctx, &to_server, &reply);
1951 data_blob_free(&tkt);
1953 if (len == -1) {
1954 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1955 return False;
1958 reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1959 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1961 TALLOC_FREE(reply_base64);
1962 data_blob_free(&to_server);
1963 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1964 return True;
1967 static void manage_client_krb5_targ(struct spnego_data spnego)
1969 switch (spnego.negTokenTarg.negResult) {
1970 case SPNEGO_ACCEPT_INCOMPLETE:
1971 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1972 x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
1973 "ACCEPT_INCOMPLETE\n");
1974 break;
1975 case SPNEGO_ACCEPT_COMPLETED:
1976 DEBUG(10, ("Accept completed\n"));
1977 x_fprintf(x_stdout, "AF\n");
1978 break;
1979 case SPNEGO_REJECT:
1980 DEBUG(10, ("Rejected\n"));
1981 x_fprintf(x_stdout, "NA\n");
1982 break;
1983 default:
1984 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1985 x_fprintf(x_stdout, "AF\n");
1989 #endif
1991 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1992 struct loadparm_context *lp_ctx,
1993 struct ntlm_auth_state *state,
1994 char *buf, int length, void **private2)
1996 DATA_BLOB request;
1997 struct spnego_data spnego;
1998 ssize_t len;
1999 TALLOC_CTX *ctx = talloc_tos();
2001 if (!opt_username || !*opt_username) {
2002 x_fprintf(x_stderr, "username must be specified!\n\n");
2003 exit(1);
2006 if (strlen(buf) <= 3) {
2007 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
2008 x_fprintf(x_stdout, "BH SPNEGO query too short\n");
2009 return;
2012 request = base64_decode_data_blob(buf+3);
2014 if (strncmp(buf, "PW ", 3) == 0) {
2016 /* We asked for a password and obviously got it :-) */
2018 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
2020 if (opt_password == NULL) {
2021 DEBUG(1, ("Out of memory\n"));
2022 x_fprintf(x_stdout, "BH Out of memory\n");
2023 data_blob_free(&request);
2024 return;
2027 x_fprintf(x_stdout, "OK\n");
2028 data_blob_free(&request);
2029 return;
2032 if ( (strncmp(buf, "TT ", 3) != 0) &&
2033 (strncmp(buf, "AF ", 3) != 0) &&
2034 (strncmp(buf, "NA ", 3) != 0) ) {
2035 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
2036 x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
2037 data_blob_free(&request);
2038 return;
2041 /* So we got a server challenge to generate a SPNEGO
2042 client-to-server request... */
2044 len = spnego_read_data(ctx, request, &spnego);
2045 data_blob_free(&request);
2047 if (len == -1) {
2048 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
2049 x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
2050 return;
2053 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
2055 /* The server offers a list of mechanisms */
2057 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
2059 while (*mechType != NULL) {
2061 #ifdef HAVE_KRB5
2062 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
2063 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
2064 if (manage_client_krb5_init(spnego))
2065 goto out;
2067 #endif
2069 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
2070 if (manage_client_ntlmssp_init(spnego))
2071 goto out;
2074 mechType++;
2077 DEBUG(1, ("Server offered no compatible mechanism\n"));
2078 x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
2079 return;
2082 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
2084 if (spnego.negTokenTarg.supportedMech == NULL) {
2085 /* On accept/reject Windows does not send the
2086 mechanism anymore. Handle that here and
2087 shut down the mechanisms. */
2089 switch (spnego.negTokenTarg.negResult) {
2090 case SPNEGO_ACCEPT_COMPLETED:
2091 x_fprintf(x_stdout, "AF\n");
2092 break;
2093 case SPNEGO_REJECT:
2094 x_fprintf(x_stdout, "NA\n");
2095 break;
2096 default:
2097 DEBUG(1, ("Got a negTokenTarg with no mech and an "
2098 "unknown negResult: %d\n",
2099 spnego.negTokenTarg.negResult));
2100 x_fprintf(x_stdout, "BH Got a negTokenTarg with"
2101 " no mech and an unknown "
2102 "negResult\n");
2105 TALLOC_FREE(client_ntlmssp_state);
2106 goto out;
2109 if (strcmp(spnego.negTokenTarg.supportedMech,
2110 OID_NTLMSSP) == 0) {
2111 manage_client_ntlmssp_targ(spnego);
2112 goto out;
2115 #if HAVE_KRB5
2116 if (strcmp(spnego.negTokenTarg.supportedMech,
2117 OID_KERBEROS5_OLD) == 0) {
2118 manage_client_krb5_targ(spnego);
2119 goto out;
2121 #endif
2125 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
2126 x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
2127 return;
2129 out:
2130 spnego_free_data(&spnego);
2131 return;
2134 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
2135 struct loadparm_context *lp_ctx,
2136 struct ntlm_auth_state *state,
2137 char *buf, int length, void **private2)
2139 char *request, *parameter;
2140 static DATA_BLOB challenge;
2141 static DATA_BLOB lm_response;
2142 static DATA_BLOB nt_response;
2143 static char *full_username;
2144 static char *username;
2145 static char *domain;
2146 static char *plaintext_password;
2147 static bool ntlm_server_1_user_session_key;
2148 static bool ntlm_server_1_lm_session_key;
2150 if (strequal(buf, ".")) {
2151 if (!full_username && !username) {
2152 x_fprintf(x_stdout, "Error: No username supplied!\n");
2153 } else if (plaintext_password) {
2154 /* handle this request as plaintext */
2155 if (!full_username) {
2156 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
2157 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
2158 return;
2161 if (check_plaintext_auth(full_username, plaintext_password, False)) {
2162 x_fprintf(x_stdout, "Authenticated: Yes\n");
2163 } else {
2164 x_fprintf(x_stdout, "Authenticated: No\n");
2166 } else if (!lm_response.data && !nt_response.data) {
2167 x_fprintf(x_stdout, "Error: No password supplied!\n");
2168 } else if (!challenge.data) {
2169 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
2170 } else {
2171 char *error_string = NULL;
2172 uchar lm_key[8];
2173 uchar user_session_key[16];
2174 uint32 flags = 0;
2176 if (full_username && !username) {
2177 fstring fstr_user;
2178 fstring fstr_domain;
2180 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
2181 /* username might be 'tainted', don't print into our new-line deleimianted stream */
2182 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
2184 SAFE_FREE(username);
2185 SAFE_FREE(domain);
2186 username = smb_xstrdup(fstr_user);
2187 domain = smb_xstrdup(fstr_domain);
2190 if (!domain) {
2191 domain = smb_xstrdup(get_winbind_domain());
2194 if (ntlm_server_1_lm_session_key)
2195 flags |= WBFLAG_PAM_LMKEY;
2197 if (ntlm_server_1_user_session_key)
2198 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2200 if (!NT_STATUS_IS_OK(
2201 contact_winbind_auth_crap(username,
2202 domain,
2203 lp_netbios_name(),
2204 &challenge,
2205 &lm_response,
2206 &nt_response,
2207 flags, 0,
2208 lm_key,
2209 user_session_key,
2210 &error_string,
2211 NULL))) {
2213 x_fprintf(x_stdout, "Authenticated: No\n");
2214 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
2215 } else {
2216 static char zeros[16];
2217 char *hex_lm_key;
2218 char *hex_user_session_key;
2220 x_fprintf(x_stdout, "Authenticated: Yes\n");
2222 if (ntlm_server_1_lm_session_key
2223 && (memcmp(zeros, lm_key,
2224 sizeof(lm_key)) != 0)) {
2225 hex_lm_key = hex_encode_talloc(NULL,
2226 (const unsigned char *)lm_key,
2227 sizeof(lm_key));
2228 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
2229 TALLOC_FREE(hex_lm_key);
2232 if (ntlm_server_1_user_session_key
2233 && (memcmp(zeros, user_session_key,
2234 sizeof(user_session_key)) != 0)) {
2235 hex_user_session_key = hex_encode_talloc(NULL,
2236 (const unsigned char *)user_session_key,
2237 sizeof(user_session_key));
2238 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
2239 TALLOC_FREE(hex_user_session_key);
2242 SAFE_FREE(error_string);
2244 /* clear out the state */
2245 challenge = data_blob_null;
2246 nt_response = data_blob_null;
2247 lm_response = data_blob_null;
2248 SAFE_FREE(full_username);
2249 SAFE_FREE(username);
2250 SAFE_FREE(domain);
2251 SAFE_FREE(plaintext_password);
2252 ntlm_server_1_user_session_key = False;
2253 ntlm_server_1_lm_session_key = False;
2254 x_fprintf(x_stdout, ".\n");
2256 return;
2259 request = buf;
2261 /* Indicates a base64 encoded structure */
2262 parameter = strstr_m(request, ":: ");
2263 if (!parameter) {
2264 parameter = strstr_m(request, ": ");
2266 if (!parameter) {
2267 DEBUG(0, ("Parameter not found!\n"));
2268 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2269 return;
2272 parameter[0] ='\0';
2273 parameter++;
2274 parameter[0] ='\0';
2275 parameter++;
2277 } else {
2278 parameter[0] ='\0';
2279 parameter++;
2280 parameter[0] ='\0';
2281 parameter++;
2282 parameter[0] ='\0';
2283 parameter++;
2285 base64_decode_inplace(parameter);
2288 if (strequal(request, "LANMAN-Challenge")) {
2289 challenge = strhex_to_data_blob(NULL, parameter);
2290 if (challenge.length != 8) {
2291 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
2292 parameter,
2293 (int)challenge.length);
2294 challenge = data_blob_null;
2296 } else if (strequal(request, "NT-Response")) {
2297 nt_response = strhex_to_data_blob(NULL, parameter);
2298 if (nt_response.length < 24) {
2299 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
2300 parameter,
2301 (int)nt_response.length);
2302 nt_response = data_blob_null;
2304 } else if (strequal(request, "LANMAN-Response")) {
2305 lm_response = strhex_to_data_blob(NULL, parameter);
2306 if (lm_response.length != 24) {
2307 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
2308 parameter,
2309 (int)lm_response.length);
2310 lm_response = data_blob_null;
2312 } else if (strequal(request, "Password")) {
2313 plaintext_password = smb_xstrdup(parameter);
2314 } else if (strequal(request, "NT-Domain")) {
2315 domain = smb_xstrdup(parameter);
2316 } else if (strequal(request, "Username")) {
2317 username = smb_xstrdup(parameter);
2318 } else if (strequal(request, "Full-Username")) {
2319 full_username = smb_xstrdup(parameter);
2320 } else if (strequal(request, "Request-User-Session-Key")) {
2321 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2322 } else if (strequal(request, "Request-LanMan-Session-Key")) {
2323 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2324 } else {
2325 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2329 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
2330 struct loadparm_context *lp_ctx,
2331 struct ntlm_auth_state *state,
2332 char *buf, int length, void **private2)
2334 char *request, *parameter;
2335 static DATA_BLOB new_nt_pswd;
2336 static DATA_BLOB old_nt_hash_enc;
2337 static DATA_BLOB new_lm_pswd;
2338 static DATA_BLOB old_lm_hash_enc;
2339 static char *full_username = NULL;
2340 static char *username = NULL;
2341 static char *domain = NULL;
2342 static char *newpswd = NULL;
2343 static char *oldpswd = NULL;
2345 if (strequal(buf, ".")) {
2346 if(newpswd && oldpswd) {
2347 uchar old_nt_hash[16];
2348 uchar old_lm_hash[16];
2349 uchar new_nt_hash[16];
2350 uchar new_lm_hash[16];
2352 new_nt_pswd = data_blob(NULL, 516);
2353 old_nt_hash_enc = data_blob(NULL, 16);
2355 /* Calculate the MD4 hash (NT compatible) of the
2356 * password */
2357 E_md4hash(oldpswd, old_nt_hash);
2358 E_md4hash(newpswd, new_nt_hash);
2360 /* E_deshash returns false for 'long'
2361 passwords (> 14 DOS chars).
2363 Therefore, don't send a buffer
2364 encrypted with the truncated hash
2365 (it could allow an even easier
2366 attack on the password)
2368 Likewise, obey the admin's restriction
2371 if (lp_client_lanman_auth() &&
2372 E_deshash(newpswd, new_lm_hash) &&
2373 E_deshash(oldpswd, old_lm_hash)) {
2374 new_lm_pswd = data_blob(NULL, 516);
2375 old_lm_hash_enc = data_blob(NULL, 16);
2376 encode_pw_buffer(new_lm_pswd.data, newpswd,
2377 STR_UNICODE);
2379 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
2380 E_old_pw_hash(new_nt_hash, old_lm_hash,
2381 old_lm_hash_enc.data);
2382 } else {
2383 new_lm_pswd.data = NULL;
2384 new_lm_pswd.length = 0;
2385 old_lm_hash_enc.data = NULL;
2386 old_lm_hash_enc.length = 0;
2389 encode_pw_buffer(new_nt_pswd.data, newpswd,
2390 STR_UNICODE);
2392 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
2393 E_old_pw_hash(new_nt_hash, old_nt_hash,
2394 old_nt_hash_enc.data);
2397 if (!full_username && !username) {
2398 x_fprintf(x_stdout, "Error: No username supplied!\n");
2399 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2400 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2401 x_fprintf(x_stdout, "Error: No NT or LM password "
2402 "blobs supplied!\n");
2403 } else {
2404 char *error_string = NULL;
2406 if (full_username && !username) {
2407 fstring fstr_user;
2408 fstring fstr_domain;
2410 if (!parse_ntlm_auth_domain_user(full_username,
2411 fstr_user,
2412 fstr_domain)) {
2413 /* username might be 'tainted', don't
2414 * print into our new-line
2415 * deleimianted stream */
2416 x_fprintf(x_stdout, "Error: Could not "
2417 "parse into domain and "
2418 "username\n");
2419 SAFE_FREE(username);
2420 username = smb_xstrdup(full_username);
2421 } else {
2422 SAFE_FREE(username);
2423 SAFE_FREE(domain);
2424 username = smb_xstrdup(fstr_user);
2425 domain = smb_xstrdup(fstr_domain);
2430 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2431 username, domain,
2432 new_nt_pswd,
2433 old_nt_hash_enc,
2434 new_lm_pswd,
2435 old_lm_hash_enc,
2436 &error_string))) {
2437 x_fprintf(x_stdout, "Password-Change: No\n");
2438 x_fprintf(x_stdout, "Password-Change-Error: "
2439 "%s\n.\n", error_string);
2440 } else {
2441 x_fprintf(x_stdout, "Password-Change: Yes\n");
2444 SAFE_FREE(error_string);
2446 /* clear out the state */
2447 new_nt_pswd = data_blob_null;
2448 old_nt_hash_enc = data_blob_null;
2449 new_lm_pswd = data_blob_null;
2450 old_nt_hash_enc = data_blob_null;
2451 SAFE_FREE(full_username);
2452 SAFE_FREE(username);
2453 SAFE_FREE(domain);
2454 SAFE_FREE(newpswd);
2455 SAFE_FREE(oldpswd);
2456 x_fprintf(x_stdout, ".\n");
2458 return;
2461 request = buf;
2463 /* Indicates a base64 encoded structure */
2464 parameter = strstr_m(request, ":: ");
2465 if (!parameter) {
2466 parameter = strstr_m(request, ": ");
2468 if (!parameter) {
2469 DEBUG(0, ("Parameter not found!\n"));
2470 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2471 return;
2474 parameter[0] ='\0';
2475 parameter++;
2476 parameter[0] ='\0';
2477 parameter++;
2478 } else {
2479 parameter[0] ='\0';
2480 parameter++;
2481 parameter[0] ='\0';
2482 parameter++;
2483 parameter[0] ='\0';
2484 parameter++;
2486 base64_decode_inplace(parameter);
2489 if (strequal(request, "new-nt-password-blob")) {
2490 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2491 if (new_nt_pswd.length != 516) {
2492 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2493 "(got %d bytes, expected 516)\n.\n",
2494 parameter,
2495 (int)new_nt_pswd.length);
2496 new_nt_pswd = data_blob_null;
2498 } else if (strequal(request, "old-nt-hash-blob")) {
2499 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2500 if (old_nt_hash_enc.length != 16) {
2501 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2502 "(got %d bytes, expected 16)\n.\n",
2503 parameter,
2504 (int)old_nt_hash_enc.length);
2505 old_nt_hash_enc = data_blob_null;
2507 } else if (strequal(request, "new-lm-password-blob")) {
2508 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2509 if (new_lm_pswd.length != 516) {
2510 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2511 "(got %d bytes, expected 516)\n.\n",
2512 parameter,
2513 (int)new_lm_pswd.length);
2514 new_lm_pswd = data_blob_null;
2517 else if (strequal(request, "old-lm-hash-blob")) {
2518 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2519 if (old_lm_hash_enc.length != 16)
2521 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2522 "(got %d bytes, expected 16)\n.\n",
2523 parameter,
2524 (int)old_lm_hash_enc.length);
2525 old_lm_hash_enc = data_blob_null;
2527 } else if (strequal(request, "nt-domain")) {
2528 domain = smb_xstrdup(parameter);
2529 } else if(strequal(request, "username")) {
2530 username = smb_xstrdup(parameter);
2531 } else if(strequal(request, "full-username")) {
2532 username = smb_xstrdup(parameter);
2533 } else if(strequal(request, "new-password")) {
2534 newpswd = smb_xstrdup(parameter);
2535 } else if (strequal(request, "old-password")) {
2536 oldpswd = smb_xstrdup(parameter);
2537 } else {
2538 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2542 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2543 struct loadparm_context *lp_ctx,
2544 struct ntlm_auth_state *state,
2545 stdio_helper_function fn, void **private2)
2547 char *buf;
2548 char tmp[INITIAL_BUFFER_SIZE+1];
2549 int length, buf_size = 0;
2550 char *c;
2552 buf = talloc_strdup(state->mem_ctx, "");
2553 if (!buf) {
2554 DEBUG(0, ("Failed to allocate input buffer.\n"));
2555 x_fprintf(x_stderr, "ERR\n");
2556 exit(1);
2559 do {
2561 /* this is not a typo - x_fgets doesn't work too well under
2562 * squid */
2563 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2564 if (ferror(stdin)) {
2565 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2566 "(%s)\n", ferror(stdin),
2567 strerror(ferror(stdin))));
2569 exit(1);
2571 exit(0);
2574 buf = talloc_strdup_append_buffer(buf, tmp);
2575 buf_size += INITIAL_BUFFER_SIZE;
2577 if (buf_size > MAX_BUFFER_SIZE) {
2578 DEBUG(2, ("Oversized message\n"));
2579 x_fprintf(x_stderr, "ERR\n");
2580 talloc_free(buf);
2581 return;
2584 c = strchr(buf, '\n');
2585 } while (c == NULL);
2587 *c = '\0';
2588 length = c-buf;
2590 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2592 if (buf[0] == '\0') {
2593 DEBUG(2, ("Invalid Request\n"));
2594 x_fprintf(x_stderr, "ERR\n");
2595 talloc_free(buf);
2596 return;
2599 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2600 talloc_free(buf);
2604 static void squid_stream(enum stdio_helper_mode stdio_mode,
2605 struct loadparm_context *lp_ctx,
2606 stdio_helper_function fn) {
2607 TALLOC_CTX *mem_ctx;
2608 struct ntlm_auth_state *state;
2610 /* initialize FDescs */
2611 x_setbuf(x_stdout, NULL);
2612 x_setbuf(x_stderr, NULL);
2614 mem_ctx = talloc_init("ntlm_auth");
2615 if (!mem_ctx) {
2616 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2617 x_fprintf(x_stderr, "ERR\n");
2618 exit(1);
2621 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2622 if (!state) {
2623 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2624 x_fprintf(x_stderr, "ERR\n");
2625 exit(1);
2628 state->mem_ctx = mem_ctx;
2629 state->helper_mode = stdio_mode;
2631 while(1) {
2632 TALLOC_CTX *frame = talloc_stackframe();
2633 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2634 TALLOC_FREE(frame);
2639 /* Authenticate a user with a challenge/response */
2641 static bool check_auth_crap(void)
2643 NTSTATUS nt_status;
2644 uint32 flags = 0;
2645 char lm_key[8];
2646 char user_session_key[16];
2647 char *hex_lm_key;
2648 char *hex_user_session_key;
2649 char *error_string;
2650 static uint8 zeros[16];
2652 x_setbuf(x_stdout, NULL);
2654 if (request_lm_key)
2655 flags |= WBFLAG_PAM_LMKEY;
2657 if (request_user_session_key)
2658 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2660 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2662 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2663 opt_workstation,
2664 &opt_challenge,
2665 &opt_lm_response,
2666 &opt_nt_response,
2667 flags, 0,
2668 (unsigned char *)lm_key,
2669 (unsigned char *)user_session_key,
2670 &error_string, NULL);
2672 if (!NT_STATUS_IS_OK(nt_status)) {
2673 x_fprintf(x_stdout, "%s (0x%x)\n",
2674 error_string,
2675 NT_STATUS_V(nt_status));
2676 SAFE_FREE(error_string);
2677 return False;
2680 if (request_lm_key
2681 && (memcmp(zeros, lm_key,
2682 sizeof(lm_key)) != 0)) {
2683 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2684 sizeof(lm_key));
2685 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2686 TALLOC_FREE(hex_lm_key);
2688 if (request_user_session_key
2689 && (memcmp(zeros, user_session_key,
2690 sizeof(user_session_key)) != 0)) {
2691 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2692 sizeof(user_session_key));
2693 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2694 TALLOC_FREE(hex_user_session_key);
2697 return True;
2700 /* Main program */
2702 enum {
2703 OPT_USERNAME = 1000,
2704 OPT_DOMAIN,
2705 OPT_WORKSTATION,
2706 OPT_CHALLENGE,
2707 OPT_RESPONSE,
2708 OPT_LM,
2709 OPT_NT,
2710 OPT_PASSWORD,
2711 OPT_LM_KEY,
2712 OPT_USER_SESSION_KEY,
2713 OPT_DIAGNOSTICS,
2714 OPT_REQUIRE_MEMBERSHIP,
2715 OPT_USE_CACHED_CREDS,
2716 OPT_PAM_WINBIND_CONF,
2717 OPT_TARGET_SERVICE,
2718 OPT_TARGET_HOSTNAME
2721 int main(int argc, const char **argv)
2723 TALLOC_CTX *frame = talloc_stackframe();
2724 int opt;
2725 static const char *helper_protocol;
2726 static int diagnostics;
2728 static const char *hex_challenge;
2729 static const char *hex_lm_response;
2730 static const char *hex_nt_response;
2731 struct loadparm_context *lp_ctx;
2732 poptContext pc;
2734 /* NOTE: DO NOT change this interface without considering the implications!
2735 This is an external interface, which other programs will use to interact
2736 with this helper.
2739 /* We do not use single-letter command abbreviations, because they harm future
2740 interface stability. */
2742 struct poptOption long_options[] = {
2743 POPT_AUTOHELP
2744 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2745 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2746 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2747 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2748 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2749 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2750 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2751 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2752 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2753 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2754 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2755 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2756 OPT_DIAGNOSTICS,
2757 "Perform diagnostics on the authentication chain"},
2758 { "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" },
2759 { "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" },
2760 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2761 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2762 POPT_COMMON_CONFIGFILE
2763 POPT_COMMON_VERSION
2764 POPT_TABLEEND
2767 /* Samba client initialisation */
2768 load_case_tables();
2770 setup_logging("ntlm_auth", DEBUG_STDERR);
2772 /* Parse options */
2774 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2776 /* Parse command line options */
2778 if (argc == 1) {
2779 poptPrintHelp(pc, stderr, 0);
2780 return 1;
2783 while((opt = poptGetNextOpt(pc)) != -1) {
2784 /* Get generic config options like --configfile */
2787 poptFreeContext(pc);
2789 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2790 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2791 get_dyn_CONFIGFILE(), strerror(errno));
2792 exit(1);
2795 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2796 POPT_CONTEXT_KEEP_FIRST);
2798 while((opt = poptGetNextOpt(pc)) != -1) {
2799 switch (opt) {
2800 case OPT_CHALLENGE:
2801 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2802 if (opt_challenge.length != 8) {
2803 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2804 hex_challenge,
2805 (int)opt_challenge.length);
2806 exit(1);
2808 break;
2809 case OPT_LM:
2810 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2811 if (opt_lm_response.length != 24) {
2812 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2813 hex_lm_response,
2814 (int)opt_lm_response.length);
2815 exit(1);
2817 break;
2819 case OPT_NT:
2820 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2821 if (opt_nt_response.length < 24) {
2822 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2823 hex_nt_response,
2824 (int)opt_nt_response.length);
2825 exit(1);
2827 break;
2829 case OPT_REQUIRE_MEMBERSHIP:
2830 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2831 require_membership_of_sid = require_membership_of;
2833 break;
2837 if (opt_username) {
2838 char *domain = SMB_STRDUP(opt_username);
2839 char *p = strchr_m(domain, *lp_winbind_separator());
2840 if (p) {
2841 opt_username = p+1;
2842 *p = '\0';
2843 if (opt_domain && !strequal(opt_domain, domain)) {
2844 x_fprintf(x_stderr, "Domain specified in username (%s) "
2845 "doesn't match specified domain (%s)!\n\n",
2846 domain, opt_domain);
2847 poptPrintHelp(pc, stderr, 0);
2848 exit(1);
2850 opt_domain = domain;
2851 } else {
2852 SAFE_FREE(domain);
2856 /* Note: if opt_domain is "" then send no domain */
2857 if (opt_domain == NULL) {
2858 opt_domain = get_winbind_domain();
2861 if (opt_workstation == NULL) {
2862 opt_workstation = "";
2865 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2866 if (lp_ctx == NULL) {
2867 x_fprintf(x_stderr, "loadparm_init_s3() failed!\n");
2868 exit(1);
2871 if (helper_protocol) {
2872 int i;
2873 for (i=0; i<NUM_HELPER_MODES; i++) {
2874 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2875 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2876 exit(0);
2879 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2881 for (i=0; i<NUM_HELPER_MODES; i++) {
2882 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2885 exit(1);
2888 if (!opt_username || !*opt_username) {
2889 x_fprintf(x_stderr, "username must be specified!\n\n");
2890 poptPrintHelp(pc, stderr, 0);
2891 exit(1);
2894 if (opt_challenge.length) {
2895 if (!check_auth_crap()) {
2896 exit(1);
2898 exit(0);
2901 if (!opt_password) {
2902 opt_password = getpass("password: ");
2905 if (diagnostics) {
2906 if (!diagnose_ntlm_auth()) {
2907 return 1;
2909 } else {
2910 fstring user;
2912 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2913 if (!check_plaintext_auth(user, opt_password, True)) {
2914 return 1;
2918 /* Exit code */
2920 poptFreeContext(pc);
2921 TALLOC_FREE(frame);
2922 return 0;