s3: client: Add new utility function client_clean_name().
[Samba.git] / source3 / utils / ntlm_auth.c
blob5a10e27719f8b05d099d0728687c6a093ffd25bb
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 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1311 if (*private1) {
1312 state = (struct gensec_ntlm_state *)*private1;
1313 } else {
1314 state = talloc_zero(NULL, struct gensec_ntlm_state);
1315 if (!state) {
1316 printf("BH No Memory\n");
1317 exit(1);
1319 *private1 = state;
1320 if (opt_password) {
1321 state->set_password = opt_password;
1325 if (strlen(buf) < 2) {
1326 DEBUG(1, ("query [%s] invalid", buf));
1327 printf("BH Query invalid\n");
1328 talloc_free(mem_ctx);
1329 return;
1332 if (strlen(buf) > 3) {
1333 if(strncmp(buf, "SF ", 3) == 0) {
1334 DEBUG(10, ("Setting flags to negotiate\n"));
1335 talloc_free(want_feature_list);
1336 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1337 printf("OK\n");
1338 talloc_free(mem_ctx);
1339 return;
1341 in = base64_decode_data_blob_talloc(mem_ctx, buf + 3);
1342 } else {
1343 in = data_blob(NULL, 0);
1346 if (strncmp(buf, "YR", 2) == 0) {
1347 if (state->gensec_state) {
1348 talloc_free(state->gensec_state);
1349 state->gensec_state = NULL;
1351 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1352 /* Just return BH, like ntlm_auth from Samba 3 does. */
1353 printf("BH Command expected\n");
1354 talloc_free(mem_ctx);
1355 return;
1356 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1357 (strncmp(buf, "KK ", 3) != 0) &&
1358 (strncmp(buf, "AF ", 3) != 0) &&
1359 (strncmp(buf, "NA ", 3) != 0) &&
1360 (strncmp(buf, "UG", 2) != 0) &&
1361 (strncmp(buf, "PW ", 3) != 0) &&
1362 (strncmp(buf, "GK", 2) != 0) &&
1363 (strncmp(buf, "GF", 2) != 0)) {
1364 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1365 printf("BH SPNEGO request invalid prefix\n");
1366 talloc_free(mem_ctx);
1367 return;
1370 /* setup gensec */
1371 if (!(state->gensec_state)) {
1372 switch (stdio_helper_mode) {
1373 case GSS_SPNEGO_CLIENT:
1375 * cached credentials are only supported by
1376 * NTLMSSP_CLIENT_1 for now.
1378 use_cached_creds = false;
1379 /* fall through */
1380 case NTLMSSP_CLIENT_1:
1381 /* setup the client side */
1383 if (state->set_password != NULL) {
1384 use_cached_creds = false;
1387 if (use_cached_creds) {
1388 struct wbcCredentialCacheParams params;
1389 struct wbcCredentialCacheInfo *info = NULL;
1390 struct wbcAuthErrorInfo *error = NULL;
1391 wbcErr wbc_status;
1393 params.account_name = opt_username;
1394 params.domain_name = opt_domain;
1395 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1396 params.num_blobs = 0;
1397 params.blobs = NULL;
1399 wbc_status = wbcCredentialCache(&params, &info,
1400 &error);
1401 wbcFreeMemory(error);
1402 if (!WBC_ERROR_IS_OK(wbc_status)) {
1403 use_cached_creds = false;
1405 wbcFreeMemory(info);
1408 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1409 &state->gensec_state);
1410 if (!NT_STATUS_IS_OK(nt_status)) {
1411 printf("BH GENSEC mech failed to start: %s\n",
1412 nt_errstr(nt_status));
1413 talloc_free(mem_ctx);
1414 return;
1417 creds = cli_credentials_init(state->gensec_state);
1418 cli_credentials_set_conf(creds, lp_ctx);
1419 if (opt_username) {
1420 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1422 if (opt_domain) {
1423 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1425 if (use_cached_creds) {
1426 gensec_want_feature(state->gensec_state,
1427 GENSEC_FEATURE_NTLM_CCACHE);
1428 } else if (state->set_password) {
1429 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1430 } else {
1431 cli_credentials_set_password_callback(creds, get_password);
1433 if (opt_workstation) {
1434 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1437 gensec_set_credentials(state->gensec_state, creds);
1439 break;
1440 case GSS_SPNEGO_SERVER:
1441 case SQUID_2_5_NTLMSSP:
1443 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1444 &state->gensec_state);
1445 if (!NT_STATUS_IS_OK(nt_status)) {
1446 printf("BH GENSEC mech failed to start: %s\n",
1447 nt_errstr(nt_status));
1448 talloc_free(mem_ctx);
1449 return;
1451 break;
1453 default:
1454 talloc_free(mem_ctx);
1455 abort();
1458 gensec_want_feature_list(state->gensec_state, want_feature_list);
1460 /* Session info is not complete, do not pass to auth log */
1461 gensec_want_feature(state->gensec_state, GENSEC_FEATURE_NO_AUTHZ_LOG);
1463 switch (stdio_helper_mode) {
1464 case GSS_SPNEGO_CLIENT:
1465 case GSS_SPNEGO_SERVER:
1466 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1467 if (!in.length) {
1468 first = true;
1470 break;
1471 case NTLMSSP_CLIENT_1:
1472 if (!in.length) {
1473 first = true;
1475 /* fall through */
1476 case SQUID_2_5_NTLMSSP:
1477 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1478 break;
1479 default:
1480 talloc_free(mem_ctx);
1481 abort();
1484 if (!NT_STATUS_IS_OK(nt_status)) {
1485 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1486 printf("BH GENSEC mech failed to start\n");
1487 talloc_free(mem_ctx);
1488 return;
1493 /* update */
1495 if (strncmp(buf, "PW ", 3) == 0) {
1496 state->set_password = talloc_strndup(state,
1497 (const char *)in.data,
1498 in.length);
1500 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1501 state->set_password,
1502 CRED_SPECIFIED);
1503 printf("OK\n");
1504 talloc_free(mem_ctx);
1505 return;
1508 if (strncmp(buf, "GK", 2) == 0) {
1509 char *base64_key;
1510 DEBUG(10, ("Requested session key\n"));
1511 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1512 if(!NT_STATUS_IS_OK(nt_status)) {
1513 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1514 printf("BH No session key\n");
1515 talloc_free(mem_ctx);
1516 return;
1517 } else {
1518 base64_key = base64_encode_data_blob(state, session_key);
1519 SMB_ASSERT(base64_key != NULL);
1520 printf("GK %s\n", base64_key);
1521 talloc_free(base64_key);
1523 talloc_free(mem_ctx);
1524 return;
1527 if (strncmp(buf, "GF", 2) == 0) {
1528 uint32_t neg_flags;
1530 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1532 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1533 if (neg_flags == 0) {
1534 printf("BH\n");
1535 talloc_free(mem_ctx);
1536 return;
1539 printf("GF 0x%08x\n", neg_flags);
1540 talloc_free(mem_ctx);
1541 return;
1544 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1546 /* don't leak 'bad password'/'no such user' info to the network client */
1547 nt_status = nt_status_squash(nt_status);
1549 if (out.length) {
1550 out_base64 = base64_encode_data_blob(mem_ctx, out);
1551 SMB_ASSERT(out_base64 != NULL);
1552 } else {
1553 out_base64 = NULL;
1556 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1557 reply_arg = "*";
1558 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1559 reply_code = "YR";
1560 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1561 reply_code = "KK";
1562 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1563 reply_code = "TT";
1564 } else {
1565 abort();
1569 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1570 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1571 reply_arg = nt_errstr(nt_status);
1572 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1573 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1574 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1575 reply_arg = nt_errstr(nt_status);
1576 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1577 } else if (!NT_STATUS_IS_OK(nt_status)) {
1578 reply_code = "NA";
1579 reply_arg = nt_errstr(nt_status);
1580 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1581 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1582 struct auth_session_info *session_info;
1584 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1585 if (!NT_STATUS_IS_OK(nt_status)) {
1586 reply_code = "BH Failed to retrive session info";
1587 reply_arg = nt_errstr(nt_status);
1588 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1589 } else {
1591 reply_code = "AF";
1592 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1593 if (reply_arg == NULL) {
1594 reply_code = "BH out of memory";
1595 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1597 talloc_free(session_info);
1599 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1600 reply_code = "AF";
1601 reply_arg = out_base64;
1602 } else {
1603 abort();
1606 switch (stdio_helper_mode) {
1607 case GSS_SPNEGO_SERVER:
1608 printf("%s %s %s\n", reply_code,
1609 out_base64 ? out_base64 : "*",
1610 reply_arg ? reply_arg : "*");
1611 break;
1612 default:
1613 if (out_base64) {
1614 printf("%s %s\n", reply_code, out_base64);
1615 } else if (reply_arg) {
1616 printf("%s %s\n", reply_code, reply_arg);
1617 } else {
1618 printf("%s\n", reply_code);
1622 talloc_free(mem_ctx);
1623 return;
1626 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1627 struct loadparm_context *lp_ctx,
1628 struct ntlm_auth_state *state,
1629 char *buf, int length, void **private2)
1631 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1632 return;
1635 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1636 struct loadparm_context *lp_ctx,
1637 struct ntlm_auth_state *state,
1638 char *buf, int length, void **private2)
1640 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1641 return;
1644 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1645 struct loadparm_context *lp_ctx,
1646 struct ntlm_auth_state *state,
1647 char *buf, int length, void **private2)
1649 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1650 return;
1653 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1654 struct loadparm_context *lp_ctx,
1655 struct ntlm_auth_state *state,
1656 char *buf, int length, void **private2)
1658 char *request, *parameter;
1659 static DATA_BLOB challenge;
1660 static DATA_BLOB lm_response;
1661 static DATA_BLOB nt_response;
1662 static char *full_username;
1663 static char *username;
1664 static char *domain;
1665 static char *plaintext_password;
1666 static bool ntlm_server_1_user_session_key;
1667 static bool ntlm_server_1_lm_session_key;
1669 if (strequal(buf, ".")) {
1670 if (!full_username && !username) {
1671 printf("Error: No username supplied!\n");
1672 } else if (plaintext_password) {
1673 /* handle this request as plaintext */
1674 if (!full_username) {
1675 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1676 printf("Error: Out of memory in "
1677 "asprintf!\n.\n");
1678 return;
1681 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1682 printf("Authenticated: Yes\n");
1683 } else {
1684 printf("Authenticated: No\n");
1686 } else if (!lm_response.data && !nt_response.data) {
1687 printf("Error: No password supplied!\n");
1688 } else if (!challenge.data) {
1689 printf("Error: No lanman-challenge supplied!\n");
1690 } else {
1691 char *error_string = NULL;
1692 uchar lm_key[8];
1693 uchar user_session_key[16];
1694 uint32_t flags = 0;
1695 NTSTATUS nt_status;
1696 if (full_username && !username) {
1697 fstring fstr_user;
1698 fstring fstr_domain;
1700 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1701 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1702 printf("Error: Could not parse into "
1703 "domain and username\n");
1705 SAFE_FREE(username);
1706 SAFE_FREE(domain);
1707 username = smb_xstrdup(fstr_user);
1708 domain = smb_xstrdup(fstr_domain);
1711 if (opt_password) {
1712 DATA_BLOB nt_session_key, lm_session_key;
1713 struct samr_Password lm_pw, nt_pw;
1714 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1715 ZERO_STRUCT(user_session_key);
1716 ZERO_STRUCT(lm_key);
1718 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1719 nt_status = ntlm_password_check(mem_ctx,
1720 true, true, 0,
1721 &challenge,
1722 &lm_response,
1723 &nt_response,
1724 username,
1725 username,
1726 domain,
1727 &lm_pw, &nt_pw,
1728 &nt_session_key,
1729 &lm_session_key);
1730 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1731 if (ntlm_server_1_user_session_key) {
1732 if (nt_session_key.length == sizeof(user_session_key)) {
1733 memcpy(user_session_key,
1734 nt_session_key.data,
1735 sizeof(user_session_key));
1738 if (ntlm_server_1_lm_session_key) {
1739 if (lm_session_key.length == sizeof(lm_key)) {
1740 memcpy(lm_key,
1741 lm_session_key.data,
1742 sizeof(lm_key));
1745 TALLOC_FREE(mem_ctx);
1747 } else {
1748 uint8_t authoritative = 0;
1750 if (!domain) {
1751 domain = smb_xstrdup(get_winbind_domain());
1754 if (ntlm_server_1_lm_session_key)
1755 flags |= WBFLAG_PAM_LMKEY;
1757 if (ntlm_server_1_user_session_key)
1758 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1760 nt_status = contact_winbind_auth_crap(username,
1761 domain,
1762 lp_netbios_name(),
1763 &challenge,
1764 &lm_response,
1765 &nt_response,
1766 flags, 0,
1767 lm_key,
1768 user_session_key,
1769 &authoritative,
1770 &error_string,
1771 NULL);
1774 if (!NT_STATUS_IS_OK(nt_status)) {
1775 printf("Authenticated: No\n");
1776 printf("Authentication-Error: %s\n.\n",
1777 error_string);
1778 } else {
1779 char *hex_lm_key;
1780 char *hex_user_session_key;
1782 printf("Authenticated: Yes\n");
1784 if (ntlm_server_1_lm_session_key
1785 && (!all_zero(lm_key,
1786 sizeof(lm_key)))) {
1787 hex_lm_key = hex_encode_talloc(NULL,
1788 (const unsigned char *)lm_key,
1789 sizeof(lm_key));
1790 printf("LANMAN-Session-Key: %s\n",
1791 hex_lm_key);
1792 TALLOC_FREE(hex_lm_key);
1795 if (ntlm_server_1_user_session_key
1796 && (!all_zero(user_session_key,
1797 sizeof(user_session_key)))) {
1798 hex_user_session_key = hex_encode_talloc(NULL,
1799 (const unsigned char *)user_session_key,
1800 sizeof(user_session_key));
1801 printf("User-Session-Key: %s\n",
1802 hex_user_session_key);
1803 TALLOC_FREE(hex_user_session_key);
1806 SAFE_FREE(error_string);
1808 /* clear out the state */
1809 challenge = data_blob_null;
1810 nt_response = data_blob_null;
1811 lm_response = data_blob_null;
1812 SAFE_FREE(full_username);
1813 SAFE_FREE(username);
1814 SAFE_FREE(domain);
1815 SAFE_FREE(plaintext_password);
1816 ntlm_server_1_user_session_key = False;
1817 ntlm_server_1_lm_session_key = False;
1818 printf(".\n");
1820 return;
1823 request = buf;
1825 /* Indicates a base64 encoded structure */
1826 parameter = strstr_m(request, ":: ");
1827 if (!parameter) {
1828 parameter = strstr_m(request, ": ");
1830 if (!parameter) {
1831 DEBUG(0, ("Parameter not found!\n"));
1832 printf("Error: Parameter not found!\n.\n");
1833 return;
1836 parameter[0] ='\0';
1837 parameter++;
1838 parameter[0] ='\0';
1839 parameter++;
1841 } else {
1842 parameter[0] ='\0';
1843 parameter++;
1844 parameter[0] ='\0';
1845 parameter++;
1846 parameter[0] ='\0';
1847 parameter++;
1849 base64_decode_inplace(parameter);
1852 if (strequal(request, "LANMAN-Challenge")) {
1853 challenge = strhex_to_data_blob(NULL, parameter);
1854 if (challenge.length != 8) {
1855 printf("Error: hex decode of %s failed! "
1856 "(got %d bytes, expected 8)\n.\n",
1857 parameter,
1858 (int)challenge.length);
1859 challenge = data_blob_null;
1861 } else if (strequal(request, "NT-Response")) {
1862 nt_response = strhex_to_data_blob(NULL, parameter);
1863 if (nt_response.length < 24) {
1864 printf("Error: hex decode of %s failed! "
1865 "(only got %d bytes, needed at least 24)\n.\n",
1866 parameter,
1867 (int)nt_response.length);
1868 nt_response = data_blob_null;
1870 } else if (strequal(request, "LANMAN-Response")) {
1871 lm_response = strhex_to_data_blob(NULL, parameter);
1872 if (lm_response.length != 24) {
1873 printf("Error: hex decode of %s failed! "
1874 "(got %d bytes, expected 24)\n.\n",
1875 parameter,
1876 (int)lm_response.length);
1877 lm_response = data_blob_null;
1879 } else if (strequal(request, "Password")) {
1880 plaintext_password = smb_xstrdup(parameter);
1881 } else if (strequal(request, "NT-Domain")) {
1882 domain = smb_xstrdup(parameter);
1883 } else if (strequal(request, "Username")) {
1884 username = smb_xstrdup(parameter);
1885 } else if (strequal(request, "Full-Username")) {
1886 full_username = smb_xstrdup(parameter);
1887 } else if (strequal(request, "Request-User-Session-Key")) {
1888 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1889 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1890 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1891 } else {
1892 printf("Error: Unknown request %s\n.\n", request);
1896 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
1897 struct loadparm_context *lp_ctx,
1898 struct ntlm_auth_state *state,
1899 char *buf, int length, void **private2)
1901 char *request, *parameter;
1902 static DATA_BLOB new_nt_pswd;
1903 static DATA_BLOB old_nt_hash_enc;
1904 static DATA_BLOB new_lm_pswd;
1905 static DATA_BLOB old_lm_hash_enc;
1906 static char *full_username = NULL;
1907 static char *username = NULL;
1908 static char *domain = NULL;
1909 static char *newpswd = NULL;
1910 static char *oldpswd = NULL;
1912 if (strequal(buf, ".")) {
1913 if(newpswd && oldpswd) {
1914 uchar old_nt_hash[16];
1915 uchar old_lm_hash[16];
1916 uchar new_nt_hash[16];
1917 uchar new_lm_hash[16];
1919 new_nt_pswd = data_blob(NULL, 516);
1920 old_nt_hash_enc = data_blob(NULL, 16);
1922 /* Calculate the MD4 hash (NT compatible) of the
1923 * password */
1924 E_md4hash(oldpswd, old_nt_hash);
1925 E_md4hash(newpswd, new_nt_hash);
1927 /* E_deshash returns false for 'long'
1928 passwords (> 14 DOS chars).
1930 Therefore, don't send a buffer
1931 encrypted with the truncated hash
1932 (it could allow an even easier
1933 attack on the password)
1935 Likewise, obey the admin's restriction
1938 if (lp_client_lanman_auth() &&
1939 E_deshash(newpswd, new_lm_hash) &&
1940 E_deshash(oldpswd, old_lm_hash)) {
1941 new_lm_pswd = data_blob(NULL, 516);
1942 old_lm_hash_enc = data_blob(NULL, 16);
1943 encode_pw_buffer(new_lm_pswd.data, newpswd,
1944 STR_UNICODE);
1946 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
1947 E_old_pw_hash(new_nt_hash, old_lm_hash,
1948 old_lm_hash_enc.data);
1949 } else {
1950 new_lm_pswd.data = NULL;
1951 new_lm_pswd.length = 0;
1952 old_lm_hash_enc.data = NULL;
1953 old_lm_hash_enc.length = 0;
1956 encode_pw_buffer(new_nt_pswd.data, newpswd,
1957 STR_UNICODE);
1959 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
1960 E_old_pw_hash(new_nt_hash, old_nt_hash,
1961 old_nt_hash_enc.data);
1964 if (!full_username && !username) {
1965 printf("Error: No username supplied!\n");
1966 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1967 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1968 printf("Error: No NT or LM password "
1969 "blobs supplied!\n");
1970 } else {
1971 char *error_string = NULL;
1973 if (full_username && !username) {
1974 fstring fstr_user;
1975 fstring fstr_domain;
1977 if (!parse_ntlm_auth_domain_user(full_username,
1978 fstr_user,
1979 fstr_domain)) {
1980 /* username might be 'tainted', don't
1981 * print into our new-line
1982 * deleimianted stream */
1983 printf("Error: Could not "
1984 "parse into domain and "
1985 "username\n");
1986 SAFE_FREE(username);
1987 username = smb_xstrdup(full_username);
1988 } else {
1989 SAFE_FREE(username);
1990 SAFE_FREE(domain);
1991 username = smb_xstrdup(fstr_user);
1992 domain = smb_xstrdup(fstr_domain);
1997 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1998 username, domain,
1999 new_nt_pswd,
2000 old_nt_hash_enc,
2001 new_lm_pswd,
2002 old_lm_hash_enc,
2003 &error_string))) {
2004 printf("Password-Change: No\n");
2005 printf("Password-Change-Error: %s\n.\n",
2006 error_string);
2007 } else {
2008 printf("Password-Change: Yes\n");
2011 SAFE_FREE(error_string);
2013 /* clear out the state */
2014 new_nt_pswd = data_blob_null;
2015 old_nt_hash_enc = data_blob_null;
2016 new_lm_pswd = data_blob_null;
2017 old_nt_hash_enc = data_blob_null;
2018 SAFE_FREE(full_username);
2019 SAFE_FREE(username);
2020 SAFE_FREE(domain);
2021 SAFE_FREE(newpswd);
2022 SAFE_FREE(oldpswd);
2023 printf(".\n");
2025 return;
2028 request = buf;
2030 /* Indicates a base64 encoded structure */
2031 parameter = strstr_m(request, ":: ");
2032 if (!parameter) {
2033 parameter = strstr_m(request, ": ");
2035 if (!parameter) {
2036 DEBUG(0, ("Parameter not found!\n"));
2037 printf("Error: Parameter not found!\n.\n");
2038 return;
2041 parameter[0] ='\0';
2042 parameter++;
2043 parameter[0] ='\0';
2044 parameter++;
2045 } else {
2046 parameter[0] ='\0';
2047 parameter++;
2048 parameter[0] ='\0';
2049 parameter++;
2050 parameter[0] ='\0';
2051 parameter++;
2053 base64_decode_inplace(parameter);
2056 if (strequal(request, "new-nt-password-blob")) {
2057 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2058 if (new_nt_pswd.length != 516) {
2059 printf("Error: hex decode of %s failed! "
2060 "(got %d bytes, expected 516)\n.\n",
2061 parameter,
2062 (int)new_nt_pswd.length);
2063 new_nt_pswd = data_blob_null;
2065 } else if (strequal(request, "old-nt-hash-blob")) {
2066 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2067 if (old_nt_hash_enc.length != 16) {
2068 printf("Error: hex decode of %s failed! "
2069 "(got %d bytes, expected 16)\n.\n",
2070 parameter,
2071 (int)old_nt_hash_enc.length);
2072 old_nt_hash_enc = data_blob_null;
2074 } else if (strequal(request, "new-lm-password-blob")) {
2075 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2076 if (new_lm_pswd.length != 516) {
2077 printf("Error: hex decode of %s failed! "
2078 "(got %d bytes, expected 516)\n.\n",
2079 parameter,
2080 (int)new_lm_pswd.length);
2081 new_lm_pswd = data_blob_null;
2084 else if (strequal(request, "old-lm-hash-blob")) {
2085 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2086 if (old_lm_hash_enc.length != 16)
2088 printf("Error: hex decode of %s failed! "
2089 "(got %d bytes, expected 16)\n.\n",
2090 parameter,
2091 (int)old_lm_hash_enc.length);
2092 old_lm_hash_enc = data_blob_null;
2094 } else if (strequal(request, "nt-domain")) {
2095 domain = smb_xstrdup(parameter);
2096 } else if(strequal(request, "username")) {
2097 username = smb_xstrdup(parameter);
2098 } else if(strequal(request, "full-username")) {
2099 username = smb_xstrdup(parameter);
2100 } else if(strequal(request, "new-password")) {
2101 newpswd = smb_xstrdup(parameter);
2102 } else if (strequal(request, "old-password")) {
2103 oldpswd = smb_xstrdup(parameter);
2104 } else {
2105 printf("Error: Unknown request %s\n.\n", request);
2109 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2110 struct loadparm_context *lp_ctx,
2111 struct ntlm_auth_state *state,
2112 stdio_helper_function fn, void **private2)
2114 char *buf;
2115 char tmp[INITIAL_BUFFER_SIZE+1];
2116 int length, buf_size = 0;
2117 char *c;
2119 buf = talloc_strdup(state->mem_ctx, "");
2120 if (!buf) {
2121 DEBUG(0, ("Failed to allocate input buffer.\n"));
2122 fprintf(stderr, "ERR\n");
2123 exit(1);
2126 do {
2128 /* this is not a typo - x_fgets doesn't work too well under
2129 * squid */
2130 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2131 if (ferror(stdin)) {
2132 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2133 "(%s)\n", ferror(stdin),
2134 strerror(ferror(stdin))));
2136 exit(1);
2138 exit(0);
2141 buf = talloc_strdup_append_buffer(buf, tmp);
2142 buf_size += INITIAL_BUFFER_SIZE;
2144 if (buf_size > MAX_BUFFER_SIZE) {
2145 DEBUG(2, ("Oversized message\n"));
2146 fprintf(stderr, "ERR\n");
2147 talloc_free(buf);
2148 return;
2151 c = strchr(buf, '\n');
2152 } while (c == NULL);
2154 *c = '\0';
2155 length = c-buf;
2157 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2159 if (buf[0] == '\0') {
2160 DEBUG(2, ("Invalid Request\n"));
2161 fprintf(stderr, "ERR\n");
2162 talloc_free(buf);
2163 return;
2166 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2167 talloc_free(buf);
2171 static void squid_stream(enum stdio_helper_mode stdio_mode,
2172 struct loadparm_context *lp_ctx,
2173 stdio_helper_function fn) {
2174 TALLOC_CTX *mem_ctx;
2175 struct ntlm_auth_state *state;
2177 /* initialize FDescs */
2178 setbuf(stdout, NULL);
2179 setbuf(stderr, NULL);
2181 mem_ctx = talloc_init("ntlm_auth");
2182 if (!mem_ctx) {
2183 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2184 fprintf(stderr, "ERR\n");
2185 exit(1);
2188 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2189 if (!state) {
2190 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2191 fprintf(stderr, "ERR\n");
2192 exit(1);
2195 state->mem_ctx = mem_ctx;
2196 state->helper_mode = stdio_mode;
2198 while(1) {
2199 TALLOC_CTX *frame = talloc_stackframe();
2200 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2201 TALLOC_FREE(frame);
2206 /* Authenticate a user with a challenge/response */
2208 static bool check_auth_crap(void)
2210 NTSTATUS nt_status;
2211 uint32_t flags = 0;
2212 char lm_key[8];
2213 char user_session_key[16];
2214 char *hex_lm_key;
2215 char *hex_user_session_key;
2216 char *error_string;
2217 uint8_t authoritative = 0;
2219 setbuf(stdout, NULL);
2221 if (request_lm_key)
2222 flags |= WBFLAG_PAM_LMKEY;
2224 if (request_user_session_key)
2225 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2227 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2229 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2230 opt_workstation,
2231 &opt_challenge,
2232 &opt_lm_response,
2233 &opt_nt_response,
2234 flags, 0,
2235 (unsigned char *)lm_key,
2236 (unsigned char *)user_session_key,
2237 &authoritative,
2238 &error_string, NULL);
2240 if (!NT_STATUS_IS_OK(nt_status)) {
2241 printf("%s (0x%x)\n", error_string,
2242 NT_STATUS_V(nt_status));
2243 SAFE_FREE(error_string);
2244 return False;
2247 if (request_lm_key
2248 && (!all_zero((uint8_t *)lm_key, sizeof(lm_key)))) {
2249 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2250 sizeof(lm_key));
2251 printf("LM_KEY: %s\n", hex_lm_key);
2252 TALLOC_FREE(hex_lm_key);
2254 if (request_user_session_key
2255 && (!all_zero((uint8_t *)user_session_key,
2256 sizeof(user_session_key)))) {
2257 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2258 sizeof(user_session_key));
2259 printf("NT_KEY: %s\n", hex_user_session_key);
2260 TALLOC_FREE(hex_user_session_key);
2263 return True;
2266 /* Main program */
2268 enum {
2269 OPT_USERNAME = 1000,
2270 OPT_DOMAIN,
2271 OPT_WORKSTATION,
2272 OPT_CHALLENGE,
2273 OPT_RESPONSE,
2274 OPT_LM,
2275 OPT_NT,
2276 OPT_PASSWORD,
2277 OPT_LM_KEY,
2278 OPT_USER_SESSION_KEY,
2279 OPT_DIAGNOSTICS,
2280 OPT_REQUIRE_MEMBERSHIP,
2281 OPT_USE_CACHED_CREDS,
2282 OPT_ALLOW_MSCHAPV2,
2283 OPT_PAM_WINBIND_CONF,
2284 OPT_TARGET_SERVICE,
2285 OPT_TARGET_HOSTNAME,
2286 OPT_OFFLINE_LOGON
2289 int main(int argc, const char **argv)
2291 TALLOC_CTX *frame = talloc_stackframe();
2292 int opt;
2293 const char *helper_protocol = NULL;
2294 int diagnostics = 0;
2296 const char *hex_challenge = NULL;
2297 const char *hex_lm_response = NULL;
2298 const char *hex_nt_response = NULL;
2299 struct loadparm_context *lp_ctx;
2300 poptContext pc;
2302 /* NOTE: DO NOT change this interface without considering the implications!
2303 This is an external interface, which other programs will use to interact
2304 with this helper.
2307 /* We do not use single-letter command abbreviations, because they harm future
2308 interface stability. */
2310 struct poptOption long_options[] = {
2311 POPT_AUTOHELP
2312 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2313 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2314 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2315 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2316 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2317 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2318 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2319 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2320 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2321 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2322 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2323 { "allow-mschapv2", 0, POPT_ARG_NONE, &opt_allow_mschapv2, OPT_ALLOW_MSCHAPV2, "Explicitly allow MSCHAPv2" },
2324 { "offline-logon", 0, POPT_ARG_NONE, &offline_logon,
2325 OPT_OFFLINE_LOGON,
2326 "Use cached passwords when DC is offline"},
2327 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2328 OPT_DIAGNOSTICS,
2329 "Perform diagnostics on the authentication chain"},
2330 { "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" },
2331 { "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" },
2332 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2333 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2334 POPT_COMMON_CONFIGFILE
2335 POPT_COMMON_VERSION
2336 POPT_COMMON_OPTION
2337 POPT_TABLEEND
2340 /* Samba client initialisation */
2341 smb_init_locale();
2343 setup_logging("ntlm_auth", DEBUG_STDERR);
2344 fault_setup();
2346 /* Parse options */
2348 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2350 /* Parse command line options */
2352 if (argc == 1) {
2353 poptPrintHelp(pc, stderr, 0);
2354 return 1;
2357 while((opt = poptGetNextOpt(pc)) != -1) {
2358 /* Get generic config options like --configfile */
2361 poptFreeContext(pc);
2363 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2364 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2365 get_dyn_CONFIGFILE(), strerror(errno));
2366 exit(1);
2369 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2370 POPT_CONTEXT_KEEP_FIRST);
2372 while((opt = poptGetNextOpt(pc)) != -1) {
2373 switch (opt) {
2374 case OPT_CHALLENGE:
2375 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2376 if (opt_challenge.length != 8) {
2377 fprintf(stderr, "hex decode of %s failed! "
2378 "(only got %d bytes)\n",
2379 hex_challenge,
2380 (int)opt_challenge.length);
2381 exit(1);
2383 break;
2384 case OPT_LM:
2385 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2386 if (opt_lm_response.length != 24) {
2387 fprintf(stderr, "hex decode of %s failed! "
2388 "(only got %d bytes)\n",
2389 hex_lm_response,
2390 (int)opt_lm_response.length);
2391 exit(1);
2393 break;
2395 case OPT_NT:
2396 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2397 if (opt_nt_response.length < 24) {
2398 fprintf(stderr, "hex decode of %s failed! "
2399 "(only got %d bytes)\n",
2400 hex_nt_response,
2401 (int)opt_nt_response.length);
2402 exit(1);
2404 break;
2406 case OPT_REQUIRE_MEMBERSHIP:
2407 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2408 require_membership_of_sid = require_membership_of;
2410 break;
2414 if (opt_username) {
2415 char *domain = SMB_STRDUP(opt_username);
2416 char *p = strchr_m(domain, *lp_winbind_separator());
2417 if (p) {
2418 opt_username = p+1;
2419 *p = '\0';
2420 if (opt_domain && !strequal(opt_domain, domain)) {
2421 fprintf(stderr, "Domain specified in username (%s) "
2422 "doesn't match specified domain (%s)!\n\n",
2423 domain, opt_domain);
2424 poptPrintHelp(pc, stderr, 0);
2425 exit(1);
2427 opt_domain = domain;
2428 } else {
2429 SAFE_FREE(domain);
2433 /* Note: if opt_domain is "" then send no domain */
2434 if (opt_domain == NULL) {
2435 opt_domain = get_winbind_domain();
2438 if (opt_workstation == NULL) {
2439 opt_workstation = "";
2442 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2443 if (lp_ctx == NULL) {
2444 fprintf(stderr, "loadparm_init_s3() failed!\n");
2445 exit(1);
2448 if (helper_protocol) {
2449 int i;
2450 for (i=0; i<NUM_HELPER_MODES; i++) {
2451 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2452 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2453 exit(0);
2456 fprintf(stderr, "unknown helper protocol [%s]\n\n"
2457 "Valid helper protools:\n\n", helper_protocol);
2459 for (i=0; i<NUM_HELPER_MODES; i++) {
2460 fprintf(stderr, "%s\n",
2461 stdio_helper_protocols[i].name);
2464 exit(1);
2467 if (!opt_username || !*opt_username) {
2468 fprintf(stderr, "username must be specified!\n\n");
2469 poptPrintHelp(pc, stderr, 0);
2470 exit(1);
2473 if (opt_challenge.length) {
2474 if (!check_auth_crap()) {
2475 exit(1);
2477 exit(0);
2480 if (!opt_password) {
2481 char pwd[256] = {0};
2482 int rc;
2484 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2485 if (rc == 0) {
2486 opt_password = SMB_STRDUP(pwd);
2490 if (diagnostics) {
2491 if (!diagnose_ntlm_auth()) {
2492 return 1;
2494 } else {
2495 fstring user;
2497 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2498 if (!check_plaintext_auth(user, opt_password, True)) {
2499 return 1;
2503 /* Exit code */
2505 poptFreeContext(pc);
2506 TALLOC_FREE(frame);
2507 return 0;