ntlm_auth: Set ntlm_auth as the service_description into gensec
[Samba.git] / source3 / utils / ntlm_auth.c
blobc0b552153000e5036ef107e39b89aed5d305427a
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 "libcli/security/security.h"
31 #include "utils/ntlm_auth.h"
32 #include "../libcli/auth/libcli_auth.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 "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"
49 #include "lib/util/base64.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_gensec_request(enum stdio_helper_mode stdio_helper_mode,
104 struct loadparm_context *lp_ctx,
105 char *buf, int length, void **private1);
107 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
108 struct loadparm_context *lp_ctx,
109 struct ntlm_auth_state *state,
110 stdio_helper_function fn, void **private2);
112 static void manage_squid_basic_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_squid_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_client_ntlmssp_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_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_gss_spnego_client_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_server_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 void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
143 struct loadparm_context *lp_ctx,
144 struct ntlm_auth_state *state,
145 char *buf, int length, void **private2);
147 static const struct {
148 enum stdio_helper_mode mode;
149 const char *name;
150 stdio_helper_function fn;
151 } stdio_helper_protocols[] = {
152 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
153 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
154 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
155 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
156 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
157 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
158 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
159 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
160 { NUM_HELPER_MODES, NULL, NULL}
163 const char *opt_username;
164 const char *opt_domain;
165 const char *opt_workstation;
166 const char *opt_password;
167 static DATA_BLOB opt_challenge;
168 static DATA_BLOB opt_lm_response;
169 static DATA_BLOB opt_nt_response;
170 static int request_lm_key;
171 static int request_user_session_key;
172 static int use_cached_creds;
173 static int offline_logon;
174 static int opt_allow_mschapv2;
176 static const char *require_membership_of;
177 static const char *require_membership_of_sid;
178 static const char *opt_pam_winbind_conf;
180 const char *opt_target_service;
181 const char *opt_target_hostname;
184 /* This is a bit hairy, but the basic idea is to do a password callback
185 to the calling application. The callback comes from within gensec */
187 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
188 struct loadparm_context *lp_ctx,
189 struct ntlm_auth_state *state, char *buf, int length,
190 void **password)
192 DATA_BLOB in;
193 if (strlen(buf) < 2) {
194 DEBUG(1, ("query [%s] invalid", buf));
195 printf("BH Query invalid\n");
196 return;
199 if (strlen(buf) > 3) {
200 in = base64_decode_data_blob(buf + 3);
201 } else {
202 in = data_blob(NULL, 0);
205 if (strncmp(buf, "PW ", 3) == 0) {
207 *password = talloc_strndup(NULL,
208 (const char *)in.data, in.length);
210 if (*password == NULL) {
211 DEBUG(1, ("Out of memory\n"));
212 printf("BH Out of memory\n");
213 data_blob_free(&in);
214 return;
217 printf("OK\n");
218 data_blob_free(&in);
219 return;
221 DEBUG(1, ("Asked for (and expected) a password\n"));
222 printf("BH Expected a password\n");
223 data_blob_free(&in);
227 * Callback for password credentials. This is not async, and when
228 * GENSEC and the credentials code is made async, it will look rather
229 * different.
232 static const char *get_password(struct cli_credentials *credentials)
234 TALLOC_CTX *frame = talloc_stackframe();
235 char *password = NULL;
236 struct ntlm_auth_state *state;
238 state = talloc_zero(frame, struct ntlm_auth_state);
239 if (state == NULL) {
240 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
241 fprintf(stderr, "ERR\n");
242 exit(1);
245 state->mem_ctx = state;
247 /* Ask for a password */
248 printf("PW\n");
250 manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, state, manage_gensec_get_pw_request, (void **)&password);
251 talloc_steal(credentials, password);
252 TALLOC_FREE(frame);
253 return password;
257 * A limited set of features are defined with text strings as needed
258 * by ntlm_auth
261 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
263 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
264 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
265 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
267 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
268 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
269 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
271 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
272 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
273 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
275 if (in_list("NTLMSSP_FEATURE_CCACHE", feature_list, true)) {
276 DEBUG(10, ("want GENSEC_FEATURE_NTLM_CCACHE\n"));
277 gensec_want_feature(state, GENSEC_FEATURE_NTLM_CCACHE);
281 static char winbind_separator(void)
283 struct winbindd_response response;
284 static bool got_sep;
285 static char sep;
287 if (got_sep)
288 return sep;
290 ZERO_STRUCT(response);
292 /* Send off request */
294 if (winbindd_request_response(NULL, WINBINDD_INFO, NULL, &response) !=
295 NSS_STATUS_SUCCESS) {
296 d_fprintf(stderr, "could not obtain winbind separator!\n");
297 return *lp_winbind_separator();
300 sep = response.data.info.winbind_separator;
301 got_sep = True;
303 if (!sep) {
304 d_fprintf(stderr, "winbind separator was NULL!\n");
305 return *lp_winbind_separator();
308 return sep;
311 const char *get_winbind_domain(void)
313 struct winbindd_response response;
315 static fstring winbind_domain;
316 if (*winbind_domain) {
317 return winbind_domain;
320 ZERO_STRUCT(response);
322 /* Send off request */
324 if (winbindd_request_response(NULL, WINBINDD_DOMAIN_NAME, NULL, &response) !=
325 NSS_STATUS_SUCCESS) {
326 DEBUG(1, ("could not obtain winbind domain name!\n"));
327 return lp_workgroup();
330 fstrcpy(winbind_domain, response.data.domain_name);
332 return winbind_domain;
336 const char *get_winbind_netbios_name(void)
338 struct winbindd_response response;
340 static fstring winbind_netbios_name;
342 if (*winbind_netbios_name) {
343 return winbind_netbios_name;
346 ZERO_STRUCT(response);
348 /* Send off request */
350 if (winbindd_request_response(NULL, WINBINDD_NETBIOS_NAME, NULL, &response) !=
351 NSS_STATUS_SUCCESS) {
352 DEBUG(1, ("could not obtain winbind netbios name!\n"));
353 return lp_netbios_name();
356 fstrcpy(winbind_netbios_name, response.data.netbios_name);
358 return winbind_netbios_name;
362 DATA_BLOB get_challenge(void)
364 static DATA_BLOB chal;
365 if (opt_challenge.length)
366 return opt_challenge;
368 chal = data_blob(NULL, 8);
370 generate_random_buffer(chal.data, chal.length);
371 return chal;
374 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
375 form DOMAIN/user into a domain and a user */
377 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
378 fstring user)
381 char *p = strchr(domuser,winbind_separator());
383 if (!p) {
384 return False;
387 fstrcpy(user, p+1);
388 fstrcpy(domain, domuser);
389 domain[PTR_DIFF(p, domuser)] = 0;
390 return strupper_m(domain);
393 static bool get_require_membership_sid(void) {
394 struct winbindd_request request;
395 struct winbindd_response response;
397 if (!require_membership_of) {
398 return True;
401 if (require_membership_of_sid) {
402 return True;
405 /* Otherwise, ask winbindd for the name->sid request */
407 ZERO_STRUCT(request);
408 ZERO_STRUCT(response);
410 if (!parse_ntlm_auth_domain_user(require_membership_of,
411 request.data.name.dom_name,
412 request.data.name.name)) {
413 DEBUG(0, ("Could not parse %s into separate domain/name parts!\n",
414 require_membership_of));
415 return False;
418 if (winbindd_request_response(NULL, WINBINDD_LOOKUPNAME, &request, &response) !=
419 NSS_STATUS_SUCCESS) {
420 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
421 require_membership_of));
422 return False;
425 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
427 if (require_membership_of_sid)
428 return True;
430 return False;
434 * Get some configuration from pam_winbind.conf to see if we
435 * need to contact trusted domain
438 int get_pam_winbind_config()
440 int ctrl = 0;
441 struct tiniparser_dictionary *d = NULL;
443 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
444 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
447 d = tiniparser_load(opt_pam_winbind_conf);
449 if (!d) {
450 return 0;
453 if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
454 ctrl |= WINBIND_KRB5_AUTH;
457 tiniparser_freedict(d);
459 return ctrl;
462 /* Authenticate a user with a plaintext password */
464 static bool check_plaintext_auth(const char *user, const char *pass,
465 bool stdout_diagnostics)
467 struct winbindd_request request;
468 struct winbindd_response response;
469 NSS_STATUS result;
471 if (!get_require_membership_sid()) {
472 return False;
475 /* Send off request */
477 ZERO_STRUCT(request);
478 ZERO_STRUCT(response);
480 fstrcpy(request.data.auth.user, user);
481 fstrcpy(request.data.auth.pass, pass);
482 if (require_membership_of_sid) {
483 strlcpy(request.data.auth.require_membership_of_sid,
484 require_membership_of_sid,
485 sizeof(request.data.auth.require_membership_of_sid));
488 if (offline_logon) {
489 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
492 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH, &request, &response);
494 /* Display response */
496 if (stdout_diagnostics) {
497 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
498 d_fprintf(stderr, "Reading winbind reply failed! (0x01)\n");
501 d_printf("%s: %s (0x%x)\n",
502 response.data.auth.nt_status_string,
503 response.data.auth.error_string,
504 response.data.auth.nt_status);
505 } else {
506 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
507 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
510 DEBUG(3, ("%s: %s (0x%x)\n",
511 response.data.auth.nt_status_string,
512 response.data.auth.error_string,
513 response.data.auth.nt_status));
516 return (result == NSS_STATUS_SUCCESS);
519 /* authenticate a user with an encrypted username/password */
521 NTSTATUS contact_winbind_auth_crap(const char *username,
522 const char *domain,
523 const char *workstation,
524 const DATA_BLOB *challenge,
525 const DATA_BLOB *lm_response,
526 const DATA_BLOB *nt_response,
527 uint32_t flags,
528 uint32_t extra_logon_parameters,
529 uint8_t lm_key[8],
530 uint8_t user_session_key[16],
531 uint8_t *pauthoritative,
532 char **error_string,
533 char **unix_name)
535 NTSTATUS nt_status;
536 NSS_STATUS result;
537 struct winbindd_request request;
538 struct winbindd_response response;
540 *pauthoritative = 1;
542 if (!get_require_membership_sid()) {
543 return NT_STATUS_INVALID_PARAMETER;
546 ZERO_STRUCT(request);
547 ZERO_STRUCT(response);
549 request.flags = flags;
551 request.data.auth_crap.logon_parameters = extra_logon_parameters
552 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
554 if (opt_allow_mschapv2) {
555 request.data.auth_crap.logon_parameters |= MSV1_0_ALLOW_MSVCHAPV2;
558 if (require_membership_of_sid)
559 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
561 fstrcpy(request.data.auth_crap.user, username);
562 fstrcpy(request.data.auth_crap.domain, domain);
564 fstrcpy(request.data.auth_crap.workstation,
565 workstation);
567 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
569 if (lm_response && lm_response->length) {
570 memcpy(request.data.auth_crap.lm_resp,
571 lm_response->data,
572 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
573 request.data.auth_crap.lm_resp_len = lm_response->length;
576 if (nt_response && nt_response->length) {
577 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
578 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
579 request.extra_len = nt_response->length;
580 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
581 if (request.extra_data.data == NULL) {
582 return NT_STATUS_NO_MEMORY;
584 memcpy(request.extra_data.data, nt_response->data,
585 nt_response->length);
587 } else {
588 memcpy(request.data.auth_crap.nt_resp,
589 nt_response->data, nt_response->length);
591 request.data.auth_crap.nt_resp_len = nt_response->length;
594 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH_CRAP, &request, &response);
595 SAFE_FREE(request.extra_data.data);
597 /* Display response */
599 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
600 nt_status = NT_STATUS_UNSUCCESSFUL;
601 if (error_string)
602 *error_string = smb_xstrdup("Reading winbind reply failed!");
603 winbindd_free_response(&response);
604 return nt_status;
607 nt_status = (NT_STATUS(response.data.auth.nt_status));
608 if (!NT_STATUS_IS_OK(nt_status)) {
609 if (error_string)
610 *error_string = smb_xstrdup(response.data.auth.error_string);
611 *pauthoritative = response.data.auth.authoritative;
612 winbindd_free_response(&response);
613 return nt_status;
616 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
617 memcpy(lm_key, response.data.auth.first_8_lm_hash,
618 sizeof(response.data.auth.first_8_lm_hash));
620 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
621 memcpy(user_session_key, response.data.auth.user_session_key,
622 sizeof(response.data.auth.user_session_key));
625 if (flags & WBFLAG_PAM_UNIX_NAME) {
626 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
627 if (!*unix_name) {
628 winbindd_free_response(&response);
629 return NT_STATUS_NO_MEMORY;
633 winbindd_free_response(&response);
634 return nt_status;
637 /* contact server to change user password using auth crap */
638 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
639 const char *domain,
640 const DATA_BLOB new_nt_pswd,
641 const DATA_BLOB old_nt_hash_enc,
642 const DATA_BLOB new_lm_pswd,
643 const DATA_BLOB old_lm_hash_enc,
644 char **error_string)
646 NTSTATUS nt_status;
647 NSS_STATUS result;
648 struct winbindd_request request;
649 struct winbindd_response response;
651 if (!get_require_membership_sid())
653 if(error_string)
654 *error_string = smb_xstrdup("Can't get membership sid.");
655 return NT_STATUS_INVALID_PARAMETER;
658 ZERO_STRUCT(request);
659 ZERO_STRUCT(response);
661 if(username != NULL)
662 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
663 if(domain != NULL)
664 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
666 if(new_nt_pswd.length)
668 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
669 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
672 if(old_nt_hash_enc.length)
674 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));
675 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
678 if(new_lm_pswd.length)
680 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
681 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
684 if(old_lm_hash_enc.length)
686 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));
687 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
690 result = winbindd_request_response(NULL, WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
692 /* Display response */
694 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
696 nt_status = NT_STATUS_UNSUCCESSFUL;
697 if (error_string)
698 *error_string = smb_xstrdup("Reading winbind reply failed!");
699 winbindd_free_response(&response);
700 return nt_status;
703 nt_status = (NT_STATUS(response.data.auth.nt_status));
704 if (!NT_STATUS_IS_OK(nt_status))
706 if (error_string)
707 *error_string = smb_xstrdup(response.data.auth.error_string);
708 winbindd_free_response(&response);
709 return nt_status;
712 winbindd_free_response(&response);
714 return nt_status;
717 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
718 TALLOC_CTX *mem_ctx,
719 void *server_returned_info,
720 const char *original_user_name,
721 uint32_t session_info_flags,
722 struct auth_session_info **session_info_out)
724 const char *unix_username = (const char *)server_returned_info;
725 bool ok;
726 struct dom_sid *sids = NULL;
727 struct auth_session_info *session_info = NULL;
729 session_info = talloc_zero(mem_ctx, struct auth_session_info);
730 if (session_info == NULL) {
731 return NT_STATUS_NO_MEMORY;
734 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
735 if (session_info->unix_info == NULL) {
736 TALLOC_FREE(session_info);
737 return NT_STATUS_NO_MEMORY;
739 session_info->unix_info->unix_name = talloc_strdup(session_info->unix_info,
740 unix_username);
741 if (session_info->unix_info->unix_name == NULL) {
742 TALLOC_FREE(session_info);
743 return NT_STATUS_NO_MEMORY;
746 session_info->security_token = talloc_zero(session_info, struct security_token);
747 if (session_info->security_token == NULL) {
748 TALLOC_FREE(session_info);
749 return NT_STATUS_NO_MEMORY;
752 sids = talloc_zero_array(session_info->security_token,
753 struct dom_sid, 3);
754 if (sids == NULL) {
755 TALLOC_FREE(session_info);
756 return NT_STATUS_NO_MEMORY;
758 ok = dom_sid_parse(SID_WORLD, &sids[0]);
759 if (!ok) {
760 TALLOC_FREE(session_info);
761 return NT_STATUS_INTERNAL_ERROR;
763 ok = dom_sid_parse(SID_NT_NETWORK, &sids[1]);
764 if (!ok) {
765 TALLOC_FREE(session_info);
766 return NT_STATUS_INTERNAL_ERROR;
768 ok = dom_sid_parse(SID_NT_AUTHENTICATED_USERS, &sids[2]);
769 if (!ok) {
770 TALLOC_FREE(session_info);
771 return NT_STATUS_INTERNAL_ERROR;
774 session_info->security_token->num_sids = talloc_array_length(sids);
775 session_info->security_token->sids = sids;
777 *session_info_out = session_info;
779 return NT_STATUS_OK;
782 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
783 TALLOC_CTX *mem_ctx,
784 struct smb_krb5_context *smb_krb5_context,
785 DATA_BLOB *pac_blob,
786 const char *princ_name,
787 const struct tsocket_address *remote_address,
788 uint32_t session_info_flags,
789 struct auth_session_info **session_info)
791 TALLOC_CTX *tmp_ctx;
792 struct PAC_LOGON_INFO *logon_info = NULL;
793 char *unixuser;
794 NTSTATUS status;
795 char *domain = NULL;
796 char *realm = NULL;
797 char *user = NULL;
798 char *p;
800 tmp_ctx = talloc_new(mem_ctx);
801 if (!tmp_ctx) {
802 return NT_STATUS_NO_MEMORY;
805 if (pac_blob) {
806 #ifdef HAVE_KRB5
807 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
808 NULL, NULL, 0, &logon_info);
809 #else
810 status = NT_STATUS_ACCESS_DENIED;
811 #endif
812 if (!NT_STATUS_IS_OK(status)) {
813 goto done;
817 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
819 p = strchr_m(princ_name, '@');
820 if (!p) {
821 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
822 princ_name));
823 return NT_STATUS_LOGON_FAILURE;
826 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
827 if (!user) {
828 return NT_STATUS_NO_MEMORY;
831 realm = talloc_strdup(talloc_tos(), p + 1);
832 if (!realm) {
833 return NT_STATUS_NO_MEMORY;
836 if (!strequal(realm, lp_realm())) {
837 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
838 if (!lp_allow_trusted_domains()) {
839 return NT_STATUS_LOGON_FAILURE;
843 if (logon_info && logon_info->info3.base.logon_domain.string) {
844 domain = talloc_strdup(mem_ctx,
845 logon_info->info3.base.logon_domain.string);
846 if (!domain) {
847 return NT_STATUS_NO_MEMORY;
849 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
850 } else {
852 /* If we have winbind running, we can (and must) shorten the
853 username by using the short netbios name. Otherwise we will
854 have inconsistent user names. With Kerberos, we get the
855 fully qualified realm, with ntlmssp we get the short
856 name. And even w2k3 does use ntlmssp if you for example
857 connect to an ip address. */
859 wbcErr wbc_status;
860 struct wbcDomainInfo *info = NULL;
862 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
863 realm));
865 wbc_status = wbcDomainInfo(realm, &info);
867 if (WBC_ERROR_IS_OK(wbc_status)) {
868 domain = talloc_strdup(mem_ctx,
869 info->short_name);
870 wbcFreeMemory(info);
871 } else {
872 DEBUG(3, ("Could not find short name: %s\n",
873 wbcErrorString(wbc_status)));
874 domain = talloc_strdup(mem_ctx, realm);
876 if (!domain) {
877 return NT_STATUS_NO_MEMORY;
879 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
882 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
883 if (!unixuser) {
884 status = NT_STATUS_NO_MEMORY;
885 goto done;
888 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
890 done:
891 TALLOC_FREE(tmp_ctx);
892 return status;
898 * Return the challenge as determined by the authentication subsystem
899 * @return an 8 byte random challenge
902 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
903 uint8_t chal[8])
905 if (auth_ctx->challenge.data.length == 8) {
906 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
907 auth_ctx->challenge.set_by));
908 memcpy(chal, auth_ctx->challenge.data.data, 8);
909 return NT_STATUS_OK;
912 if (!auth_ctx->challenge.set_by) {
913 generate_random_buffer(chal, 8);
915 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
916 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
917 auth_ctx->challenge.set_by = "random";
920 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
921 auth_ctx->challenge.set_by));
923 return NT_STATUS_OK;
927 * NTLM2 authentication modifies the effective challenge,
928 * @param challenge The new challenge value
930 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
932 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
933 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
935 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
936 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
938 return NT_STATUS_OK;
942 * Check the password on an NTLMSSP login.
944 * Return the session keys used on the connection.
947 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
948 TALLOC_CTX *mem_ctx,
949 const struct auth_usersupplied_info *user_info,
950 uint8_t *pauthoritative,
951 void **server_returned_info,
952 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
954 NTSTATUS nt_status;
955 char *error_string = NULL;
956 uint8_t lm_key[8];
957 uint8_t user_sess_key[16];
958 char *unix_name = NULL;
960 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
961 user_info->workstation_name,
962 &auth4_context->challenge.data,
963 &user_info->password.response.lanman,
964 &user_info->password.response.nt,
965 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
967 lm_key, user_sess_key,
968 pauthoritative,
969 &error_string, &unix_name);
971 if (NT_STATUS_IS_OK(nt_status)) {
972 if (!all_zero(lm_key, 8)) {
973 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
974 memcpy(lm_session_key->data, lm_key, 8);
975 memset(lm_session_key->data+8, '\0', 8);
978 if (!all_zero(user_sess_key, 16)) {
979 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
981 *server_returned_info = talloc_strdup(mem_ctx,
982 unix_name);
983 } else {
984 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
985 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
986 user_info->client.domain_name, user_info->client.account_name,
987 user_info->workstation_name,
988 error_string ? error_string : "unknown error (NULL)"));
991 SAFE_FREE(error_string);
992 SAFE_FREE(unix_name);
993 return nt_status;
996 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
997 TALLOC_CTX *mem_ctx,
998 const struct auth_usersupplied_info *user_info,
999 uint8_t *pauthoritative,
1000 void **server_returned_info,
1001 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
1003 NTSTATUS nt_status;
1004 struct samr_Password lm_pw, nt_pw;
1006 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1008 *pauthoritative = 1;
1010 nt_status = ntlm_password_check(mem_ctx,
1011 true, true, 0,
1012 &auth4_context->challenge.data,
1013 &user_info->password.response.lanman,
1014 &user_info->password.response.nt,
1015 user_info->client.account_name,
1016 user_info->client.account_name,
1017 user_info->client.domain_name,
1018 &lm_pw, &nt_pw, session_key, lm_session_key);
1020 if (NT_STATUS_IS_OK(nt_status)) {
1021 *server_returned_info = talloc_asprintf(mem_ctx,
1022 "%s%c%s", user_info->client.domain_name,
1023 *lp_winbind_separator(),
1024 user_info->client.account_name);
1025 } else {
1026 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
1027 user_info->client.domain_name, user_info->client.account_name,
1028 user_info->workstation_name,
1029 nt_errstr(nt_status)));
1031 return nt_status;
1034 static NTSTATUS ntlm_auth_prepare_gensec_client(TALLOC_CTX *mem_ctx,
1035 struct loadparm_context *lp_ctx,
1036 struct gensec_security **gensec_security_out)
1038 struct gensec_security *gensec_security = NULL;
1039 NTSTATUS nt_status;
1040 TALLOC_CTX *tmp_ctx;
1041 const struct gensec_security_ops **backends = NULL;
1042 struct gensec_settings *gensec_settings = NULL;
1043 size_t idx = 0;
1045 tmp_ctx = talloc_new(mem_ctx);
1046 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1048 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1049 if (gensec_settings == NULL) {
1050 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1051 TALLOC_FREE(tmp_ctx);
1052 return NT_STATUS_NO_MEMORY;
1055 backends = talloc_zero_array(gensec_settings,
1056 const struct gensec_security_ops *, 4);
1057 if (backends == NULL) {
1058 TALLOC_FREE(tmp_ctx);
1059 return NT_STATUS_NO_MEMORY;
1061 gensec_settings->backends = backends;
1063 gensec_init();
1065 /* These need to be in priority order, krb5 before NTLMSSP */
1066 #if defined(HAVE_KRB5)
1067 backends[idx++] = &gensec_gse_krb5_security_ops;
1068 #endif
1070 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1072 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1074 nt_status = gensec_client_start(NULL, &gensec_security,
1075 gensec_settings);
1076 if (!NT_STATUS_IS_OK(nt_status)) {
1077 TALLOC_FREE(tmp_ctx);
1078 return nt_status;
1081 talloc_unlink(tmp_ctx, gensec_settings);
1083 if (opt_target_service != NULL) {
1084 nt_status = gensec_set_target_service(gensec_security,
1085 opt_target_service);
1086 if (!NT_STATUS_IS_OK(nt_status)) {
1087 TALLOC_FREE(tmp_ctx);
1088 return nt_status;
1092 if (opt_target_hostname != NULL) {
1093 nt_status = gensec_set_target_hostname(gensec_security,
1094 opt_target_hostname);
1095 if (!NT_STATUS_IS_OK(nt_status)) {
1096 TALLOC_FREE(tmp_ctx);
1097 return nt_status;
1101 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1102 TALLOC_FREE(tmp_ctx);
1103 return NT_STATUS_OK;
1106 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1108 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1109 if (auth4_context == NULL) {
1110 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1111 return NULL;
1113 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1114 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1115 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1116 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1117 if (local_pw) {
1118 auth4_context->check_ntlm_password = local_pw_check;
1119 } else {
1120 auth4_context->check_ntlm_password = winbind_pw_check;
1122 auth4_context->private_data = NULL;
1123 return auth4_context;
1126 static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx,
1127 struct loadparm_context *lp_ctx,
1128 struct gensec_security **gensec_security_out)
1130 struct gensec_security *gensec_security;
1131 NTSTATUS nt_status;
1133 TALLOC_CTX *tmp_ctx;
1134 const struct gensec_security_ops **backends;
1135 struct gensec_settings *gensec_settings;
1136 size_t idx = 0;
1137 struct cli_credentials *server_credentials;
1139 struct auth4_context *auth4_context;
1141 tmp_ctx = talloc_new(mem_ctx);
1142 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1144 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1145 if (auth4_context == NULL) {
1146 TALLOC_FREE(tmp_ctx);
1147 return NT_STATUS_NO_MEMORY;
1150 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1151 if (lp_ctx == NULL) {
1152 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1153 TALLOC_FREE(tmp_ctx);
1154 return NT_STATUS_NO_MEMORY;
1158 * This should be a 'netbios domain -> DNS domain'
1159 * mapping, and can currently validly return NULL on
1160 * poorly configured systems.
1162 * This is used for the NTLMSSP server
1165 if (opt_password) {
1166 gensec_settings->server_netbios_name = lp_netbios_name();
1167 gensec_settings->server_netbios_domain = lp_workgroup();
1168 } else {
1169 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1170 gensec_settings->server_netbios_domain = get_winbind_domain();
1173 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1174 get_mydnsdomname(talloc_tos()));
1175 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1176 get_mydnsfullname());
1178 backends = talloc_zero_array(gensec_settings,
1179 const struct gensec_security_ops *, 4);
1181 if (backends == NULL) {
1182 TALLOC_FREE(tmp_ctx);
1183 return NT_STATUS_NO_MEMORY;
1185 gensec_settings->backends = backends;
1187 gensec_init();
1189 /* These need to be in priority order, krb5 before NTLMSSP */
1190 #if defined(HAVE_KRB5)
1191 backends[idx++] = &gensec_gse_krb5_security_ops;
1192 #endif
1194 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1196 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1199 * This is anonymous for now, because we just use it
1200 * to set the kerberos state at the moment
1202 server_credentials = cli_credentials_init_anon(tmp_ctx);
1203 if (!server_credentials) {
1204 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1205 return NT_STATUS_NO_MEMORY;
1208 cli_credentials_set_conf(server_credentials, lp_ctx);
1210 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1211 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1212 } else {
1213 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1216 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1217 auth4_context, &gensec_security);
1219 if (!NT_STATUS_IS_OK(nt_status)) {
1220 TALLOC_FREE(tmp_ctx);
1221 return nt_status;
1224 gensec_set_credentials(gensec_security, server_credentials);
1227 * TODO: Allow the caller to pass their own description here
1228 * via a command-line option
1230 nt_status = gensec_set_target_service_description(gensec_security,
1231 "ntlm_auth");
1232 if (!NT_STATUS_IS_OK(nt_status)) {
1233 TALLOC_FREE(tmp_ctx);
1234 return nt_status;
1237 talloc_unlink(tmp_ctx, lp_ctx);
1238 talloc_unlink(tmp_ctx, server_credentials);
1239 talloc_unlink(tmp_ctx, gensec_settings);
1240 talloc_unlink(tmp_ctx, auth4_context);
1242 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1243 TALLOC_FREE(tmp_ctx);
1244 return NT_STATUS_OK;
1247 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1248 struct loadparm_context *lp_ctx,
1249 struct ntlm_auth_state *state,
1250 char *buf, int length, void **private2)
1252 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1253 return;
1256 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1257 struct loadparm_context *lp_ctx,
1258 struct ntlm_auth_state *state,
1259 char *buf, int length, void **private2)
1261 char *user, *pass;
1262 user=buf;
1264 pass=(char *)memchr(buf,' ',length);
1265 if (!pass) {
1266 DEBUG(2, ("Password not found. Denying access\n"));
1267 printf("ERR\n");
1268 return;
1270 *pass='\0';
1271 pass++;
1273 if (state->helper_mode == SQUID_2_5_BASIC) {
1274 rfc1738_unescape(user);
1275 rfc1738_unescape(pass);
1278 if (check_plaintext_auth(user, pass, False)) {
1279 printf("OK\n");
1280 } else {
1281 printf("ERR\n");
1285 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1286 struct loadparm_context *lp_ctx,
1287 char *buf, int length, void **private1)
1289 DATA_BLOB in;
1290 DATA_BLOB out = data_blob(NULL, 0);
1291 char *out_base64 = NULL;
1292 const char *reply_arg = NULL;
1293 struct gensec_ntlm_state {
1294 struct gensec_security *gensec_state;
1295 const char *set_password;
1297 struct gensec_ntlm_state *state;
1299 NTSTATUS nt_status;
1300 bool first = false;
1301 const char *reply_code;
1302 struct cli_credentials *creds;
1304 static char *want_feature_list = NULL;
1305 static DATA_BLOB session_key;
1307 TALLOC_CTX *mem_ctx;
1309 if (*private1) {
1310 state = (struct gensec_ntlm_state *)*private1;
1311 } else {
1312 state = talloc_zero(NULL, struct gensec_ntlm_state);
1313 if (!state) {
1314 printf("BH No Memory\n");
1315 exit(1);
1317 *private1 = state;
1318 if (opt_password) {
1319 state->set_password = opt_password;
1323 if (strlen(buf) < 2) {
1324 DEBUG(1, ("query [%s] invalid", buf));
1325 printf("BH Query invalid\n");
1326 return;
1329 if (strlen(buf) > 3) {
1330 if(strncmp(buf, "SF ", 3) == 0) {
1331 DEBUG(10, ("Setting flags to negotiate\n"));
1332 talloc_free(want_feature_list);
1333 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1334 printf("OK\n");
1335 return;
1337 in = base64_decode_data_blob(buf + 3);
1338 } else {
1339 in = data_blob(NULL, 0);
1342 if (strncmp(buf, "YR", 2) == 0) {
1343 if (state->gensec_state) {
1344 talloc_free(state->gensec_state);
1345 state->gensec_state = NULL;
1347 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1348 /* Just return BH, like ntlm_auth from Samba 3 does. */
1349 printf("BH Command expected\n");
1350 data_blob_free(&in);
1351 return;
1352 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1353 (strncmp(buf, "KK ", 3) != 0) &&
1354 (strncmp(buf, "AF ", 3) != 0) &&
1355 (strncmp(buf, "NA ", 3) != 0) &&
1356 (strncmp(buf, "UG", 2) != 0) &&
1357 (strncmp(buf, "PW ", 3) != 0) &&
1358 (strncmp(buf, "GK", 2) != 0) &&
1359 (strncmp(buf, "GF", 2) != 0)) {
1360 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1361 printf("BH SPNEGO request invalid prefix\n");
1362 data_blob_free(&in);
1363 return;
1366 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1368 /* setup gensec */
1369 if (!(state->gensec_state)) {
1370 switch (stdio_helper_mode) {
1371 case GSS_SPNEGO_CLIENT:
1373 * cached credentials are only supported by
1374 * NTLMSSP_CLIENT_1 for now.
1376 use_cached_creds = false;
1377 /* fall through */
1378 case NTLMSSP_CLIENT_1:
1379 /* setup the client side */
1381 if (state->set_password != NULL) {
1382 use_cached_creds = false;
1385 if (use_cached_creds) {
1386 struct wbcCredentialCacheParams params;
1387 struct wbcCredentialCacheInfo *info = NULL;
1388 struct wbcAuthErrorInfo *error = NULL;
1389 wbcErr wbc_status;
1391 params.account_name = opt_username;
1392 params.domain_name = opt_domain;
1393 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1394 params.num_blobs = 0;
1395 params.blobs = NULL;
1397 wbc_status = wbcCredentialCache(&params, &info,
1398 &error);
1399 wbcFreeMemory(error);
1400 if (!WBC_ERROR_IS_OK(wbc_status)) {
1401 use_cached_creds = false;
1403 wbcFreeMemory(info);
1406 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1407 &state->gensec_state);
1408 if (!NT_STATUS_IS_OK(nt_status)) {
1409 printf("BH GENSEC mech failed to start: %s\n",
1410 nt_errstr(nt_status));
1411 talloc_free(mem_ctx);
1412 return;
1415 creds = cli_credentials_init(state->gensec_state);
1416 cli_credentials_set_conf(creds, lp_ctx);
1417 if (opt_username) {
1418 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1420 if (opt_domain) {
1421 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1423 if (use_cached_creds) {
1424 gensec_want_feature(state->gensec_state,
1425 GENSEC_FEATURE_NTLM_CCACHE);
1426 } else if (state->set_password) {
1427 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1428 } else {
1429 cli_credentials_set_password_callback(creds, get_password);
1431 if (opt_workstation) {
1432 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1435 gensec_set_credentials(state->gensec_state, creds);
1437 break;
1438 case GSS_SPNEGO_SERVER:
1439 case SQUID_2_5_NTLMSSP:
1441 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1442 &state->gensec_state);
1443 if (!NT_STATUS_IS_OK(nt_status)) {
1444 printf("BH GENSEC mech failed to start: %s\n",
1445 nt_errstr(nt_status));
1446 talloc_free(mem_ctx);
1447 return;
1449 break;
1451 default:
1452 talloc_free(mem_ctx);
1453 abort();
1456 gensec_want_feature_list(state->gensec_state, want_feature_list);
1458 switch (stdio_helper_mode) {
1459 case GSS_SPNEGO_CLIENT:
1460 case GSS_SPNEGO_SERVER:
1461 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1462 if (!in.length) {
1463 first = true;
1465 break;
1466 case NTLMSSP_CLIENT_1:
1467 if (!in.length) {
1468 first = true;
1470 /* fall through */
1471 case SQUID_2_5_NTLMSSP:
1472 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1473 break;
1474 default:
1475 talloc_free(mem_ctx);
1476 abort();
1479 if (!NT_STATUS_IS_OK(nt_status)) {
1480 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1481 printf("BH GENSEC mech failed to start\n");
1482 talloc_free(mem_ctx);
1483 return;
1488 /* update */
1490 if (strncmp(buf, "PW ", 3) == 0) {
1491 state->set_password = talloc_strndup(state,
1492 (const char *)in.data,
1493 in.length);
1495 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1496 state->set_password,
1497 CRED_SPECIFIED);
1498 printf("OK\n");
1499 data_blob_free(&in);
1500 talloc_free(mem_ctx);
1501 return;
1504 if (strncmp(buf, "GK", 2) == 0) {
1505 char *base64_key;
1506 DEBUG(10, ("Requested session key\n"));
1507 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1508 if(!NT_STATUS_IS_OK(nt_status)) {
1509 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1510 printf("BH No session key\n");
1511 talloc_free(mem_ctx);
1512 return;
1513 } else {
1514 base64_key = base64_encode_data_blob(state, session_key);
1515 SMB_ASSERT(base64_key != NULL);
1516 printf("GK %s\n", base64_key);
1517 talloc_free(base64_key);
1519 talloc_free(mem_ctx);
1520 return;
1523 if (strncmp(buf, "GF", 2) == 0) {
1524 uint32_t neg_flags;
1526 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1528 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1529 if (neg_flags == 0) {
1530 printf("BH\n");
1531 return;
1534 printf("GF 0x%08x\n", neg_flags);
1535 return;
1538 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1540 /* don't leak 'bad password'/'no such user' info to the network client */
1541 nt_status = nt_status_squash(nt_status);
1543 if (out.length) {
1544 out_base64 = base64_encode_data_blob(mem_ctx, out);
1545 SMB_ASSERT(out_base64 != NULL);
1546 } else {
1547 out_base64 = NULL;
1550 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1551 reply_arg = "*";
1552 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1553 reply_code = "YR";
1554 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1555 reply_code = "KK";
1556 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1557 reply_code = "TT";
1558 } else {
1559 abort();
1563 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1564 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1565 reply_arg = nt_errstr(nt_status);
1566 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1567 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1568 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1569 reply_arg = nt_errstr(nt_status);
1570 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1571 } else if (!NT_STATUS_IS_OK(nt_status)) {
1572 reply_code = "NA";
1573 reply_arg = nt_errstr(nt_status);
1574 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1575 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1576 struct auth_session_info *session_info;
1578 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1579 if (!NT_STATUS_IS_OK(nt_status)) {
1580 reply_code = "BH Failed to retrive session info";
1581 reply_arg = nt_errstr(nt_status);
1582 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1583 } else {
1585 reply_code = "AF";
1586 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1587 if (reply_arg == NULL) {
1588 reply_code = "BH out of memory";
1589 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1591 talloc_free(session_info);
1593 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1594 reply_code = "AF";
1595 reply_arg = out_base64;
1596 } else {
1597 abort();
1600 switch (stdio_helper_mode) {
1601 case GSS_SPNEGO_SERVER:
1602 printf("%s %s %s\n", reply_code,
1603 out_base64 ? out_base64 : "*",
1604 reply_arg ? reply_arg : "*");
1605 break;
1606 default:
1607 if (out_base64) {
1608 printf("%s %s\n", reply_code, out_base64);
1609 } else if (reply_arg) {
1610 printf("%s %s\n", reply_code, reply_arg);
1611 } else {
1612 printf("%s\n", reply_code);
1616 talloc_free(mem_ctx);
1617 return;
1620 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1621 struct loadparm_context *lp_ctx,
1622 struct ntlm_auth_state *state,
1623 char *buf, int length, void **private2)
1625 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1626 return;
1629 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1630 struct loadparm_context *lp_ctx,
1631 struct ntlm_auth_state *state,
1632 char *buf, int length, void **private2)
1634 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1635 return;
1638 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1639 struct loadparm_context *lp_ctx,
1640 struct ntlm_auth_state *state,
1641 char *buf, int length, void **private2)
1643 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1644 return;
1647 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1648 struct loadparm_context *lp_ctx,
1649 struct ntlm_auth_state *state,
1650 char *buf, int length, void **private2)
1652 char *request, *parameter;
1653 static DATA_BLOB challenge;
1654 static DATA_BLOB lm_response;
1655 static DATA_BLOB nt_response;
1656 static char *full_username;
1657 static char *username;
1658 static char *domain;
1659 static char *plaintext_password;
1660 static bool ntlm_server_1_user_session_key;
1661 static bool ntlm_server_1_lm_session_key;
1663 if (strequal(buf, ".")) {
1664 if (!full_username && !username) {
1665 printf("Error: No username supplied!\n");
1666 } else if (plaintext_password) {
1667 /* handle this request as plaintext */
1668 if (!full_username) {
1669 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1670 printf("Error: Out of memory in "
1671 "asprintf!\n.\n");
1672 return;
1675 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1676 printf("Authenticated: Yes\n");
1677 } else {
1678 printf("Authenticated: No\n");
1680 } else if (!lm_response.data && !nt_response.data) {
1681 printf("Error: No password supplied!\n");
1682 } else if (!challenge.data) {
1683 printf("Error: No lanman-challenge supplied!\n");
1684 } else {
1685 char *error_string = NULL;
1686 uchar lm_key[8];
1687 uchar user_session_key[16];
1688 uint32_t flags = 0;
1689 NTSTATUS nt_status;
1690 if (full_username && !username) {
1691 fstring fstr_user;
1692 fstring fstr_domain;
1694 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1695 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1696 printf("Error: Could not parse into "
1697 "domain and username\n");
1699 SAFE_FREE(username);
1700 SAFE_FREE(domain);
1701 username = smb_xstrdup(fstr_user);
1702 domain = smb_xstrdup(fstr_domain);
1705 if (opt_password) {
1706 DATA_BLOB nt_session_key, lm_session_key;
1707 struct samr_Password lm_pw, nt_pw;
1708 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1709 ZERO_STRUCT(user_session_key);
1710 ZERO_STRUCT(lm_key);
1712 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1713 nt_status = ntlm_password_check(mem_ctx,
1714 true, true, 0,
1715 &challenge,
1716 &lm_response,
1717 &nt_response,
1718 username,
1719 username,
1720 domain,
1721 &lm_pw, &nt_pw,
1722 &nt_session_key,
1723 &lm_session_key);
1724 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1725 if (ntlm_server_1_user_session_key) {
1726 if (nt_session_key.length == sizeof(user_session_key)) {
1727 memcpy(user_session_key,
1728 nt_session_key.data,
1729 sizeof(user_session_key));
1732 if (ntlm_server_1_lm_session_key) {
1733 if (lm_session_key.length == sizeof(lm_key)) {
1734 memcpy(lm_key,
1735 lm_session_key.data,
1736 sizeof(lm_key));
1739 TALLOC_FREE(mem_ctx);
1741 } else {
1742 uint8_t authoritative = 0;
1744 if (!domain) {
1745 domain = smb_xstrdup(get_winbind_domain());
1748 if (ntlm_server_1_lm_session_key)
1749 flags |= WBFLAG_PAM_LMKEY;
1751 if (ntlm_server_1_user_session_key)
1752 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1754 nt_status = contact_winbind_auth_crap(username,
1755 domain,
1756 lp_netbios_name(),
1757 &challenge,
1758 &lm_response,
1759 &nt_response,
1760 flags, 0,
1761 lm_key,
1762 user_session_key,
1763 &authoritative,
1764 &error_string,
1765 NULL);
1768 if (!NT_STATUS_IS_OK(nt_status)) {
1769 printf("Authenticated: No\n");
1770 printf("Authentication-Error: %s\n.\n",
1771 error_string);
1772 } else {
1773 char *hex_lm_key;
1774 char *hex_user_session_key;
1776 printf("Authenticated: Yes\n");
1778 if (ntlm_server_1_lm_session_key
1779 && (!all_zero(lm_key,
1780 sizeof(lm_key)))) {
1781 hex_lm_key = hex_encode_talloc(NULL,
1782 (const unsigned char *)lm_key,
1783 sizeof(lm_key));
1784 printf("LANMAN-Session-Key: %s\n",
1785 hex_lm_key);
1786 TALLOC_FREE(hex_lm_key);
1789 if (ntlm_server_1_user_session_key
1790 && (!all_zero(user_session_key,
1791 sizeof(user_session_key)))) {
1792 hex_user_session_key = hex_encode_talloc(NULL,
1793 (const unsigned char *)user_session_key,
1794 sizeof(user_session_key));
1795 printf("User-Session-Key: %s\n",
1796 hex_user_session_key);
1797 TALLOC_FREE(hex_user_session_key);
1800 SAFE_FREE(error_string);
1802 /* clear out the state */
1803 challenge = data_blob_null;
1804 nt_response = data_blob_null;
1805 lm_response = data_blob_null;
1806 SAFE_FREE(full_username);
1807 SAFE_FREE(username);
1808 SAFE_FREE(domain);
1809 SAFE_FREE(plaintext_password);
1810 ntlm_server_1_user_session_key = False;
1811 ntlm_server_1_lm_session_key = False;
1812 printf(".\n");
1814 return;
1817 request = buf;
1819 /* Indicates a base64 encoded structure */
1820 parameter = strstr_m(request, ":: ");
1821 if (!parameter) {
1822 parameter = strstr_m(request, ": ");
1824 if (!parameter) {
1825 DEBUG(0, ("Parameter not found!\n"));
1826 printf("Error: Parameter not found!\n.\n");
1827 return;
1830 parameter[0] ='\0';
1831 parameter++;
1832 parameter[0] ='\0';
1833 parameter++;
1835 } else {
1836 parameter[0] ='\0';
1837 parameter++;
1838 parameter[0] ='\0';
1839 parameter++;
1840 parameter[0] ='\0';
1841 parameter++;
1843 base64_decode_inplace(parameter);
1846 if (strequal(request, "LANMAN-Challenge")) {
1847 challenge = strhex_to_data_blob(NULL, parameter);
1848 if (challenge.length != 8) {
1849 printf("Error: hex decode of %s failed! "
1850 "(got %d bytes, expected 8)\n.\n",
1851 parameter,
1852 (int)challenge.length);
1853 challenge = data_blob_null;
1855 } else if (strequal(request, "NT-Response")) {
1856 nt_response = strhex_to_data_blob(NULL, parameter);
1857 if (nt_response.length < 24) {
1858 printf("Error: hex decode of %s failed! "
1859 "(only got %d bytes, needed at least 24)\n.\n",
1860 parameter,
1861 (int)nt_response.length);
1862 nt_response = data_blob_null;
1864 } else if (strequal(request, "LANMAN-Response")) {
1865 lm_response = strhex_to_data_blob(NULL, parameter);
1866 if (lm_response.length != 24) {
1867 printf("Error: hex decode of %s failed! "
1868 "(got %d bytes, expected 24)\n.\n",
1869 parameter,
1870 (int)lm_response.length);
1871 lm_response = data_blob_null;
1873 } else if (strequal(request, "Password")) {
1874 plaintext_password = smb_xstrdup(parameter);
1875 } else if (strequal(request, "NT-Domain")) {
1876 domain = smb_xstrdup(parameter);
1877 } else if (strequal(request, "Username")) {
1878 username = smb_xstrdup(parameter);
1879 } else if (strequal(request, "Full-Username")) {
1880 full_username = smb_xstrdup(parameter);
1881 } else if (strequal(request, "Request-User-Session-Key")) {
1882 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1883 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1884 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1885 } else {
1886 printf("Error: Unknown request %s\n.\n", request);
1890 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
1891 struct loadparm_context *lp_ctx,
1892 struct ntlm_auth_state *state,
1893 char *buf, int length, void **private2)
1895 char *request, *parameter;
1896 static DATA_BLOB new_nt_pswd;
1897 static DATA_BLOB old_nt_hash_enc;
1898 static DATA_BLOB new_lm_pswd;
1899 static DATA_BLOB old_lm_hash_enc;
1900 static char *full_username = NULL;
1901 static char *username = NULL;
1902 static char *domain = NULL;
1903 static char *newpswd = NULL;
1904 static char *oldpswd = NULL;
1906 if (strequal(buf, ".")) {
1907 if(newpswd && oldpswd) {
1908 uchar old_nt_hash[16];
1909 uchar old_lm_hash[16];
1910 uchar new_nt_hash[16];
1911 uchar new_lm_hash[16];
1913 new_nt_pswd = data_blob(NULL, 516);
1914 old_nt_hash_enc = data_blob(NULL, 16);
1916 /* Calculate the MD4 hash (NT compatible) of the
1917 * password */
1918 E_md4hash(oldpswd, old_nt_hash);
1919 E_md4hash(newpswd, new_nt_hash);
1921 /* E_deshash returns false for 'long'
1922 passwords (> 14 DOS chars).
1924 Therefore, don't send a buffer
1925 encrypted with the truncated hash
1926 (it could allow an even easier
1927 attack on the password)
1929 Likewise, obey the admin's restriction
1932 if (lp_client_lanman_auth() &&
1933 E_deshash(newpswd, new_lm_hash) &&
1934 E_deshash(oldpswd, old_lm_hash)) {
1935 new_lm_pswd = data_blob(NULL, 516);
1936 old_lm_hash_enc = data_blob(NULL, 16);
1937 encode_pw_buffer(new_lm_pswd.data, newpswd,
1938 STR_UNICODE);
1940 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
1941 E_old_pw_hash(new_nt_hash, old_lm_hash,
1942 old_lm_hash_enc.data);
1943 } else {
1944 new_lm_pswd.data = NULL;
1945 new_lm_pswd.length = 0;
1946 old_lm_hash_enc.data = NULL;
1947 old_lm_hash_enc.length = 0;
1950 encode_pw_buffer(new_nt_pswd.data, newpswd,
1951 STR_UNICODE);
1953 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
1954 E_old_pw_hash(new_nt_hash, old_nt_hash,
1955 old_nt_hash_enc.data);
1958 if (!full_username && !username) {
1959 printf("Error: No username supplied!\n");
1960 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1961 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1962 printf("Error: No NT or LM password "
1963 "blobs supplied!\n");
1964 } else {
1965 char *error_string = NULL;
1967 if (full_username && !username) {
1968 fstring fstr_user;
1969 fstring fstr_domain;
1971 if (!parse_ntlm_auth_domain_user(full_username,
1972 fstr_user,
1973 fstr_domain)) {
1974 /* username might be 'tainted', don't
1975 * print into our new-line
1976 * deleimianted stream */
1977 printf("Error: Could not "
1978 "parse into domain and "
1979 "username\n");
1980 SAFE_FREE(username);
1981 username = smb_xstrdup(full_username);
1982 } else {
1983 SAFE_FREE(username);
1984 SAFE_FREE(domain);
1985 username = smb_xstrdup(fstr_user);
1986 domain = smb_xstrdup(fstr_domain);
1991 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1992 username, domain,
1993 new_nt_pswd,
1994 old_nt_hash_enc,
1995 new_lm_pswd,
1996 old_lm_hash_enc,
1997 &error_string))) {
1998 printf("Password-Change: No\n");
1999 printf("Password-Change-Error: %s\n.\n",
2000 error_string);
2001 } else {
2002 printf("Password-Change: Yes\n");
2005 SAFE_FREE(error_string);
2007 /* clear out the state */
2008 new_nt_pswd = data_blob_null;
2009 old_nt_hash_enc = data_blob_null;
2010 new_lm_pswd = data_blob_null;
2011 old_nt_hash_enc = data_blob_null;
2012 SAFE_FREE(full_username);
2013 SAFE_FREE(username);
2014 SAFE_FREE(domain);
2015 SAFE_FREE(newpswd);
2016 SAFE_FREE(oldpswd);
2017 printf(".\n");
2019 return;
2022 request = buf;
2024 /* Indicates a base64 encoded structure */
2025 parameter = strstr_m(request, ":: ");
2026 if (!parameter) {
2027 parameter = strstr_m(request, ": ");
2029 if (!parameter) {
2030 DEBUG(0, ("Parameter not found!\n"));
2031 printf("Error: Parameter not found!\n.\n");
2032 return;
2035 parameter[0] ='\0';
2036 parameter++;
2037 parameter[0] ='\0';
2038 parameter++;
2039 } else {
2040 parameter[0] ='\0';
2041 parameter++;
2042 parameter[0] ='\0';
2043 parameter++;
2044 parameter[0] ='\0';
2045 parameter++;
2047 base64_decode_inplace(parameter);
2050 if (strequal(request, "new-nt-password-blob")) {
2051 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2052 if (new_nt_pswd.length != 516) {
2053 printf("Error: hex decode of %s failed! "
2054 "(got %d bytes, expected 516)\n.\n",
2055 parameter,
2056 (int)new_nt_pswd.length);
2057 new_nt_pswd = data_blob_null;
2059 } else if (strequal(request, "old-nt-hash-blob")) {
2060 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2061 if (old_nt_hash_enc.length != 16) {
2062 printf("Error: hex decode of %s failed! "
2063 "(got %d bytes, expected 16)\n.\n",
2064 parameter,
2065 (int)old_nt_hash_enc.length);
2066 old_nt_hash_enc = data_blob_null;
2068 } else if (strequal(request, "new-lm-password-blob")) {
2069 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2070 if (new_lm_pswd.length != 516) {
2071 printf("Error: hex decode of %s failed! "
2072 "(got %d bytes, expected 516)\n.\n",
2073 parameter,
2074 (int)new_lm_pswd.length);
2075 new_lm_pswd = data_blob_null;
2078 else if (strequal(request, "old-lm-hash-blob")) {
2079 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2080 if (old_lm_hash_enc.length != 16)
2082 printf("Error: hex decode of %s failed! "
2083 "(got %d bytes, expected 16)\n.\n",
2084 parameter,
2085 (int)old_lm_hash_enc.length);
2086 old_lm_hash_enc = data_blob_null;
2088 } else if (strequal(request, "nt-domain")) {
2089 domain = smb_xstrdup(parameter);
2090 } else if(strequal(request, "username")) {
2091 username = smb_xstrdup(parameter);
2092 } else if(strequal(request, "full-username")) {
2093 username = smb_xstrdup(parameter);
2094 } else if(strequal(request, "new-password")) {
2095 newpswd = smb_xstrdup(parameter);
2096 } else if (strequal(request, "old-password")) {
2097 oldpswd = smb_xstrdup(parameter);
2098 } else {
2099 printf("Error: Unknown request %s\n.\n", request);
2103 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2104 struct loadparm_context *lp_ctx,
2105 struct ntlm_auth_state *state,
2106 stdio_helper_function fn, void **private2)
2108 char *buf;
2109 char tmp[INITIAL_BUFFER_SIZE+1];
2110 int length, buf_size = 0;
2111 char *c;
2113 buf = talloc_strdup(state->mem_ctx, "");
2114 if (!buf) {
2115 DEBUG(0, ("Failed to allocate input buffer.\n"));
2116 fprintf(stderr, "ERR\n");
2117 exit(1);
2120 do {
2122 /* this is not a typo - x_fgets doesn't work too well under
2123 * squid */
2124 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2125 if (ferror(stdin)) {
2126 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2127 "(%s)\n", ferror(stdin),
2128 strerror(ferror(stdin))));
2130 exit(1);
2132 exit(0);
2135 buf = talloc_strdup_append_buffer(buf, tmp);
2136 buf_size += INITIAL_BUFFER_SIZE;
2138 if (buf_size > MAX_BUFFER_SIZE) {
2139 DEBUG(2, ("Oversized message\n"));
2140 fprintf(stderr, "ERR\n");
2141 talloc_free(buf);
2142 return;
2145 c = strchr(buf, '\n');
2146 } while (c == NULL);
2148 *c = '\0';
2149 length = c-buf;
2151 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2153 if (buf[0] == '\0') {
2154 DEBUG(2, ("Invalid Request\n"));
2155 fprintf(stderr, "ERR\n");
2156 talloc_free(buf);
2157 return;
2160 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2161 talloc_free(buf);
2165 static void squid_stream(enum stdio_helper_mode stdio_mode,
2166 struct loadparm_context *lp_ctx,
2167 stdio_helper_function fn) {
2168 TALLOC_CTX *mem_ctx;
2169 struct ntlm_auth_state *state;
2171 /* initialize FDescs */
2172 setbuf(stdout, NULL);
2173 setbuf(stderr, NULL);
2175 mem_ctx = talloc_init("ntlm_auth");
2176 if (!mem_ctx) {
2177 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2178 fprintf(stderr, "ERR\n");
2179 exit(1);
2182 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2183 if (!state) {
2184 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2185 fprintf(stderr, "ERR\n");
2186 exit(1);
2189 state->mem_ctx = mem_ctx;
2190 state->helper_mode = stdio_mode;
2192 while(1) {
2193 TALLOC_CTX *frame = talloc_stackframe();
2194 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2195 TALLOC_FREE(frame);
2200 /* Authenticate a user with a challenge/response */
2202 static bool check_auth_crap(void)
2204 NTSTATUS nt_status;
2205 uint32_t flags = 0;
2206 char lm_key[8];
2207 char user_session_key[16];
2208 char *hex_lm_key;
2209 char *hex_user_session_key;
2210 char *error_string;
2211 uint8_t authoritative = 0;
2213 setbuf(stdout, NULL);
2215 if (request_lm_key)
2216 flags |= WBFLAG_PAM_LMKEY;
2218 if (request_user_session_key)
2219 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2221 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2223 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2224 opt_workstation,
2225 &opt_challenge,
2226 &opt_lm_response,
2227 &opt_nt_response,
2228 flags, 0,
2229 (unsigned char *)lm_key,
2230 (unsigned char *)user_session_key,
2231 &authoritative,
2232 &error_string, NULL);
2234 if (!NT_STATUS_IS_OK(nt_status)) {
2235 printf("%s (0x%x)\n", error_string,
2236 NT_STATUS_V(nt_status));
2237 SAFE_FREE(error_string);
2238 return False;
2241 if (request_lm_key
2242 && (!all_zero((uint8_t *)lm_key, sizeof(lm_key)))) {
2243 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2244 sizeof(lm_key));
2245 printf("LM_KEY: %s\n", hex_lm_key);
2246 TALLOC_FREE(hex_lm_key);
2248 if (request_user_session_key
2249 && (!all_zero((uint8_t *)user_session_key,
2250 sizeof(user_session_key)))) {
2251 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2252 sizeof(user_session_key));
2253 printf("NT_KEY: %s\n", hex_user_session_key);
2254 TALLOC_FREE(hex_user_session_key);
2257 return True;
2260 /* Main program */
2262 enum {
2263 OPT_USERNAME = 1000,
2264 OPT_DOMAIN,
2265 OPT_WORKSTATION,
2266 OPT_CHALLENGE,
2267 OPT_RESPONSE,
2268 OPT_LM,
2269 OPT_NT,
2270 OPT_PASSWORD,
2271 OPT_LM_KEY,
2272 OPT_USER_SESSION_KEY,
2273 OPT_DIAGNOSTICS,
2274 OPT_REQUIRE_MEMBERSHIP,
2275 OPT_USE_CACHED_CREDS,
2276 OPT_ALLOW_MSCHAPV2,
2277 OPT_PAM_WINBIND_CONF,
2278 OPT_TARGET_SERVICE,
2279 OPT_TARGET_HOSTNAME,
2280 OPT_OFFLINE_LOGON
2283 int main(int argc, const char **argv)
2285 TALLOC_CTX *frame = talloc_stackframe();
2286 int opt;
2287 const char *helper_protocol = NULL;
2288 int diagnostics = 0;
2290 const char *hex_challenge = NULL;
2291 const char *hex_lm_response = NULL;
2292 const char *hex_nt_response = NULL;
2293 struct loadparm_context *lp_ctx;
2294 poptContext pc;
2296 /* NOTE: DO NOT change this interface without considering the implications!
2297 This is an external interface, which other programs will use to interact
2298 with this helper.
2301 /* We do not use single-letter command abbreviations, because they harm future
2302 interface stability. */
2304 struct poptOption long_options[] = {
2305 POPT_AUTOHELP
2306 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2307 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2308 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2309 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2310 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2311 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2312 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2313 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2314 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2315 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2316 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2317 { "allow-mschapv2", 0, POPT_ARG_NONE, &opt_allow_mschapv2, OPT_ALLOW_MSCHAPV2, "Explicitly allow MSCHAPv2" },
2318 { "offline-logon", 0, POPT_ARG_NONE, &offline_logon,
2319 OPT_OFFLINE_LOGON,
2320 "Use cached passwords when DC is offline"},
2321 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2322 OPT_DIAGNOSTICS,
2323 "Perform diagnostics on the authentication chain"},
2324 { "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" },
2325 { "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" },
2326 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2327 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2328 POPT_COMMON_CONFIGFILE
2329 POPT_COMMON_VERSION
2330 POPT_COMMON_OPTION
2331 POPT_TABLEEND
2334 /* Samba client initialisation */
2335 smb_init_locale();
2337 setup_logging("ntlm_auth", DEBUG_STDERR);
2338 fault_setup();
2340 /* Parse options */
2342 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2344 /* Parse command line options */
2346 if (argc == 1) {
2347 poptPrintHelp(pc, stderr, 0);
2348 return 1;
2351 while((opt = poptGetNextOpt(pc)) != -1) {
2352 /* Get generic config options like --configfile */
2355 poptFreeContext(pc);
2357 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2358 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2359 get_dyn_CONFIGFILE(), strerror(errno));
2360 exit(1);
2363 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2364 POPT_CONTEXT_KEEP_FIRST);
2366 while((opt = poptGetNextOpt(pc)) != -1) {
2367 switch (opt) {
2368 case OPT_CHALLENGE:
2369 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2370 if (opt_challenge.length != 8) {
2371 fprintf(stderr, "hex decode of %s failed! "
2372 "(only got %d bytes)\n",
2373 hex_challenge,
2374 (int)opt_challenge.length);
2375 exit(1);
2377 break;
2378 case OPT_LM:
2379 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2380 if (opt_lm_response.length != 24) {
2381 fprintf(stderr, "hex decode of %s failed! "
2382 "(only got %d bytes)\n",
2383 hex_lm_response,
2384 (int)opt_lm_response.length);
2385 exit(1);
2387 break;
2389 case OPT_NT:
2390 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2391 if (opt_nt_response.length < 24) {
2392 fprintf(stderr, "hex decode of %s failed! "
2393 "(only got %d bytes)\n",
2394 hex_nt_response,
2395 (int)opt_nt_response.length);
2396 exit(1);
2398 break;
2400 case OPT_REQUIRE_MEMBERSHIP:
2401 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2402 require_membership_of_sid = require_membership_of;
2404 break;
2408 if (opt_username) {
2409 char *domain = SMB_STRDUP(opt_username);
2410 char *p = strchr_m(domain, *lp_winbind_separator());
2411 if (p) {
2412 opt_username = p+1;
2413 *p = '\0';
2414 if (opt_domain && !strequal(opt_domain, domain)) {
2415 fprintf(stderr, "Domain specified in username (%s) "
2416 "doesn't match specified domain (%s)!\n\n",
2417 domain, opt_domain);
2418 poptPrintHelp(pc, stderr, 0);
2419 exit(1);
2421 opt_domain = domain;
2422 } else {
2423 SAFE_FREE(domain);
2427 /* Note: if opt_domain is "" then send no domain */
2428 if (opt_domain == NULL) {
2429 opt_domain = get_winbind_domain();
2432 if (opt_workstation == NULL) {
2433 opt_workstation = "";
2436 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2437 if (lp_ctx == NULL) {
2438 fprintf(stderr, "loadparm_init_s3() failed!\n");
2439 exit(1);
2442 if (helper_protocol) {
2443 int i;
2444 for (i=0; i<NUM_HELPER_MODES; i++) {
2445 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2446 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2447 exit(0);
2450 fprintf(stderr, "unknown helper protocol [%s]\n\n"
2451 "Valid helper protools:\n\n", helper_protocol);
2453 for (i=0; i<NUM_HELPER_MODES; i++) {
2454 fprintf(stderr, "%s\n",
2455 stdio_helper_protocols[i].name);
2458 exit(1);
2461 if (!opt_username || !*opt_username) {
2462 fprintf(stderr, "username must be specified!\n\n");
2463 poptPrintHelp(pc, stderr, 0);
2464 exit(1);
2467 if (opt_challenge.length) {
2468 if (!check_auth_crap()) {
2469 exit(1);
2471 exit(0);
2474 if (!opt_password) {
2475 char pwd[256] = {0};
2476 int rc;
2478 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2479 if (rc == 0) {
2480 opt_password = SMB_STRDUP(pwd);
2484 if (diagnostics) {
2485 if (!diagnose_ntlm_auth()) {
2486 return 1;
2488 } else {
2489 fstring user;
2491 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2492 if (!check_plaintext_auth(user, opt_password, True)) {
2493 return 1;
2497 /* Exit code */
2499 poptFreeContext(pc);
2500 TALLOC_FREE(frame);
2501 return 0;