vfs_aio_fork: Use a shorter random delay
[Samba.git] / source3 / utils / ntlm_auth.c
blob3f544902a24a01903ec7f596f593664388a357d6
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 wbcInterfaceDetails *details;
284 wbcErr ret;
285 static bool got_sep;
286 static char sep;
288 if (got_sep)
289 return sep;
291 ret = wbcInterfaceDetails(&details);
292 if (!WBC_ERROR_IS_OK(ret)) {
293 d_fprintf(stderr, "could not obtain winbind separator!\n");
294 return *lp_winbind_separator();
297 sep = details->winbind_separator;
299 wbcFreeMemory(details);
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 wbcInterfaceDetails *details;
314 wbcErr ret;
316 static fstring winbind_domain;
317 if (*winbind_domain) {
318 return winbind_domain;
321 /* Send off request */
323 ret = wbcInterfaceDetails(&details);
324 if (!WBC_ERROR_IS_OK(ret)) {
325 DEBUG(1, ("could not obtain winbind domain name!\n"));
326 return lp_workgroup();
329 fstrcpy(winbind_domain, details->netbios_domain);
331 wbcFreeMemory(details);
333 return winbind_domain;
337 const char *get_winbind_netbios_name(void)
339 struct wbcInterfaceDetails *details;
340 wbcErr ret;
342 static fstring winbind_netbios_name;
344 if (*winbind_netbios_name) {
345 return winbind_netbios_name;
348 /* Send off request */
350 ret = wbcInterfaceDetails(&details);
351 if (!WBC_ERROR_IS_OK(ret)) {
352 DEBUG(1, ("could not obtain winbind netbios name!\n"));
353 return lp_netbios_name();
356 fstrcpy(winbind_netbios_name, details->netbios_name);
358 wbcFreeMemory(details);
360 return winbind_netbios_name;
364 DATA_BLOB get_challenge(void)
366 static DATA_BLOB chal;
367 if (opt_challenge.length)
368 return opt_challenge;
370 chal = data_blob(NULL, 8);
372 generate_random_buffer(chal.data, chal.length);
373 return chal;
376 /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
377 form DOMAIN/user into a domain and a user */
379 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
380 fstring user)
383 char *p = strchr(domuser,winbind_separator());
385 if (!p) {
386 return False;
389 fstrcpy(user, p+1);
390 fstrcpy(domain, domuser);
391 domain[PTR_DIFF(p, domuser)] = 0;
392 return strupper_m(domain);
395 static bool get_require_membership_sid(void) {
396 fstring domain, name, sidbuf;
397 struct wbcDomainSid sid;
398 enum wbcSidType type;
399 wbcErr ret;
401 if (!require_membership_of) {
402 return True;
405 if (require_membership_of_sid) {
406 return True;
409 /* Otherwise, ask winbindd for the name->sid request */
411 if (!parse_ntlm_auth_domain_user(require_membership_of,
412 domain, name)) {
413 DEBUG(0, ("Could not parse %s into separate domain/name parts!\n",
414 require_membership_of));
415 return False;
418 ret = wbcLookupName(domain, name, &sid, &type);
419 if (!WBC_ERROR_IS_OK(ret)) {
420 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
421 require_membership_of));
422 return False;
425 wbcSidToStringBuf(&sid, sidbuf, sizeof(sidbuf));
427 require_membership_of_sid = SMB_STRDUP(sidbuf);
429 if (require_membership_of_sid)
430 return True;
432 return False;
436 * Get some configuration from pam_winbind.conf to see if we
437 * need to contact trusted domain
440 int get_pam_winbind_config()
442 int ctrl = 0;
443 struct tiniparser_dictionary *d = NULL;
445 if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
446 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
449 d = tiniparser_load(opt_pam_winbind_conf);
451 if (!d) {
452 return 0;
455 if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
456 ctrl |= WINBIND_KRB5_AUTH;
459 tiniparser_freedict(d);
461 return ctrl;
464 /* Authenticate a user with a plaintext password */
466 static bool check_plaintext_auth(const char *user, const char *pass,
467 bool stdout_diagnostics)
469 struct winbindd_request request;
470 struct winbindd_response response;
471 NSS_STATUS result;
473 if (!get_require_membership_sid()) {
474 return False;
477 /* Send off request */
479 ZERO_STRUCT(request);
480 ZERO_STRUCT(response);
482 fstrcpy(request.data.auth.user, user);
483 fstrcpy(request.data.auth.pass, pass);
484 if (require_membership_of_sid) {
485 strlcpy(request.data.auth.require_membership_of_sid,
486 require_membership_of_sid,
487 sizeof(request.data.auth.require_membership_of_sid));
490 if (offline_logon) {
491 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
494 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH, &request, &response);
496 /* Display response */
498 if (stdout_diagnostics) {
499 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
500 d_fprintf(stderr, "Reading winbind reply failed! (0x01)\n");
503 d_printf("%s: %s (0x%x)\n",
504 response.data.auth.nt_status_string,
505 response.data.auth.error_string,
506 response.data.auth.nt_status);
507 } else {
508 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
509 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
512 DEBUG(3, ("%s: %s (0x%x)\n",
513 response.data.auth.nt_status_string,
514 response.data.auth.error_string,
515 response.data.auth.nt_status));
518 return (result == NSS_STATUS_SUCCESS);
521 /* authenticate a user with an encrypted username/password */
523 NTSTATUS contact_winbind_auth_crap(const char *username,
524 const char *domain,
525 const char *workstation,
526 const DATA_BLOB *challenge,
527 const DATA_BLOB *lm_response,
528 const DATA_BLOB *nt_response,
529 uint32_t flags,
530 uint32_t extra_logon_parameters,
531 uint8_t lm_key[8],
532 uint8_t user_session_key[16],
533 uint8_t *pauthoritative,
534 char **error_string,
535 char **unix_name)
537 NTSTATUS nt_status;
538 NSS_STATUS result;
539 struct winbindd_request request;
540 struct winbindd_response response;
542 *pauthoritative = 1;
544 if (!get_require_membership_sid()) {
545 return NT_STATUS_INVALID_PARAMETER;
548 ZERO_STRUCT(request);
549 ZERO_STRUCT(response);
551 request.flags = flags;
553 request.data.auth_crap.logon_parameters = extra_logon_parameters
554 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
556 if (opt_allow_mschapv2) {
557 request.data.auth_crap.logon_parameters |= MSV1_0_ALLOW_MSVCHAPV2;
560 if (require_membership_of_sid)
561 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
563 fstrcpy(request.data.auth_crap.user, username);
564 fstrcpy(request.data.auth_crap.domain, domain);
566 fstrcpy(request.data.auth_crap.workstation,
567 workstation);
569 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
571 if (lm_response && lm_response->length) {
572 memcpy(request.data.auth_crap.lm_resp,
573 lm_response->data,
574 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
575 request.data.auth_crap.lm_resp_len = lm_response->length;
578 if (nt_response && nt_response->length) {
579 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
580 request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
581 request.extra_len = nt_response->length;
582 request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
583 if (request.extra_data.data == NULL) {
584 return NT_STATUS_NO_MEMORY;
586 memcpy(request.extra_data.data, nt_response->data,
587 nt_response->length);
589 } else {
590 memcpy(request.data.auth_crap.nt_resp,
591 nt_response->data, nt_response->length);
593 request.data.auth_crap.nt_resp_len = nt_response->length;
596 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH_CRAP, &request, &response);
597 SAFE_FREE(request.extra_data.data);
599 /* Display response */
601 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
602 nt_status = NT_STATUS_UNSUCCESSFUL;
603 if (error_string)
604 *error_string = smb_xstrdup("Reading winbind reply failed!");
605 winbindd_free_response(&response);
606 return nt_status;
609 nt_status = (NT_STATUS(response.data.auth.nt_status));
610 if (!NT_STATUS_IS_OK(nt_status)) {
611 if (error_string)
612 *error_string = smb_xstrdup(response.data.auth.error_string);
613 *pauthoritative = response.data.auth.authoritative;
614 winbindd_free_response(&response);
615 return nt_status;
618 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
619 memcpy(lm_key, response.data.auth.first_8_lm_hash,
620 sizeof(response.data.auth.first_8_lm_hash));
622 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
623 memcpy(user_session_key, response.data.auth.user_session_key,
624 sizeof(response.data.auth.user_session_key));
627 if (flags & WBFLAG_PAM_UNIX_NAME) {
628 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
629 if (!*unix_name) {
630 winbindd_free_response(&response);
631 return NT_STATUS_NO_MEMORY;
635 winbindd_free_response(&response);
636 return nt_status;
639 /* contact server to change user password using auth crap */
640 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
641 const char *domain,
642 const DATA_BLOB new_nt_pswd,
643 const DATA_BLOB old_nt_hash_enc,
644 const DATA_BLOB new_lm_pswd,
645 const DATA_BLOB old_lm_hash_enc,
646 char **error_string)
648 NTSTATUS nt_status;
649 NSS_STATUS result;
650 struct winbindd_request request;
651 struct winbindd_response response;
653 if (!get_require_membership_sid())
655 if(error_string)
656 *error_string = smb_xstrdup("Can't get membership sid.");
657 return NT_STATUS_INVALID_PARAMETER;
660 ZERO_STRUCT(request);
661 ZERO_STRUCT(response);
663 if(username != NULL)
664 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
665 if(domain != NULL)
666 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
668 if(new_nt_pswd.length)
670 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
671 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
674 if(old_nt_hash_enc.length)
676 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));
677 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
680 if(new_lm_pswd.length)
682 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
683 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
686 if(old_lm_hash_enc.length)
688 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));
689 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
692 result = winbindd_request_response(NULL, WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
694 /* Display response */
696 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
698 nt_status = NT_STATUS_UNSUCCESSFUL;
699 if (error_string)
700 *error_string = smb_xstrdup("Reading winbind reply failed!");
701 winbindd_free_response(&response);
702 return nt_status;
705 nt_status = (NT_STATUS(response.data.auth.nt_status));
706 if (!NT_STATUS_IS_OK(nt_status))
708 if (error_string)
709 *error_string = smb_xstrdup(response.data.auth.error_string);
710 winbindd_free_response(&response);
711 return nt_status;
714 winbindd_free_response(&response);
716 return nt_status;
719 static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
720 TALLOC_CTX *mem_ctx,
721 void *server_returned_info,
722 const char *original_user_name,
723 uint32_t session_info_flags,
724 struct auth_session_info **session_info_out)
726 const char *unix_username = (const char *)server_returned_info;
727 bool ok;
728 struct dom_sid *sids = NULL;
729 struct auth_session_info *session_info = NULL;
731 session_info = talloc_zero(mem_ctx, struct auth_session_info);
732 if (session_info == NULL) {
733 return NT_STATUS_NO_MEMORY;
736 session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
737 if (session_info->unix_info == NULL) {
738 TALLOC_FREE(session_info);
739 return NT_STATUS_NO_MEMORY;
741 session_info->unix_info->unix_name = talloc_strdup(session_info->unix_info,
742 unix_username);
743 if (session_info->unix_info->unix_name == NULL) {
744 TALLOC_FREE(session_info);
745 return NT_STATUS_NO_MEMORY;
748 session_info->security_token = talloc_zero(session_info, struct security_token);
749 if (session_info->security_token == NULL) {
750 TALLOC_FREE(session_info);
751 return NT_STATUS_NO_MEMORY;
754 sids = talloc_zero_array(session_info->security_token,
755 struct dom_sid, 3);
756 if (sids == NULL) {
757 TALLOC_FREE(session_info);
758 return NT_STATUS_NO_MEMORY;
760 ok = dom_sid_parse(SID_WORLD, &sids[0]);
761 if (!ok) {
762 TALLOC_FREE(session_info);
763 return NT_STATUS_INTERNAL_ERROR;
765 ok = dom_sid_parse(SID_NT_NETWORK, &sids[1]);
766 if (!ok) {
767 TALLOC_FREE(session_info);
768 return NT_STATUS_INTERNAL_ERROR;
770 ok = dom_sid_parse(SID_NT_AUTHENTICATED_USERS, &sids[2]);
771 if (!ok) {
772 TALLOC_FREE(session_info);
773 return NT_STATUS_INTERNAL_ERROR;
776 session_info->security_token->num_sids = talloc_array_length(sids);
777 session_info->security_token->sids = sids;
779 *session_info_out = session_info;
781 return NT_STATUS_OK;
784 static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
785 TALLOC_CTX *mem_ctx,
786 struct smb_krb5_context *smb_krb5_context,
787 DATA_BLOB *pac_blob,
788 const char *princ_name,
789 const struct tsocket_address *remote_address,
790 uint32_t session_info_flags,
791 struct auth_session_info **session_info)
793 TALLOC_CTX *tmp_ctx;
794 struct PAC_LOGON_INFO *logon_info = NULL;
795 char *unixuser;
796 NTSTATUS status;
797 char *domain = NULL;
798 char *realm = NULL;
799 char *user = NULL;
800 char *p;
802 tmp_ctx = talloc_new(mem_ctx);
803 if (!tmp_ctx) {
804 return NT_STATUS_NO_MEMORY;
807 if (pac_blob) {
808 #ifdef HAVE_KRB5
809 status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
810 NULL, NULL, 0, &logon_info);
811 #else
812 status = NT_STATUS_ACCESS_DENIED;
813 #endif
814 if (!NT_STATUS_IS_OK(status)) {
815 goto done;
819 DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
821 p = strchr_m(princ_name, '@');
822 if (!p) {
823 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
824 princ_name));
825 return NT_STATUS_LOGON_FAILURE;
828 user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
829 if (!user) {
830 return NT_STATUS_NO_MEMORY;
833 realm = talloc_strdup(talloc_tos(), p + 1);
834 if (!realm) {
835 return NT_STATUS_NO_MEMORY;
838 if (!strequal(realm, lp_realm())) {
839 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
840 if (!lp_allow_trusted_domains()) {
841 return NT_STATUS_LOGON_FAILURE;
845 if (logon_info && logon_info->info3.base.logon_domain.string) {
846 domain = talloc_strdup(mem_ctx,
847 logon_info->info3.base.logon_domain.string);
848 if (!domain) {
849 return NT_STATUS_NO_MEMORY;
851 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
852 } else {
854 /* If we have winbind running, we can (and must) shorten the
855 username by using the short netbios name. Otherwise we will
856 have inconsistent user names. With Kerberos, we get the
857 fully qualified realm, with ntlmssp we get the short
858 name. And even w2k3 does use ntlmssp if you for example
859 connect to an ip address. */
861 wbcErr wbc_status;
862 struct wbcDomainInfo *info = NULL;
864 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
865 realm));
867 wbc_status = wbcDomainInfo(realm, &info);
869 if (WBC_ERROR_IS_OK(wbc_status)) {
870 domain = talloc_strdup(mem_ctx,
871 info->short_name);
872 wbcFreeMemory(info);
873 } else {
874 DEBUG(3, ("Could not find short name: %s\n",
875 wbcErrorString(wbc_status)));
876 domain = talloc_strdup(mem_ctx, realm);
878 if (!domain) {
879 return NT_STATUS_NO_MEMORY;
881 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
884 unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
885 if (!unixuser) {
886 status = NT_STATUS_NO_MEMORY;
887 goto done;
890 status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
892 done:
893 TALLOC_FREE(tmp_ctx);
894 return status;
900 * Return the challenge as determined by the authentication subsystem
901 * @return an 8 byte random challenge
904 static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
905 uint8_t chal[8])
907 if (auth_ctx->challenge.data.length == 8) {
908 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
909 auth_ctx->challenge.set_by));
910 memcpy(chal, auth_ctx->challenge.data.data, 8);
911 return NT_STATUS_OK;
914 if (!auth_ctx->challenge.set_by) {
915 generate_random_buffer(chal, 8);
917 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
918 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
919 auth_ctx->challenge.set_by = "random";
922 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
923 auth_ctx->challenge.set_by));
925 return NT_STATUS_OK;
929 * NTLM2 authentication modifies the effective challenge,
930 * @param challenge The new challenge value
932 static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
934 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
935 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
937 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
938 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
940 return NT_STATUS_OK;
944 * Check the password on an NTLMSSP login.
946 * Return the session keys used on the connection.
949 static NTSTATUS winbind_pw_check(struct auth4_context *auth4_context,
950 TALLOC_CTX *mem_ctx,
951 const struct auth_usersupplied_info *user_info,
952 uint8_t *pauthoritative,
953 void **server_returned_info,
954 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
956 NTSTATUS nt_status;
957 char *error_string = NULL;
958 uint8_t lm_key[8];
959 uint8_t user_sess_key[16];
960 char *unix_name = NULL;
962 nt_status = contact_winbind_auth_crap(user_info->client.account_name, user_info->client.domain_name,
963 user_info->workstation_name,
964 &auth4_context->challenge.data,
965 &user_info->password.response.lanman,
966 &user_info->password.response.nt,
967 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
969 lm_key, user_sess_key,
970 pauthoritative,
971 &error_string, &unix_name);
973 if (NT_STATUS_IS_OK(nt_status)) {
974 if (!all_zero(lm_key, 8)) {
975 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
976 memcpy(lm_session_key->data, lm_key, 8);
977 memset(lm_session_key->data+8, '\0', 8);
980 if (!all_zero(user_sess_key, 16)) {
981 *session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
983 *server_returned_info = talloc_strdup(mem_ctx,
984 unix_name);
985 } else {
986 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
987 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
988 user_info->client.domain_name, user_info->client.account_name,
989 user_info->workstation_name,
990 error_string ? error_string : "unknown error (NULL)"));
993 SAFE_FREE(error_string);
994 SAFE_FREE(unix_name);
995 return nt_status;
998 static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
999 TALLOC_CTX *mem_ctx,
1000 const struct auth_usersupplied_info *user_info,
1001 uint8_t *pauthoritative,
1002 void **server_returned_info,
1003 DATA_BLOB *session_key, DATA_BLOB *lm_session_key)
1005 NTSTATUS nt_status;
1006 struct samr_Password lm_pw, nt_pw;
1008 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1010 *pauthoritative = 1;
1012 nt_status = ntlm_password_check(mem_ctx,
1013 true, true, 0,
1014 &auth4_context->challenge.data,
1015 &user_info->password.response.lanman,
1016 &user_info->password.response.nt,
1017 user_info->client.account_name,
1018 user_info->client.account_name,
1019 user_info->client.domain_name,
1020 &lm_pw, &nt_pw, session_key, lm_session_key);
1022 if (NT_STATUS_IS_OK(nt_status)) {
1023 *server_returned_info = talloc_asprintf(mem_ctx,
1024 "%s%c%s", user_info->client.domain_name,
1025 *lp_winbind_separator(),
1026 user_info->client.account_name);
1027 } else {
1028 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
1029 user_info->client.domain_name, user_info->client.account_name,
1030 user_info->workstation_name,
1031 nt_errstr(nt_status)));
1033 return nt_status;
1036 static NTSTATUS ntlm_auth_prepare_gensec_client(TALLOC_CTX *mem_ctx,
1037 struct loadparm_context *lp_ctx,
1038 struct gensec_security **gensec_security_out)
1040 struct gensec_security *gensec_security = NULL;
1041 NTSTATUS nt_status;
1042 TALLOC_CTX *tmp_ctx;
1043 const struct gensec_security_ops **backends = NULL;
1044 struct gensec_settings *gensec_settings = NULL;
1045 size_t idx = 0;
1047 tmp_ctx = talloc_new(mem_ctx);
1048 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1050 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1051 if (gensec_settings == NULL) {
1052 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1053 TALLOC_FREE(tmp_ctx);
1054 return NT_STATUS_NO_MEMORY;
1057 backends = talloc_zero_array(gensec_settings,
1058 const struct gensec_security_ops *, 4);
1059 if (backends == NULL) {
1060 TALLOC_FREE(tmp_ctx);
1061 return NT_STATUS_NO_MEMORY;
1063 gensec_settings->backends = backends;
1065 gensec_init();
1067 /* These need to be in priority order, krb5 before NTLMSSP */
1068 #if defined(HAVE_KRB5)
1069 backends[idx++] = &gensec_gse_krb5_security_ops;
1070 #endif
1072 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1074 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1076 nt_status = gensec_client_start(NULL, &gensec_security,
1077 gensec_settings);
1078 if (!NT_STATUS_IS_OK(nt_status)) {
1079 TALLOC_FREE(tmp_ctx);
1080 return nt_status;
1083 talloc_unlink(tmp_ctx, gensec_settings);
1085 if (opt_target_service != NULL) {
1086 nt_status = gensec_set_target_service(gensec_security,
1087 opt_target_service);
1088 if (!NT_STATUS_IS_OK(nt_status)) {
1089 TALLOC_FREE(tmp_ctx);
1090 return nt_status;
1094 if (opt_target_hostname != NULL) {
1095 nt_status = gensec_set_target_hostname(gensec_security,
1096 opt_target_hostname);
1097 if (!NT_STATUS_IS_OK(nt_status)) {
1098 TALLOC_FREE(tmp_ctx);
1099 return nt_status;
1103 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1104 TALLOC_FREE(tmp_ctx);
1105 return NT_STATUS_OK;
1108 static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
1110 struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
1111 if (auth4_context == NULL) {
1112 DEBUG(10, ("failed to allocate auth4_context failed\n"));
1113 return NULL;
1115 auth4_context->generate_session_info = ntlm_auth_generate_session_info;
1116 auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
1117 auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
1118 auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
1119 if (local_pw) {
1120 auth4_context->check_ntlm_password = local_pw_check;
1121 } else {
1122 auth4_context->check_ntlm_password = winbind_pw_check;
1124 auth4_context->private_data = NULL;
1125 return auth4_context;
1128 static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx,
1129 struct loadparm_context *lp_ctx,
1130 struct gensec_security **gensec_security_out)
1132 struct gensec_security *gensec_security;
1133 NTSTATUS nt_status;
1135 TALLOC_CTX *tmp_ctx;
1136 const struct gensec_security_ops **backends;
1137 struct gensec_settings *gensec_settings;
1138 size_t idx = 0;
1139 struct cli_credentials *server_credentials;
1141 struct auth4_context *auth4_context;
1143 tmp_ctx = talloc_new(mem_ctx);
1144 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1146 auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
1147 if (auth4_context == NULL) {
1148 TALLOC_FREE(tmp_ctx);
1149 return NT_STATUS_NO_MEMORY;
1152 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
1153 if (lp_ctx == NULL) {
1154 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
1155 TALLOC_FREE(tmp_ctx);
1156 return NT_STATUS_NO_MEMORY;
1160 * This should be a 'netbios domain -> DNS domain'
1161 * mapping, and can currently validly return NULL on
1162 * poorly configured systems.
1164 * This is used for the NTLMSSP server
1167 if (opt_password) {
1168 gensec_settings->server_netbios_name = lp_netbios_name();
1169 gensec_settings->server_netbios_domain = lp_workgroup();
1170 } else {
1171 gensec_settings->server_netbios_name = get_winbind_netbios_name();
1172 gensec_settings->server_netbios_domain = get_winbind_domain();
1175 gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
1176 get_mydnsdomname(talloc_tos()));
1177 gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
1178 get_mydnsfullname());
1180 backends = talloc_zero_array(gensec_settings,
1181 const struct gensec_security_ops *, 4);
1183 if (backends == NULL) {
1184 TALLOC_FREE(tmp_ctx);
1185 return NT_STATUS_NO_MEMORY;
1187 gensec_settings->backends = backends;
1189 gensec_init();
1191 /* These need to be in priority order, krb5 before NTLMSSP */
1192 #if defined(HAVE_KRB5)
1193 backends[idx++] = &gensec_gse_krb5_security_ops;
1194 #endif
1196 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
1198 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1201 * This is anonymous for now, because we just use it
1202 * to set the kerberos state at the moment
1204 server_credentials = cli_credentials_init_anon(tmp_ctx);
1205 if (!server_credentials) {
1206 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
1207 return NT_STATUS_NO_MEMORY;
1210 cli_credentials_set_conf(server_credentials, lp_ctx);
1212 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
1213 cli_credentials_set_kerberos_state(server_credentials, CRED_AUTO_USE_KERBEROS);
1214 } else {
1215 cli_credentials_set_kerberos_state(server_credentials, CRED_DONT_USE_KERBEROS);
1218 nt_status = gensec_server_start(tmp_ctx, gensec_settings,
1219 auth4_context, &gensec_security);
1221 if (!NT_STATUS_IS_OK(nt_status)) {
1222 TALLOC_FREE(tmp_ctx);
1223 return nt_status;
1226 gensec_set_credentials(gensec_security, server_credentials);
1229 * TODO: Allow the caller to pass their own description here
1230 * via a command-line option
1232 nt_status = gensec_set_target_service_description(gensec_security,
1233 "ntlm_auth");
1234 if (!NT_STATUS_IS_OK(nt_status)) {
1235 TALLOC_FREE(tmp_ctx);
1236 return nt_status;
1239 talloc_unlink(tmp_ctx, lp_ctx);
1240 talloc_unlink(tmp_ctx, server_credentials);
1241 talloc_unlink(tmp_ctx, gensec_settings);
1242 talloc_unlink(tmp_ctx, auth4_context);
1244 *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
1245 TALLOC_FREE(tmp_ctx);
1246 return NT_STATUS_OK;
1249 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1250 struct loadparm_context *lp_ctx,
1251 struct ntlm_auth_state *state,
1252 char *buf, int length, void **private2)
1254 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1255 return;
1258 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
1259 struct loadparm_context *lp_ctx,
1260 struct ntlm_auth_state *state,
1261 char *buf, int length, void **private2)
1263 char *user, *pass;
1264 user=buf;
1266 pass=(char *)memchr(buf,' ',length);
1267 if (!pass) {
1268 DEBUG(2, ("Password not found. Denying access\n"));
1269 printf("ERR\n");
1270 return;
1272 *pass='\0';
1273 pass++;
1275 if (state->helper_mode == SQUID_2_5_BASIC) {
1276 rfc1738_unescape(user);
1277 rfc1738_unescape(pass);
1280 if (check_plaintext_auth(user, pass, False)) {
1281 printf("OK\n");
1282 } else {
1283 printf("ERR\n");
1287 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1288 struct loadparm_context *lp_ctx,
1289 char *buf, int length, void **private1)
1291 DATA_BLOB in;
1292 DATA_BLOB out = data_blob(NULL, 0);
1293 char *out_base64 = NULL;
1294 const char *reply_arg = NULL;
1295 struct gensec_ntlm_state {
1296 struct gensec_security *gensec_state;
1297 const char *set_password;
1299 struct gensec_ntlm_state *state;
1301 NTSTATUS nt_status;
1302 bool first = false;
1303 const char *reply_code;
1304 struct cli_credentials *creds;
1306 static char *want_feature_list = NULL;
1307 static DATA_BLOB session_key;
1309 TALLOC_CTX *mem_ctx;
1311 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1313 if (*private1) {
1314 state = (struct gensec_ntlm_state *)*private1;
1315 } else {
1316 state = talloc_zero(NULL, struct gensec_ntlm_state);
1317 if (!state) {
1318 printf("BH No Memory\n");
1319 exit(1);
1321 *private1 = state;
1322 if (opt_password) {
1323 state->set_password = opt_password;
1327 if (strlen(buf) < 2) {
1328 DEBUG(1, ("query [%s] invalid", buf));
1329 printf("BH Query invalid\n");
1330 talloc_free(mem_ctx);
1331 return;
1334 if (strlen(buf) > 3) {
1335 if(strncmp(buf, "SF ", 3) == 0) {
1336 DEBUG(10, ("Setting flags to negotiate\n"));
1337 talloc_free(want_feature_list);
1338 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1339 printf("OK\n");
1340 talloc_free(mem_ctx);
1341 return;
1343 in = base64_decode_data_blob_talloc(mem_ctx, buf + 3);
1344 } else {
1345 in = data_blob(NULL, 0);
1348 if (strncmp(buf, "YR", 2) == 0) {
1349 if (state->gensec_state) {
1350 talloc_free(state->gensec_state);
1351 state->gensec_state = NULL;
1353 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1354 /* Just return BH, like ntlm_auth from Samba 3 does. */
1355 printf("BH Command expected\n");
1356 talloc_free(mem_ctx);
1357 return;
1358 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1359 (strncmp(buf, "KK ", 3) != 0) &&
1360 (strncmp(buf, "AF ", 3) != 0) &&
1361 (strncmp(buf, "NA ", 3) != 0) &&
1362 (strncmp(buf, "UG", 2) != 0) &&
1363 (strncmp(buf, "PW ", 3) != 0) &&
1364 (strncmp(buf, "GK", 2) != 0) &&
1365 (strncmp(buf, "GF", 2) != 0)) {
1366 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1367 printf("BH SPNEGO request invalid prefix\n");
1368 talloc_free(mem_ctx);
1369 return;
1372 /* setup gensec */
1373 if (!(state->gensec_state)) {
1374 switch (stdio_helper_mode) {
1375 case GSS_SPNEGO_CLIENT:
1377 * cached credentials are only supported by
1378 * NTLMSSP_CLIENT_1 for now.
1380 use_cached_creds = false;
1381 /* fall through */
1382 case NTLMSSP_CLIENT_1:
1383 /* setup the client side */
1385 if (state->set_password != NULL) {
1386 use_cached_creds = false;
1389 if (use_cached_creds) {
1390 struct wbcCredentialCacheParams params;
1391 struct wbcCredentialCacheInfo *info = NULL;
1392 struct wbcAuthErrorInfo *error = NULL;
1393 wbcErr wbc_status;
1395 params.account_name = opt_username;
1396 params.domain_name = opt_domain;
1397 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1398 params.num_blobs = 0;
1399 params.blobs = NULL;
1401 wbc_status = wbcCredentialCache(&params, &info,
1402 &error);
1403 wbcFreeMemory(error);
1404 if (!WBC_ERROR_IS_OK(wbc_status)) {
1405 use_cached_creds = false;
1407 wbcFreeMemory(info);
1410 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1411 &state->gensec_state);
1412 if (!NT_STATUS_IS_OK(nt_status)) {
1413 printf("BH GENSEC mech failed to start: %s\n",
1414 nt_errstr(nt_status));
1415 talloc_free(mem_ctx);
1416 return;
1419 creds = cli_credentials_init(state->gensec_state);
1420 cli_credentials_set_conf(creds, lp_ctx);
1421 if (opt_username) {
1422 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1424 if (opt_domain) {
1425 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1427 if (use_cached_creds) {
1428 gensec_want_feature(state->gensec_state,
1429 GENSEC_FEATURE_NTLM_CCACHE);
1430 } else if (state->set_password) {
1431 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1432 } else {
1433 cli_credentials_set_password_callback(creds, get_password);
1435 if (opt_workstation) {
1436 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1439 gensec_set_credentials(state->gensec_state, creds);
1441 break;
1442 case GSS_SPNEGO_SERVER:
1443 case SQUID_2_5_NTLMSSP:
1445 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1446 &state->gensec_state);
1447 if (!NT_STATUS_IS_OK(nt_status)) {
1448 printf("BH GENSEC mech failed to start: %s\n",
1449 nt_errstr(nt_status));
1450 talloc_free(mem_ctx);
1451 return;
1453 break;
1455 default:
1456 talloc_free(mem_ctx);
1457 abort();
1460 gensec_want_feature_list(state->gensec_state, want_feature_list);
1462 /* Session info is not complete, do not pass to auth log */
1463 gensec_want_feature(state->gensec_state, GENSEC_FEATURE_NO_AUTHZ_LOG);
1465 switch (stdio_helper_mode) {
1466 case GSS_SPNEGO_CLIENT:
1467 case GSS_SPNEGO_SERVER:
1468 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1469 if (!in.length) {
1470 first = true;
1472 break;
1473 case NTLMSSP_CLIENT_1:
1474 if (!in.length) {
1475 first = true;
1477 /* fall through */
1478 case SQUID_2_5_NTLMSSP:
1479 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1480 break;
1481 default:
1482 talloc_free(mem_ctx);
1483 abort();
1486 if (!NT_STATUS_IS_OK(nt_status)) {
1487 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1488 printf("BH GENSEC mech failed to start\n");
1489 talloc_free(mem_ctx);
1490 return;
1495 /* update */
1497 if (strncmp(buf, "PW ", 3) == 0) {
1498 state->set_password = talloc_strndup(state,
1499 (const char *)in.data,
1500 in.length);
1502 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1503 state->set_password,
1504 CRED_SPECIFIED);
1505 printf("OK\n");
1506 talloc_free(mem_ctx);
1507 return;
1510 if (strncmp(buf, "GK", 2) == 0) {
1511 char *base64_key;
1512 DEBUG(10, ("Requested session key\n"));
1513 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1514 if(!NT_STATUS_IS_OK(nt_status)) {
1515 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1516 printf("BH No session key\n");
1517 talloc_free(mem_ctx);
1518 return;
1519 } else {
1520 base64_key = base64_encode_data_blob(state, session_key);
1521 SMB_ASSERT(base64_key != NULL);
1522 printf("GK %s\n", base64_key);
1523 talloc_free(base64_key);
1525 talloc_free(mem_ctx);
1526 return;
1529 if (strncmp(buf, "GF", 2) == 0) {
1530 uint32_t neg_flags;
1532 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1534 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1535 if (neg_flags == 0) {
1536 printf("BH\n");
1537 talloc_free(mem_ctx);
1538 return;
1541 printf("GF 0x%08x\n", neg_flags);
1542 talloc_free(mem_ctx);
1543 return;
1546 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1548 /* don't leak 'bad password'/'no such user' info to the network client */
1549 nt_status = nt_status_squash(nt_status);
1551 if (out.length) {
1552 out_base64 = base64_encode_data_blob(mem_ctx, out);
1553 SMB_ASSERT(out_base64 != NULL);
1554 } else {
1555 out_base64 = NULL;
1558 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1559 reply_arg = "*";
1560 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1561 reply_code = "YR";
1562 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1563 reply_code = "KK";
1564 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1565 reply_code = "TT";
1566 } else {
1567 abort();
1571 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1572 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1573 reply_arg = nt_errstr(nt_status);
1574 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1575 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1576 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1577 reply_arg = nt_errstr(nt_status);
1578 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1579 } else if (!NT_STATUS_IS_OK(nt_status)) {
1580 reply_code = "NA";
1581 reply_arg = nt_errstr(nt_status);
1582 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1583 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1584 struct auth_session_info *session_info;
1586 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1587 if (!NT_STATUS_IS_OK(nt_status)) {
1588 reply_code = "BH Failed to retrive session info";
1589 reply_arg = nt_errstr(nt_status);
1590 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1591 } else {
1593 reply_code = "AF";
1594 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1595 if (reply_arg == NULL) {
1596 reply_code = "BH out of memory";
1597 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1599 talloc_free(session_info);
1601 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1602 reply_code = "AF";
1603 reply_arg = out_base64;
1604 } else {
1605 abort();
1608 switch (stdio_helper_mode) {
1609 case GSS_SPNEGO_SERVER:
1610 printf("%s %s %s\n", reply_code,
1611 out_base64 ? out_base64 : "*",
1612 reply_arg ? reply_arg : "*");
1613 break;
1614 default:
1615 if (out_base64) {
1616 printf("%s %s\n", reply_code, out_base64);
1617 } else if (reply_arg) {
1618 printf("%s %s\n", reply_code, reply_arg);
1619 } else {
1620 printf("%s\n", reply_code);
1624 talloc_free(mem_ctx);
1625 return;
1628 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1629 struct loadparm_context *lp_ctx,
1630 struct ntlm_auth_state *state,
1631 char *buf, int length, void **private2)
1633 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1634 return;
1637 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1638 struct loadparm_context *lp_ctx,
1639 struct ntlm_auth_state *state,
1640 char *buf, int length, void **private2)
1642 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1643 return;
1646 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1647 struct loadparm_context *lp_ctx,
1648 struct ntlm_auth_state *state,
1649 char *buf, int length, void **private2)
1651 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1652 return;
1655 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1656 struct loadparm_context *lp_ctx,
1657 struct ntlm_auth_state *state,
1658 char *buf, int length, void **private2)
1660 char *request, *parameter;
1661 static DATA_BLOB challenge;
1662 static DATA_BLOB lm_response;
1663 static DATA_BLOB nt_response;
1664 static char *full_username;
1665 static char *username;
1666 static char *domain;
1667 static char *plaintext_password;
1668 static bool ntlm_server_1_user_session_key;
1669 static bool ntlm_server_1_lm_session_key;
1671 if (strequal(buf, ".")) {
1672 if (!full_username && !username) {
1673 printf("Error: No username supplied!\n");
1674 } else if (plaintext_password) {
1675 /* handle this request as plaintext */
1676 if (!full_username) {
1677 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1678 printf("Error: Out of memory in "
1679 "asprintf!\n.\n");
1680 return;
1683 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1684 printf("Authenticated: Yes\n");
1685 } else {
1686 printf("Authenticated: No\n");
1688 } else if (!lm_response.data && !nt_response.data) {
1689 printf("Error: No password supplied!\n");
1690 } else if (!challenge.data) {
1691 printf("Error: No lanman-challenge supplied!\n");
1692 } else {
1693 char *error_string = NULL;
1694 uchar lm_key[8];
1695 uchar user_session_key[16];
1696 uint32_t flags = 0;
1697 NTSTATUS nt_status;
1698 if (full_username && !username) {
1699 fstring fstr_user;
1700 fstring fstr_domain;
1702 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1703 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1704 printf("Error: Could not parse into "
1705 "domain and username\n");
1707 SAFE_FREE(username);
1708 SAFE_FREE(domain);
1709 username = smb_xstrdup(fstr_user);
1710 domain = smb_xstrdup(fstr_domain);
1713 if (opt_password) {
1714 DATA_BLOB nt_session_key, lm_session_key;
1715 struct samr_Password lm_pw, nt_pw;
1716 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1717 ZERO_STRUCT(user_session_key);
1718 ZERO_STRUCT(lm_key);
1720 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1721 nt_status = ntlm_password_check(mem_ctx,
1722 true, true, 0,
1723 &challenge,
1724 &lm_response,
1725 &nt_response,
1726 username,
1727 username,
1728 domain,
1729 &lm_pw, &nt_pw,
1730 &nt_session_key,
1731 &lm_session_key);
1732 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1733 if (ntlm_server_1_user_session_key) {
1734 if (nt_session_key.length == sizeof(user_session_key)) {
1735 memcpy(user_session_key,
1736 nt_session_key.data,
1737 sizeof(user_session_key));
1740 if (ntlm_server_1_lm_session_key) {
1741 if (lm_session_key.length == sizeof(lm_key)) {
1742 memcpy(lm_key,
1743 lm_session_key.data,
1744 sizeof(lm_key));
1747 TALLOC_FREE(mem_ctx);
1749 } else {
1750 uint8_t authoritative = 0;
1752 if (!domain) {
1753 domain = smb_xstrdup(get_winbind_domain());
1756 if (ntlm_server_1_lm_session_key)
1757 flags |= WBFLAG_PAM_LMKEY;
1759 if (ntlm_server_1_user_session_key)
1760 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1762 nt_status = contact_winbind_auth_crap(username,
1763 domain,
1764 lp_netbios_name(),
1765 &challenge,
1766 &lm_response,
1767 &nt_response,
1768 flags, 0,
1769 lm_key,
1770 user_session_key,
1771 &authoritative,
1772 &error_string,
1773 NULL);
1776 if (!NT_STATUS_IS_OK(nt_status)) {
1777 printf("Authenticated: No\n");
1778 printf("Authentication-Error: %s\n.\n",
1779 error_string);
1780 } else {
1781 char *hex_lm_key;
1782 char *hex_user_session_key;
1784 printf("Authenticated: Yes\n");
1786 if (ntlm_server_1_lm_session_key
1787 && (!all_zero(lm_key,
1788 sizeof(lm_key)))) {
1789 hex_lm_key = hex_encode_talloc(NULL,
1790 (const unsigned char *)lm_key,
1791 sizeof(lm_key));
1792 printf("LANMAN-Session-Key: %s\n",
1793 hex_lm_key);
1794 TALLOC_FREE(hex_lm_key);
1797 if (ntlm_server_1_user_session_key
1798 && (!all_zero(user_session_key,
1799 sizeof(user_session_key)))) {
1800 hex_user_session_key = hex_encode_talloc(NULL,
1801 (const unsigned char *)user_session_key,
1802 sizeof(user_session_key));
1803 printf("User-Session-Key: %s\n",
1804 hex_user_session_key);
1805 TALLOC_FREE(hex_user_session_key);
1808 SAFE_FREE(error_string);
1810 /* clear out the state */
1811 challenge = data_blob_null;
1812 nt_response = data_blob_null;
1813 lm_response = data_blob_null;
1814 SAFE_FREE(full_username);
1815 SAFE_FREE(username);
1816 SAFE_FREE(domain);
1817 SAFE_FREE(plaintext_password);
1818 ntlm_server_1_user_session_key = False;
1819 ntlm_server_1_lm_session_key = False;
1820 printf(".\n");
1822 return;
1825 request = buf;
1827 /* Indicates a base64 encoded structure */
1828 parameter = strstr_m(request, ":: ");
1829 if (!parameter) {
1830 parameter = strstr_m(request, ": ");
1832 if (!parameter) {
1833 DEBUG(0, ("Parameter not found!\n"));
1834 printf("Error: Parameter not found!\n.\n");
1835 return;
1838 parameter[0] ='\0';
1839 parameter++;
1840 parameter[0] ='\0';
1841 parameter++;
1843 } else {
1844 parameter[0] ='\0';
1845 parameter++;
1846 parameter[0] ='\0';
1847 parameter++;
1848 parameter[0] ='\0';
1849 parameter++;
1851 base64_decode_inplace(parameter);
1854 if (strequal(request, "LANMAN-Challenge")) {
1855 challenge = strhex_to_data_blob(NULL, parameter);
1856 if (challenge.length != 8) {
1857 printf("Error: hex decode of %s failed! "
1858 "(got %d bytes, expected 8)\n.\n",
1859 parameter,
1860 (int)challenge.length);
1861 challenge = data_blob_null;
1863 } else if (strequal(request, "NT-Response")) {
1864 nt_response = strhex_to_data_blob(NULL, parameter);
1865 if (nt_response.length < 24) {
1866 printf("Error: hex decode of %s failed! "
1867 "(only got %d bytes, needed at least 24)\n.\n",
1868 parameter,
1869 (int)nt_response.length);
1870 nt_response = data_blob_null;
1872 } else if (strequal(request, "LANMAN-Response")) {
1873 lm_response = strhex_to_data_blob(NULL, parameter);
1874 if (lm_response.length != 24) {
1875 printf("Error: hex decode of %s failed! "
1876 "(got %d bytes, expected 24)\n.\n",
1877 parameter,
1878 (int)lm_response.length);
1879 lm_response = data_blob_null;
1881 } else if (strequal(request, "Password")) {
1882 plaintext_password = smb_xstrdup(parameter);
1883 } else if (strequal(request, "NT-Domain")) {
1884 domain = smb_xstrdup(parameter);
1885 } else if (strequal(request, "Username")) {
1886 username = smb_xstrdup(parameter);
1887 } else if (strequal(request, "Full-Username")) {
1888 full_username = smb_xstrdup(parameter);
1889 } else if (strequal(request, "Request-User-Session-Key")) {
1890 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1891 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1892 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1893 } else {
1894 printf("Error: Unknown request %s\n.\n", request);
1898 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
1899 struct loadparm_context *lp_ctx,
1900 struct ntlm_auth_state *state,
1901 char *buf, int length, void **private2)
1903 char *request, *parameter;
1904 static DATA_BLOB new_nt_pswd;
1905 static DATA_BLOB old_nt_hash_enc;
1906 static DATA_BLOB new_lm_pswd;
1907 static DATA_BLOB old_lm_hash_enc;
1908 static char *full_username = NULL;
1909 static char *username = NULL;
1910 static char *domain = NULL;
1911 static char *newpswd = NULL;
1912 static char *oldpswd = NULL;
1914 if (strequal(buf, ".")) {
1915 if(newpswd && oldpswd) {
1916 uchar old_nt_hash[16];
1917 uchar old_lm_hash[16];
1918 uchar new_nt_hash[16];
1919 uchar new_lm_hash[16];
1921 new_nt_pswd = data_blob(NULL, 516);
1922 old_nt_hash_enc = data_blob(NULL, 16);
1924 /* Calculate the MD4 hash (NT compatible) of the
1925 * password */
1926 E_md4hash(oldpswd, old_nt_hash);
1927 E_md4hash(newpswd, new_nt_hash);
1929 /* E_deshash returns false for 'long'
1930 passwords (> 14 DOS chars).
1932 Therefore, don't send a buffer
1933 encrypted with the truncated hash
1934 (it could allow an even easier
1935 attack on the password)
1937 Likewise, obey the admin's restriction
1940 if (lp_client_lanman_auth() &&
1941 E_deshash(newpswd, new_lm_hash) &&
1942 E_deshash(oldpswd, old_lm_hash)) {
1943 new_lm_pswd = data_blob(NULL, 516);
1944 old_lm_hash_enc = data_blob(NULL, 16);
1945 encode_pw_buffer(new_lm_pswd.data, newpswd,
1946 STR_UNICODE);
1948 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
1949 E_old_pw_hash(new_nt_hash, old_lm_hash,
1950 old_lm_hash_enc.data);
1951 } else {
1952 new_lm_pswd.data = NULL;
1953 new_lm_pswd.length = 0;
1954 old_lm_hash_enc.data = NULL;
1955 old_lm_hash_enc.length = 0;
1958 encode_pw_buffer(new_nt_pswd.data, newpswd,
1959 STR_UNICODE);
1961 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
1962 E_old_pw_hash(new_nt_hash, old_nt_hash,
1963 old_nt_hash_enc.data);
1966 if (!full_username && !username) {
1967 printf("Error: No username supplied!\n");
1968 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1969 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1970 printf("Error: No NT or LM password "
1971 "blobs supplied!\n");
1972 } else {
1973 char *error_string = NULL;
1975 if (full_username && !username) {
1976 fstring fstr_user;
1977 fstring fstr_domain;
1979 if (!parse_ntlm_auth_domain_user(full_username,
1980 fstr_user,
1981 fstr_domain)) {
1982 /* username might be 'tainted', don't
1983 * print into our new-line
1984 * deleimianted stream */
1985 printf("Error: Could not "
1986 "parse into domain and "
1987 "username\n");
1988 SAFE_FREE(username);
1989 username = smb_xstrdup(full_username);
1990 } else {
1991 SAFE_FREE(username);
1992 SAFE_FREE(domain);
1993 username = smb_xstrdup(fstr_user);
1994 domain = smb_xstrdup(fstr_domain);
1999 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2000 username, domain,
2001 new_nt_pswd,
2002 old_nt_hash_enc,
2003 new_lm_pswd,
2004 old_lm_hash_enc,
2005 &error_string))) {
2006 printf("Password-Change: No\n");
2007 printf("Password-Change-Error: %s\n.\n",
2008 error_string);
2009 } else {
2010 printf("Password-Change: Yes\n");
2013 SAFE_FREE(error_string);
2015 /* clear out the state */
2016 new_nt_pswd = data_blob_null;
2017 old_nt_hash_enc = data_blob_null;
2018 new_lm_pswd = data_blob_null;
2019 old_nt_hash_enc = data_blob_null;
2020 SAFE_FREE(full_username);
2021 SAFE_FREE(username);
2022 SAFE_FREE(domain);
2023 SAFE_FREE(newpswd);
2024 SAFE_FREE(oldpswd);
2025 printf(".\n");
2027 return;
2030 request = buf;
2032 /* Indicates a base64 encoded structure */
2033 parameter = strstr_m(request, ":: ");
2034 if (!parameter) {
2035 parameter = strstr_m(request, ": ");
2037 if (!parameter) {
2038 DEBUG(0, ("Parameter not found!\n"));
2039 printf("Error: Parameter not found!\n.\n");
2040 return;
2043 parameter[0] ='\0';
2044 parameter++;
2045 parameter[0] ='\0';
2046 parameter++;
2047 } else {
2048 parameter[0] ='\0';
2049 parameter++;
2050 parameter[0] ='\0';
2051 parameter++;
2052 parameter[0] ='\0';
2053 parameter++;
2055 base64_decode_inplace(parameter);
2058 if (strequal(request, "new-nt-password-blob")) {
2059 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2060 if (new_nt_pswd.length != 516) {
2061 printf("Error: hex decode of %s failed! "
2062 "(got %d bytes, expected 516)\n.\n",
2063 parameter,
2064 (int)new_nt_pswd.length);
2065 new_nt_pswd = data_blob_null;
2067 } else if (strequal(request, "old-nt-hash-blob")) {
2068 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2069 if (old_nt_hash_enc.length != 16) {
2070 printf("Error: hex decode of %s failed! "
2071 "(got %d bytes, expected 16)\n.\n",
2072 parameter,
2073 (int)old_nt_hash_enc.length);
2074 old_nt_hash_enc = data_blob_null;
2076 } else if (strequal(request, "new-lm-password-blob")) {
2077 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2078 if (new_lm_pswd.length != 516) {
2079 printf("Error: hex decode of %s failed! "
2080 "(got %d bytes, expected 516)\n.\n",
2081 parameter,
2082 (int)new_lm_pswd.length);
2083 new_lm_pswd = data_blob_null;
2086 else if (strequal(request, "old-lm-hash-blob")) {
2087 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2088 if (old_lm_hash_enc.length != 16)
2090 printf("Error: hex decode of %s failed! "
2091 "(got %d bytes, expected 16)\n.\n",
2092 parameter,
2093 (int)old_lm_hash_enc.length);
2094 old_lm_hash_enc = data_blob_null;
2096 } else if (strequal(request, "nt-domain")) {
2097 domain = smb_xstrdup(parameter);
2098 } else if(strequal(request, "username")) {
2099 username = smb_xstrdup(parameter);
2100 } else if(strequal(request, "full-username")) {
2101 username = smb_xstrdup(parameter);
2102 } else if(strequal(request, "new-password")) {
2103 newpswd = smb_xstrdup(parameter);
2104 } else if (strequal(request, "old-password")) {
2105 oldpswd = smb_xstrdup(parameter);
2106 } else {
2107 printf("Error: Unknown request %s\n.\n", request);
2111 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2112 struct loadparm_context *lp_ctx,
2113 struct ntlm_auth_state *state,
2114 stdio_helper_function fn, void **private2)
2116 char *buf;
2117 char tmp[INITIAL_BUFFER_SIZE+1];
2118 int length, buf_size = 0;
2119 char *c;
2121 buf = talloc_strdup(state->mem_ctx, "");
2122 if (!buf) {
2123 DEBUG(0, ("Failed to allocate input buffer.\n"));
2124 fprintf(stderr, "ERR\n");
2125 exit(1);
2128 do {
2130 /* this is not a typo - x_fgets doesn't work too well under
2131 * squid */
2132 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2133 if (ferror(stdin)) {
2134 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2135 "(%s)\n", ferror(stdin),
2136 strerror(ferror(stdin))));
2138 exit(1);
2140 exit(0);
2143 buf = talloc_strdup_append_buffer(buf, tmp);
2144 buf_size += INITIAL_BUFFER_SIZE;
2146 if (buf_size > MAX_BUFFER_SIZE) {
2147 DEBUG(2, ("Oversized message\n"));
2148 fprintf(stderr, "ERR\n");
2149 talloc_free(buf);
2150 return;
2153 c = strchr(buf, '\n');
2154 } while (c == NULL);
2156 *c = '\0';
2157 length = c-buf;
2159 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2161 if (buf[0] == '\0') {
2162 DEBUG(2, ("Invalid Request\n"));
2163 fprintf(stderr, "ERR\n");
2164 talloc_free(buf);
2165 return;
2168 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2169 talloc_free(buf);
2173 static void squid_stream(enum stdio_helper_mode stdio_mode,
2174 struct loadparm_context *lp_ctx,
2175 stdio_helper_function fn) {
2176 TALLOC_CTX *mem_ctx;
2177 struct ntlm_auth_state *state;
2179 /* initialize FDescs */
2180 setbuf(stdout, NULL);
2181 setbuf(stderr, NULL);
2183 mem_ctx = talloc_init("ntlm_auth");
2184 if (!mem_ctx) {
2185 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2186 fprintf(stderr, "ERR\n");
2187 exit(1);
2190 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2191 if (!state) {
2192 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2193 fprintf(stderr, "ERR\n");
2194 exit(1);
2197 state->mem_ctx = mem_ctx;
2198 state->helper_mode = stdio_mode;
2200 while(1) {
2201 TALLOC_CTX *frame = talloc_stackframe();
2202 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2203 TALLOC_FREE(frame);
2208 /* Authenticate a user with a challenge/response */
2210 static bool check_auth_crap(void)
2212 NTSTATUS nt_status;
2213 uint32_t flags = 0;
2214 char lm_key[8];
2215 char user_session_key[16];
2216 char *hex_lm_key;
2217 char *hex_user_session_key;
2218 char *error_string;
2219 uint8_t authoritative = 0;
2221 setbuf(stdout, NULL);
2223 if (request_lm_key)
2224 flags |= WBFLAG_PAM_LMKEY;
2226 if (request_user_session_key)
2227 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2229 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2231 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2232 opt_workstation,
2233 &opt_challenge,
2234 &opt_lm_response,
2235 &opt_nt_response,
2236 flags, 0,
2237 (unsigned char *)lm_key,
2238 (unsigned char *)user_session_key,
2239 &authoritative,
2240 &error_string, NULL);
2242 if (!NT_STATUS_IS_OK(nt_status)) {
2243 printf("%s (0x%x)\n", error_string,
2244 NT_STATUS_V(nt_status));
2245 SAFE_FREE(error_string);
2246 return False;
2249 if (request_lm_key
2250 && (!all_zero((uint8_t *)lm_key, sizeof(lm_key)))) {
2251 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2252 sizeof(lm_key));
2253 printf("LM_KEY: %s\n", hex_lm_key);
2254 TALLOC_FREE(hex_lm_key);
2256 if (request_user_session_key
2257 && (!all_zero((uint8_t *)user_session_key,
2258 sizeof(user_session_key)))) {
2259 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2260 sizeof(user_session_key));
2261 printf("NT_KEY: %s\n", hex_user_session_key);
2262 TALLOC_FREE(hex_user_session_key);
2265 return True;
2268 /* Main program */
2270 enum {
2271 OPT_USERNAME = 1000,
2272 OPT_DOMAIN,
2273 OPT_WORKSTATION,
2274 OPT_CHALLENGE,
2275 OPT_RESPONSE,
2276 OPT_LM,
2277 OPT_NT,
2278 OPT_PASSWORD,
2279 OPT_LM_KEY,
2280 OPT_USER_SESSION_KEY,
2281 OPT_DIAGNOSTICS,
2282 OPT_REQUIRE_MEMBERSHIP,
2283 OPT_USE_CACHED_CREDS,
2284 OPT_ALLOW_MSCHAPV2,
2285 OPT_PAM_WINBIND_CONF,
2286 OPT_TARGET_SERVICE,
2287 OPT_TARGET_HOSTNAME,
2288 OPT_OFFLINE_LOGON
2291 int main(int argc, const char **argv)
2293 TALLOC_CTX *frame = talloc_stackframe();
2294 int opt;
2295 const char *helper_protocol = NULL;
2296 int diagnostics = 0;
2298 const char *hex_challenge = NULL;
2299 const char *hex_lm_response = NULL;
2300 const char *hex_nt_response = NULL;
2301 struct loadparm_context *lp_ctx;
2302 poptContext pc;
2304 /* NOTE: DO NOT change this interface without considering the implications!
2305 This is an external interface, which other programs will use to interact
2306 with this helper.
2309 /* We do not use single-letter command abbreviations, because they harm future
2310 interface stability. */
2312 struct poptOption long_options[] = {
2313 POPT_AUTOHELP
2314 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2315 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2316 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2317 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2318 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2319 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2320 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2321 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2322 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2323 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2324 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2325 { "allow-mschapv2", 0, POPT_ARG_NONE, &opt_allow_mschapv2, OPT_ALLOW_MSCHAPV2, "Explicitly allow MSCHAPv2" },
2326 { "offline-logon", 0, POPT_ARG_NONE, &offline_logon,
2327 OPT_OFFLINE_LOGON,
2328 "Use cached passwords when DC is offline"},
2329 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2330 OPT_DIAGNOSTICS,
2331 "Perform diagnostics on the authentication chain"},
2332 { "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" },
2333 { "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" },
2334 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2335 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2336 POPT_COMMON_CONFIGFILE
2337 POPT_COMMON_VERSION
2338 POPT_COMMON_OPTION
2339 POPT_TABLEEND
2342 /* Samba client initialisation */
2343 smb_init_locale();
2345 setup_logging("ntlm_auth", DEBUG_STDERR);
2346 fault_setup();
2348 /* Parse options */
2350 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2352 /* Parse command line options */
2354 if (argc == 1) {
2355 poptPrintHelp(pc, stderr, 0);
2356 return 1;
2359 while((opt = poptGetNextOpt(pc)) != -1) {
2360 /* Get generic config options like --configfile */
2363 poptFreeContext(pc);
2365 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2366 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2367 get_dyn_CONFIGFILE(), strerror(errno));
2368 exit(1);
2371 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2372 POPT_CONTEXT_KEEP_FIRST);
2374 while((opt = poptGetNextOpt(pc)) != -1) {
2375 switch (opt) {
2376 case OPT_CHALLENGE:
2377 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2378 if (opt_challenge.length != 8) {
2379 fprintf(stderr, "hex decode of %s failed! "
2380 "(only got %d bytes)\n",
2381 hex_challenge,
2382 (int)opt_challenge.length);
2383 exit(1);
2385 break;
2386 case OPT_LM:
2387 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2388 if (opt_lm_response.length != 24) {
2389 fprintf(stderr, "hex decode of %s failed! "
2390 "(only got %d bytes)\n",
2391 hex_lm_response,
2392 (int)opt_lm_response.length);
2393 exit(1);
2395 break;
2397 case OPT_NT:
2398 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2399 if (opt_nt_response.length < 24) {
2400 fprintf(stderr, "hex decode of %s failed! "
2401 "(only got %d bytes)\n",
2402 hex_nt_response,
2403 (int)opt_nt_response.length);
2404 exit(1);
2406 break;
2408 case OPT_REQUIRE_MEMBERSHIP:
2409 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2410 require_membership_of_sid = require_membership_of;
2412 break;
2416 if (opt_username) {
2417 char *domain = SMB_STRDUP(opt_username);
2418 char *p = strchr_m(domain, *lp_winbind_separator());
2419 if (p) {
2420 opt_username = p+1;
2421 *p = '\0';
2422 if (opt_domain && !strequal(opt_domain, domain)) {
2423 fprintf(stderr, "Domain specified in username (%s) "
2424 "doesn't match specified domain (%s)!\n\n",
2425 domain, opt_domain);
2426 poptPrintHelp(pc, stderr, 0);
2427 exit(1);
2429 opt_domain = domain;
2430 } else {
2431 SAFE_FREE(domain);
2435 /* Note: if opt_domain is "" then send no domain */
2436 if (opt_domain == NULL) {
2437 opt_domain = get_winbind_domain();
2440 if (opt_workstation == NULL) {
2441 opt_workstation = "";
2444 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2445 if (lp_ctx == NULL) {
2446 fprintf(stderr, "loadparm_init_s3() failed!\n");
2447 exit(1);
2450 if (helper_protocol) {
2451 int i;
2452 for (i=0; i<NUM_HELPER_MODES; i++) {
2453 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2454 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2455 exit(0);
2458 fprintf(stderr, "unknown helper protocol [%s]\n\n"
2459 "Valid helper protools:\n\n", helper_protocol);
2461 for (i=0; i<NUM_HELPER_MODES; i++) {
2462 fprintf(stderr, "%s\n",
2463 stdio_helper_protocols[i].name);
2466 exit(1);
2469 if (!opt_username || !*opt_username) {
2470 fprintf(stderr, "username must be specified!\n\n");
2471 poptPrintHelp(pc, stderr, 0);
2472 exit(1);
2475 if (opt_challenge.length) {
2476 if (!check_auth_crap()) {
2477 exit(1);
2479 exit(0);
2482 if (!opt_password) {
2483 char pwd[256] = {0};
2484 int rc;
2486 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2487 if (rc == 0) {
2488 opt_password = SMB_STRDUP(pwd);
2492 if (diagnostics) {
2493 if (!diagnose_ntlm_auth()) {
2494 return 1;
2496 } else {
2497 fstring user;
2499 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2500 if (!check_plaintext_auth(user, opt_password, True)) {
2501 return 1;
2505 /* Exit code */
2507 poptFreeContext(pc);
2508 TALLOC_FREE(frame);
2509 return 0;