winbindd: remove unused single_domains array
[Samba.git] / source3 / utils / ntlm_auth.c
blob6a7a269987c67f86a7afc39b61e24297ef76df12
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 /* Session info is not complete, do not pass to auth log */
1459 gensec_want_feature(state->gensec_state, GENSEC_FEATURE_NO_AUTHZ_LOG);
1461 switch (stdio_helper_mode) {
1462 case GSS_SPNEGO_CLIENT:
1463 case GSS_SPNEGO_SERVER:
1464 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1465 if (!in.length) {
1466 first = true;
1468 break;
1469 case NTLMSSP_CLIENT_1:
1470 if (!in.length) {
1471 first = true;
1473 /* fall through */
1474 case SQUID_2_5_NTLMSSP:
1475 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1476 break;
1477 default:
1478 talloc_free(mem_ctx);
1479 abort();
1482 if (!NT_STATUS_IS_OK(nt_status)) {
1483 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1484 printf("BH GENSEC mech failed to start\n");
1485 talloc_free(mem_ctx);
1486 return;
1491 /* update */
1493 if (strncmp(buf, "PW ", 3) == 0) {
1494 state->set_password = talloc_strndup(state,
1495 (const char *)in.data,
1496 in.length);
1498 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1499 state->set_password,
1500 CRED_SPECIFIED);
1501 printf("OK\n");
1502 data_blob_free(&in);
1503 talloc_free(mem_ctx);
1504 return;
1507 if (strncmp(buf, "GK", 2) == 0) {
1508 char *base64_key;
1509 DEBUG(10, ("Requested session key\n"));
1510 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1511 if(!NT_STATUS_IS_OK(nt_status)) {
1512 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1513 printf("BH No session key\n");
1514 talloc_free(mem_ctx);
1515 return;
1516 } else {
1517 base64_key = base64_encode_data_blob(state, session_key);
1518 SMB_ASSERT(base64_key != NULL);
1519 printf("GK %s\n", base64_key);
1520 talloc_free(base64_key);
1522 talloc_free(mem_ctx);
1523 return;
1526 if (strncmp(buf, "GF", 2) == 0) {
1527 uint32_t neg_flags;
1529 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1531 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1532 if (neg_flags == 0) {
1533 printf("BH\n");
1534 return;
1537 printf("GF 0x%08x\n", neg_flags);
1538 return;
1541 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1543 /* don't leak 'bad password'/'no such user' info to the network client */
1544 nt_status = nt_status_squash(nt_status);
1546 if (out.length) {
1547 out_base64 = base64_encode_data_blob(mem_ctx, out);
1548 SMB_ASSERT(out_base64 != NULL);
1549 } else {
1550 out_base64 = NULL;
1553 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1554 reply_arg = "*";
1555 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1556 reply_code = "YR";
1557 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1558 reply_code = "KK";
1559 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1560 reply_code = "TT";
1561 } else {
1562 abort();
1566 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1567 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1568 reply_arg = nt_errstr(nt_status);
1569 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1570 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1571 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1572 reply_arg = nt_errstr(nt_status);
1573 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1574 } else if (!NT_STATUS_IS_OK(nt_status)) {
1575 reply_code = "NA";
1576 reply_arg = nt_errstr(nt_status);
1577 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1578 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1579 struct auth_session_info *session_info;
1581 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1582 if (!NT_STATUS_IS_OK(nt_status)) {
1583 reply_code = "BH Failed to retrive session info";
1584 reply_arg = nt_errstr(nt_status);
1585 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1586 } else {
1588 reply_code = "AF";
1589 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1590 if (reply_arg == NULL) {
1591 reply_code = "BH out of memory";
1592 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1594 talloc_free(session_info);
1596 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1597 reply_code = "AF";
1598 reply_arg = out_base64;
1599 } else {
1600 abort();
1603 switch (stdio_helper_mode) {
1604 case GSS_SPNEGO_SERVER:
1605 printf("%s %s %s\n", reply_code,
1606 out_base64 ? out_base64 : "*",
1607 reply_arg ? reply_arg : "*");
1608 break;
1609 default:
1610 if (out_base64) {
1611 printf("%s %s\n", reply_code, out_base64);
1612 } else if (reply_arg) {
1613 printf("%s %s\n", reply_code, reply_arg);
1614 } else {
1615 printf("%s\n", reply_code);
1619 talloc_free(mem_ctx);
1620 return;
1623 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1624 struct loadparm_context *lp_ctx,
1625 struct ntlm_auth_state *state,
1626 char *buf, int length, void **private2)
1628 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1629 return;
1632 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1633 struct loadparm_context *lp_ctx,
1634 struct ntlm_auth_state *state,
1635 char *buf, int length, void **private2)
1637 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1638 return;
1641 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1642 struct loadparm_context *lp_ctx,
1643 struct ntlm_auth_state *state,
1644 char *buf, int length, void **private2)
1646 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1647 return;
1650 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1651 struct loadparm_context *lp_ctx,
1652 struct ntlm_auth_state *state,
1653 char *buf, int length, void **private2)
1655 char *request, *parameter;
1656 static DATA_BLOB challenge;
1657 static DATA_BLOB lm_response;
1658 static DATA_BLOB nt_response;
1659 static char *full_username;
1660 static char *username;
1661 static char *domain;
1662 static char *plaintext_password;
1663 static bool ntlm_server_1_user_session_key;
1664 static bool ntlm_server_1_lm_session_key;
1666 if (strequal(buf, ".")) {
1667 if (!full_username && !username) {
1668 printf("Error: No username supplied!\n");
1669 } else if (plaintext_password) {
1670 /* handle this request as plaintext */
1671 if (!full_username) {
1672 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1673 printf("Error: Out of memory in "
1674 "asprintf!\n.\n");
1675 return;
1678 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1679 printf("Authenticated: Yes\n");
1680 } else {
1681 printf("Authenticated: No\n");
1683 } else if (!lm_response.data && !nt_response.data) {
1684 printf("Error: No password supplied!\n");
1685 } else if (!challenge.data) {
1686 printf("Error: No lanman-challenge supplied!\n");
1687 } else {
1688 char *error_string = NULL;
1689 uchar lm_key[8];
1690 uchar user_session_key[16];
1691 uint32_t flags = 0;
1692 NTSTATUS nt_status;
1693 if (full_username && !username) {
1694 fstring fstr_user;
1695 fstring fstr_domain;
1697 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1698 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1699 printf("Error: Could not parse into "
1700 "domain and username\n");
1702 SAFE_FREE(username);
1703 SAFE_FREE(domain);
1704 username = smb_xstrdup(fstr_user);
1705 domain = smb_xstrdup(fstr_domain);
1708 if (opt_password) {
1709 DATA_BLOB nt_session_key, lm_session_key;
1710 struct samr_Password lm_pw, nt_pw;
1711 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1712 ZERO_STRUCT(user_session_key);
1713 ZERO_STRUCT(lm_key);
1715 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1716 nt_status = ntlm_password_check(mem_ctx,
1717 true, true, 0,
1718 &challenge,
1719 &lm_response,
1720 &nt_response,
1721 username,
1722 username,
1723 domain,
1724 &lm_pw, &nt_pw,
1725 &nt_session_key,
1726 &lm_session_key);
1727 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1728 if (ntlm_server_1_user_session_key) {
1729 if (nt_session_key.length == sizeof(user_session_key)) {
1730 memcpy(user_session_key,
1731 nt_session_key.data,
1732 sizeof(user_session_key));
1735 if (ntlm_server_1_lm_session_key) {
1736 if (lm_session_key.length == sizeof(lm_key)) {
1737 memcpy(lm_key,
1738 lm_session_key.data,
1739 sizeof(lm_key));
1742 TALLOC_FREE(mem_ctx);
1744 } else {
1745 uint8_t authoritative = 0;
1747 if (!domain) {
1748 domain = smb_xstrdup(get_winbind_domain());
1751 if (ntlm_server_1_lm_session_key)
1752 flags |= WBFLAG_PAM_LMKEY;
1754 if (ntlm_server_1_user_session_key)
1755 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1757 nt_status = contact_winbind_auth_crap(username,
1758 domain,
1759 lp_netbios_name(),
1760 &challenge,
1761 &lm_response,
1762 &nt_response,
1763 flags, 0,
1764 lm_key,
1765 user_session_key,
1766 &authoritative,
1767 &error_string,
1768 NULL);
1771 if (!NT_STATUS_IS_OK(nt_status)) {
1772 printf("Authenticated: No\n");
1773 printf("Authentication-Error: %s\n.\n",
1774 error_string);
1775 } else {
1776 char *hex_lm_key;
1777 char *hex_user_session_key;
1779 printf("Authenticated: Yes\n");
1781 if (ntlm_server_1_lm_session_key
1782 && (!all_zero(lm_key,
1783 sizeof(lm_key)))) {
1784 hex_lm_key = hex_encode_talloc(NULL,
1785 (const unsigned char *)lm_key,
1786 sizeof(lm_key));
1787 printf("LANMAN-Session-Key: %s\n",
1788 hex_lm_key);
1789 TALLOC_FREE(hex_lm_key);
1792 if (ntlm_server_1_user_session_key
1793 && (!all_zero(user_session_key,
1794 sizeof(user_session_key)))) {
1795 hex_user_session_key = hex_encode_talloc(NULL,
1796 (const unsigned char *)user_session_key,
1797 sizeof(user_session_key));
1798 printf("User-Session-Key: %s\n",
1799 hex_user_session_key);
1800 TALLOC_FREE(hex_user_session_key);
1803 SAFE_FREE(error_string);
1805 /* clear out the state */
1806 challenge = data_blob_null;
1807 nt_response = data_blob_null;
1808 lm_response = data_blob_null;
1809 SAFE_FREE(full_username);
1810 SAFE_FREE(username);
1811 SAFE_FREE(domain);
1812 SAFE_FREE(plaintext_password);
1813 ntlm_server_1_user_session_key = False;
1814 ntlm_server_1_lm_session_key = False;
1815 printf(".\n");
1817 return;
1820 request = buf;
1822 /* Indicates a base64 encoded structure */
1823 parameter = strstr_m(request, ":: ");
1824 if (!parameter) {
1825 parameter = strstr_m(request, ": ");
1827 if (!parameter) {
1828 DEBUG(0, ("Parameter not found!\n"));
1829 printf("Error: Parameter not found!\n.\n");
1830 return;
1833 parameter[0] ='\0';
1834 parameter++;
1835 parameter[0] ='\0';
1836 parameter++;
1838 } else {
1839 parameter[0] ='\0';
1840 parameter++;
1841 parameter[0] ='\0';
1842 parameter++;
1843 parameter[0] ='\0';
1844 parameter++;
1846 base64_decode_inplace(parameter);
1849 if (strequal(request, "LANMAN-Challenge")) {
1850 challenge = strhex_to_data_blob(NULL, parameter);
1851 if (challenge.length != 8) {
1852 printf("Error: hex decode of %s failed! "
1853 "(got %d bytes, expected 8)\n.\n",
1854 parameter,
1855 (int)challenge.length);
1856 challenge = data_blob_null;
1858 } else if (strequal(request, "NT-Response")) {
1859 nt_response = strhex_to_data_blob(NULL, parameter);
1860 if (nt_response.length < 24) {
1861 printf("Error: hex decode of %s failed! "
1862 "(only got %d bytes, needed at least 24)\n.\n",
1863 parameter,
1864 (int)nt_response.length);
1865 nt_response = data_blob_null;
1867 } else if (strequal(request, "LANMAN-Response")) {
1868 lm_response = strhex_to_data_blob(NULL, parameter);
1869 if (lm_response.length != 24) {
1870 printf("Error: hex decode of %s failed! "
1871 "(got %d bytes, expected 24)\n.\n",
1872 parameter,
1873 (int)lm_response.length);
1874 lm_response = data_blob_null;
1876 } else if (strequal(request, "Password")) {
1877 plaintext_password = smb_xstrdup(parameter);
1878 } else if (strequal(request, "NT-Domain")) {
1879 domain = smb_xstrdup(parameter);
1880 } else if (strequal(request, "Username")) {
1881 username = smb_xstrdup(parameter);
1882 } else if (strequal(request, "Full-Username")) {
1883 full_username = smb_xstrdup(parameter);
1884 } else if (strequal(request, "Request-User-Session-Key")) {
1885 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1886 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1887 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1888 } else {
1889 printf("Error: Unknown request %s\n.\n", request);
1893 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
1894 struct loadparm_context *lp_ctx,
1895 struct ntlm_auth_state *state,
1896 char *buf, int length, void **private2)
1898 char *request, *parameter;
1899 static DATA_BLOB new_nt_pswd;
1900 static DATA_BLOB old_nt_hash_enc;
1901 static DATA_BLOB new_lm_pswd;
1902 static DATA_BLOB old_lm_hash_enc;
1903 static char *full_username = NULL;
1904 static char *username = NULL;
1905 static char *domain = NULL;
1906 static char *newpswd = NULL;
1907 static char *oldpswd = NULL;
1909 if (strequal(buf, ".")) {
1910 if(newpswd && oldpswd) {
1911 uchar old_nt_hash[16];
1912 uchar old_lm_hash[16];
1913 uchar new_nt_hash[16];
1914 uchar new_lm_hash[16];
1916 new_nt_pswd = data_blob(NULL, 516);
1917 old_nt_hash_enc = data_blob(NULL, 16);
1919 /* Calculate the MD4 hash (NT compatible) of the
1920 * password */
1921 E_md4hash(oldpswd, old_nt_hash);
1922 E_md4hash(newpswd, new_nt_hash);
1924 /* E_deshash returns false for 'long'
1925 passwords (> 14 DOS chars).
1927 Therefore, don't send a buffer
1928 encrypted with the truncated hash
1929 (it could allow an even easier
1930 attack on the password)
1932 Likewise, obey the admin's restriction
1935 if (lp_client_lanman_auth() &&
1936 E_deshash(newpswd, new_lm_hash) &&
1937 E_deshash(oldpswd, old_lm_hash)) {
1938 new_lm_pswd = data_blob(NULL, 516);
1939 old_lm_hash_enc = data_blob(NULL, 16);
1940 encode_pw_buffer(new_lm_pswd.data, newpswd,
1941 STR_UNICODE);
1943 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
1944 E_old_pw_hash(new_nt_hash, old_lm_hash,
1945 old_lm_hash_enc.data);
1946 } else {
1947 new_lm_pswd.data = NULL;
1948 new_lm_pswd.length = 0;
1949 old_lm_hash_enc.data = NULL;
1950 old_lm_hash_enc.length = 0;
1953 encode_pw_buffer(new_nt_pswd.data, newpswd,
1954 STR_UNICODE);
1956 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
1957 E_old_pw_hash(new_nt_hash, old_nt_hash,
1958 old_nt_hash_enc.data);
1961 if (!full_username && !username) {
1962 printf("Error: No username supplied!\n");
1963 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1964 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1965 printf("Error: No NT or LM password "
1966 "blobs supplied!\n");
1967 } else {
1968 char *error_string = NULL;
1970 if (full_username && !username) {
1971 fstring fstr_user;
1972 fstring fstr_domain;
1974 if (!parse_ntlm_auth_domain_user(full_username,
1975 fstr_user,
1976 fstr_domain)) {
1977 /* username might be 'tainted', don't
1978 * print into our new-line
1979 * deleimianted stream */
1980 printf("Error: Could not "
1981 "parse into domain and "
1982 "username\n");
1983 SAFE_FREE(username);
1984 username = smb_xstrdup(full_username);
1985 } else {
1986 SAFE_FREE(username);
1987 SAFE_FREE(domain);
1988 username = smb_xstrdup(fstr_user);
1989 domain = smb_xstrdup(fstr_domain);
1994 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1995 username, domain,
1996 new_nt_pswd,
1997 old_nt_hash_enc,
1998 new_lm_pswd,
1999 old_lm_hash_enc,
2000 &error_string))) {
2001 printf("Password-Change: No\n");
2002 printf("Password-Change-Error: %s\n.\n",
2003 error_string);
2004 } else {
2005 printf("Password-Change: Yes\n");
2008 SAFE_FREE(error_string);
2010 /* clear out the state */
2011 new_nt_pswd = data_blob_null;
2012 old_nt_hash_enc = data_blob_null;
2013 new_lm_pswd = data_blob_null;
2014 old_nt_hash_enc = data_blob_null;
2015 SAFE_FREE(full_username);
2016 SAFE_FREE(username);
2017 SAFE_FREE(domain);
2018 SAFE_FREE(newpswd);
2019 SAFE_FREE(oldpswd);
2020 printf(".\n");
2022 return;
2025 request = buf;
2027 /* Indicates a base64 encoded structure */
2028 parameter = strstr_m(request, ":: ");
2029 if (!parameter) {
2030 parameter = strstr_m(request, ": ");
2032 if (!parameter) {
2033 DEBUG(0, ("Parameter not found!\n"));
2034 printf("Error: Parameter not found!\n.\n");
2035 return;
2038 parameter[0] ='\0';
2039 parameter++;
2040 parameter[0] ='\0';
2041 parameter++;
2042 } else {
2043 parameter[0] ='\0';
2044 parameter++;
2045 parameter[0] ='\0';
2046 parameter++;
2047 parameter[0] ='\0';
2048 parameter++;
2050 base64_decode_inplace(parameter);
2053 if (strequal(request, "new-nt-password-blob")) {
2054 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2055 if (new_nt_pswd.length != 516) {
2056 printf("Error: hex decode of %s failed! "
2057 "(got %d bytes, expected 516)\n.\n",
2058 parameter,
2059 (int)new_nt_pswd.length);
2060 new_nt_pswd = data_blob_null;
2062 } else if (strequal(request, "old-nt-hash-blob")) {
2063 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2064 if (old_nt_hash_enc.length != 16) {
2065 printf("Error: hex decode of %s failed! "
2066 "(got %d bytes, expected 16)\n.\n",
2067 parameter,
2068 (int)old_nt_hash_enc.length);
2069 old_nt_hash_enc = data_blob_null;
2071 } else if (strequal(request, "new-lm-password-blob")) {
2072 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2073 if (new_lm_pswd.length != 516) {
2074 printf("Error: hex decode of %s failed! "
2075 "(got %d bytes, expected 516)\n.\n",
2076 parameter,
2077 (int)new_lm_pswd.length);
2078 new_lm_pswd = data_blob_null;
2081 else if (strequal(request, "old-lm-hash-blob")) {
2082 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2083 if (old_lm_hash_enc.length != 16)
2085 printf("Error: hex decode of %s failed! "
2086 "(got %d bytes, expected 16)\n.\n",
2087 parameter,
2088 (int)old_lm_hash_enc.length);
2089 old_lm_hash_enc = data_blob_null;
2091 } else if (strequal(request, "nt-domain")) {
2092 domain = smb_xstrdup(parameter);
2093 } else if(strequal(request, "username")) {
2094 username = smb_xstrdup(parameter);
2095 } else if(strequal(request, "full-username")) {
2096 username = smb_xstrdup(parameter);
2097 } else if(strequal(request, "new-password")) {
2098 newpswd = smb_xstrdup(parameter);
2099 } else if (strequal(request, "old-password")) {
2100 oldpswd = smb_xstrdup(parameter);
2101 } else {
2102 printf("Error: Unknown request %s\n.\n", request);
2106 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2107 struct loadparm_context *lp_ctx,
2108 struct ntlm_auth_state *state,
2109 stdio_helper_function fn, void **private2)
2111 char *buf;
2112 char tmp[INITIAL_BUFFER_SIZE+1];
2113 int length, buf_size = 0;
2114 char *c;
2116 buf = talloc_strdup(state->mem_ctx, "");
2117 if (!buf) {
2118 DEBUG(0, ("Failed to allocate input buffer.\n"));
2119 fprintf(stderr, "ERR\n");
2120 exit(1);
2123 do {
2125 /* this is not a typo - x_fgets doesn't work too well under
2126 * squid */
2127 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2128 if (ferror(stdin)) {
2129 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2130 "(%s)\n", ferror(stdin),
2131 strerror(ferror(stdin))));
2133 exit(1);
2135 exit(0);
2138 buf = talloc_strdup_append_buffer(buf, tmp);
2139 buf_size += INITIAL_BUFFER_SIZE;
2141 if (buf_size > MAX_BUFFER_SIZE) {
2142 DEBUG(2, ("Oversized message\n"));
2143 fprintf(stderr, "ERR\n");
2144 talloc_free(buf);
2145 return;
2148 c = strchr(buf, '\n');
2149 } while (c == NULL);
2151 *c = '\0';
2152 length = c-buf;
2154 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2156 if (buf[0] == '\0') {
2157 DEBUG(2, ("Invalid Request\n"));
2158 fprintf(stderr, "ERR\n");
2159 talloc_free(buf);
2160 return;
2163 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2164 talloc_free(buf);
2168 static void squid_stream(enum stdio_helper_mode stdio_mode,
2169 struct loadparm_context *lp_ctx,
2170 stdio_helper_function fn) {
2171 TALLOC_CTX *mem_ctx;
2172 struct ntlm_auth_state *state;
2174 /* initialize FDescs */
2175 setbuf(stdout, NULL);
2176 setbuf(stderr, NULL);
2178 mem_ctx = talloc_init("ntlm_auth");
2179 if (!mem_ctx) {
2180 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2181 fprintf(stderr, "ERR\n");
2182 exit(1);
2185 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2186 if (!state) {
2187 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2188 fprintf(stderr, "ERR\n");
2189 exit(1);
2192 state->mem_ctx = mem_ctx;
2193 state->helper_mode = stdio_mode;
2195 while(1) {
2196 TALLOC_CTX *frame = talloc_stackframe();
2197 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2198 TALLOC_FREE(frame);
2203 /* Authenticate a user with a challenge/response */
2205 static bool check_auth_crap(void)
2207 NTSTATUS nt_status;
2208 uint32_t flags = 0;
2209 char lm_key[8];
2210 char user_session_key[16];
2211 char *hex_lm_key;
2212 char *hex_user_session_key;
2213 char *error_string;
2214 uint8_t authoritative = 0;
2216 setbuf(stdout, NULL);
2218 if (request_lm_key)
2219 flags |= WBFLAG_PAM_LMKEY;
2221 if (request_user_session_key)
2222 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2224 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2226 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2227 opt_workstation,
2228 &opt_challenge,
2229 &opt_lm_response,
2230 &opt_nt_response,
2231 flags, 0,
2232 (unsigned char *)lm_key,
2233 (unsigned char *)user_session_key,
2234 &authoritative,
2235 &error_string, NULL);
2237 if (!NT_STATUS_IS_OK(nt_status)) {
2238 printf("%s (0x%x)\n", error_string,
2239 NT_STATUS_V(nt_status));
2240 SAFE_FREE(error_string);
2241 return False;
2244 if (request_lm_key
2245 && (!all_zero((uint8_t *)lm_key, sizeof(lm_key)))) {
2246 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2247 sizeof(lm_key));
2248 printf("LM_KEY: %s\n", hex_lm_key);
2249 TALLOC_FREE(hex_lm_key);
2251 if (request_user_session_key
2252 && (!all_zero((uint8_t *)user_session_key,
2253 sizeof(user_session_key)))) {
2254 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2255 sizeof(user_session_key));
2256 printf("NT_KEY: %s\n", hex_user_session_key);
2257 TALLOC_FREE(hex_user_session_key);
2260 return True;
2263 /* Main program */
2265 enum {
2266 OPT_USERNAME = 1000,
2267 OPT_DOMAIN,
2268 OPT_WORKSTATION,
2269 OPT_CHALLENGE,
2270 OPT_RESPONSE,
2271 OPT_LM,
2272 OPT_NT,
2273 OPT_PASSWORD,
2274 OPT_LM_KEY,
2275 OPT_USER_SESSION_KEY,
2276 OPT_DIAGNOSTICS,
2277 OPT_REQUIRE_MEMBERSHIP,
2278 OPT_USE_CACHED_CREDS,
2279 OPT_ALLOW_MSCHAPV2,
2280 OPT_PAM_WINBIND_CONF,
2281 OPT_TARGET_SERVICE,
2282 OPT_TARGET_HOSTNAME,
2283 OPT_OFFLINE_LOGON
2286 int main(int argc, const char **argv)
2288 TALLOC_CTX *frame = talloc_stackframe();
2289 int opt;
2290 const char *helper_protocol = NULL;
2291 int diagnostics = 0;
2293 const char *hex_challenge = NULL;
2294 const char *hex_lm_response = NULL;
2295 const char *hex_nt_response = NULL;
2296 struct loadparm_context *lp_ctx;
2297 poptContext pc;
2299 /* NOTE: DO NOT change this interface without considering the implications!
2300 This is an external interface, which other programs will use to interact
2301 with this helper.
2304 /* We do not use single-letter command abbreviations, because they harm future
2305 interface stability. */
2307 struct poptOption long_options[] = {
2308 POPT_AUTOHELP
2309 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2310 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2311 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2312 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2313 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2314 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2315 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2316 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2317 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2318 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2319 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2320 { "allow-mschapv2", 0, POPT_ARG_NONE, &opt_allow_mschapv2, OPT_ALLOW_MSCHAPV2, "Explicitly allow MSCHAPv2" },
2321 { "offline-logon", 0, POPT_ARG_NONE, &offline_logon,
2322 OPT_OFFLINE_LOGON,
2323 "Use cached passwords when DC is offline"},
2324 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2325 OPT_DIAGNOSTICS,
2326 "Perform diagnostics on the authentication chain"},
2327 { "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" },
2328 { "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" },
2329 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2330 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2331 POPT_COMMON_CONFIGFILE
2332 POPT_COMMON_VERSION
2333 POPT_COMMON_OPTION
2334 POPT_TABLEEND
2337 /* Samba client initialisation */
2338 smb_init_locale();
2340 setup_logging("ntlm_auth", DEBUG_STDERR);
2341 fault_setup();
2343 /* Parse options */
2345 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2347 /* Parse command line options */
2349 if (argc == 1) {
2350 poptPrintHelp(pc, stderr, 0);
2351 return 1;
2354 while((opt = poptGetNextOpt(pc)) != -1) {
2355 /* Get generic config options like --configfile */
2358 poptFreeContext(pc);
2360 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2361 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2362 get_dyn_CONFIGFILE(), strerror(errno));
2363 exit(1);
2366 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2367 POPT_CONTEXT_KEEP_FIRST);
2369 while((opt = poptGetNextOpt(pc)) != -1) {
2370 switch (opt) {
2371 case OPT_CHALLENGE:
2372 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2373 if (opt_challenge.length != 8) {
2374 fprintf(stderr, "hex decode of %s failed! "
2375 "(only got %d bytes)\n",
2376 hex_challenge,
2377 (int)opt_challenge.length);
2378 exit(1);
2380 break;
2381 case OPT_LM:
2382 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2383 if (opt_lm_response.length != 24) {
2384 fprintf(stderr, "hex decode of %s failed! "
2385 "(only got %d bytes)\n",
2386 hex_lm_response,
2387 (int)opt_lm_response.length);
2388 exit(1);
2390 break;
2392 case OPT_NT:
2393 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2394 if (opt_nt_response.length < 24) {
2395 fprintf(stderr, "hex decode of %s failed! "
2396 "(only got %d bytes)\n",
2397 hex_nt_response,
2398 (int)opt_nt_response.length);
2399 exit(1);
2401 break;
2403 case OPT_REQUIRE_MEMBERSHIP:
2404 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2405 require_membership_of_sid = require_membership_of;
2407 break;
2411 if (opt_username) {
2412 char *domain = SMB_STRDUP(opt_username);
2413 char *p = strchr_m(domain, *lp_winbind_separator());
2414 if (p) {
2415 opt_username = p+1;
2416 *p = '\0';
2417 if (opt_domain && !strequal(opt_domain, domain)) {
2418 fprintf(stderr, "Domain specified in username (%s) "
2419 "doesn't match specified domain (%s)!\n\n",
2420 domain, opt_domain);
2421 poptPrintHelp(pc, stderr, 0);
2422 exit(1);
2424 opt_domain = domain;
2425 } else {
2426 SAFE_FREE(domain);
2430 /* Note: if opt_domain is "" then send no domain */
2431 if (opt_domain == NULL) {
2432 opt_domain = get_winbind_domain();
2435 if (opt_workstation == NULL) {
2436 opt_workstation = "";
2439 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2440 if (lp_ctx == NULL) {
2441 fprintf(stderr, "loadparm_init_s3() failed!\n");
2442 exit(1);
2445 if (helper_protocol) {
2446 int i;
2447 for (i=0; i<NUM_HELPER_MODES; i++) {
2448 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2449 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2450 exit(0);
2453 fprintf(stderr, "unknown helper protocol [%s]\n\n"
2454 "Valid helper protools:\n\n", helper_protocol);
2456 for (i=0; i<NUM_HELPER_MODES; i++) {
2457 fprintf(stderr, "%s\n",
2458 stdio_helper_protocols[i].name);
2461 exit(1);
2464 if (!opt_username || !*opt_username) {
2465 fprintf(stderr, "username must be specified!\n\n");
2466 poptPrintHelp(pc, stderr, 0);
2467 exit(1);
2470 if (opt_challenge.length) {
2471 if (!check_auth_crap()) {
2472 exit(1);
2474 exit(0);
2477 if (!opt_password) {
2478 char pwd[256] = {0};
2479 int rc;
2481 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2482 if (rc == 0) {
2483 opt_password = SMB_STRDUP(pwd);
2487 if (diagnostics) {
2488 if (!diagnose_ntlm_auth()) {
2489 return 1;
2491 } else {
2492 fstring user;
2494 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2495 if (!check_plaintext_auth(user, opt_password, True)) {
2496 return 1;
2500 /* Exit code */
2502 poptFreeContext(pc);
2503 TALLOC_FREE(frame);
2504 return 0;