selftest/Samba4: create add ${TRUST_DOMSID}-513 to a local group
[Samba.git] / source3 / utils / ntlm_auth.c
blob2c8d9910790bd6643d455b58d79f2e3fe34f081f
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 char *end = rfc1738_unescape(user);
1277 if (end == NULL || (end - user) != strlen(user)) {
1278 DEBUG(2, ("Badly rfc1738 encoded username: %s; "
1279 "denying access\n", user));
1280 printf("ERR\n");
1281 return;
1283 end = rfc1738_unescape(pass);
1284 if (end == NULL || (end - pass) != strlen(pass)) {
1285 DEBUG(2, ("Badly encoded password for %s; "
1286 "denying access\n", user));
1287 printf("ERR\n");
1288 return;
1292 if (check_plaintext_auth(user, pass, False)) {
1293 printf("OK\n");
1294 } else {
1295 printf("ERR\n");
1299 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
1300 struct loadparm_context *lp_ctx,
1301 char *buf, int length, void **private1)
1303 DATA_BLOB in;
1304 DATA_BLOB out = data_blob(NULL, 0);
1305 char *out_base64 = NULL;
1306 const char *reply_arg = NULL;
1307 struct gensec_ntlm_state {
1308 struct gensec_security *gensec_state;
1309 const char *set_password;
1311 struct gensec_ntlm_state *state;
1313 NTSTATUS nt_status;
1314 bool first = false;
1315 const char *reply_code;
1316 struct cli_credentials *creds;
1318 static char *want_feature_list = NULL;
1319 static DATA_BLOB session_key;
1321 TALLOC_CTX *mem_ctx;
1323 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
1325 if (*private1) {
1326 state = (struct gensec_ntlm_state *)*private1;
1327 } else {
1328 state = talloc_zero(NULL, struct gensec_ntlm_state);
1329 if (!state) {
1330 printf("BH No Memory\n");
1331 exit(1);
1333 *private1 = state;
1334 if (opt_password) {
1335 state->set_password = opt_password;
1339 if (strlen(buf) < 2) {
1340 DEBUG(1, ("query [%s] invalid", buf));
1341 printf("BH Query invalid\n");
1342 talloc_free(mem_ctx);
1343 return;
1346 if (strlen(buf) > 3) {
1347 if(strncmp(buf, "SF ", 3) == 0) {
1348 DEBUG(10, ("Setting flags to negotiate\n"));
1349 talloc_free(want_feature_list);
1350 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
1351 printf("OK\n");
1352 talloc_free(mem_ctx);
1353 return;
1355 in = base64_decode_data_blob_talloc(mem_ctx, buf + 3);
1356 } else {
1357 in = data_blob(NULL, 0);
1360 if (strncmp(buf, "YR", 2) == 0) {
1361 if (state->gensec_state) {
1362 talloc_free(state->gensec_state);
1363 state->gensec_state = NULL;
1365 } else if ( (strncmp(buf, "OK", 2) == 0)) {
1366 /* Just return BH, like ntlm_auth from Samba 3 does. */
1367 printf("BH Command expected\n");
1368 talloc_free(mem_ctx);
1369 return;
1370 } else if ( (strncmp(buf, "TT ", 3) != 0) &&
1371 (strncmp(buf, "KK ", 3) != 0) &&
1372 (strncmp(buf, "AF ", 3) != 0) &&
1373 (strncmp(buf, "NA ", 3) != 0) &&
1374 (strncmp(buf, "UG", 2) != 0) &&
1375 (strncmp(buf, "PW ", 3) != 0) &&
1376 (strncmp(buf, "GK", 2) != 0) &&
1377 (strncmp(buf, "GF", 2) != 0)) {
1378 DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
1379 printf("BH SPNEGO request invalid prefix\n");
1380 talloc_free(mem_ctx);
1381 return;
1384 /* setup gensec */
1385 if (!(state->gensec_state)) {
1386 switch (stdio_helper_mode) {
1387 case GSS_SPNEGO_CLIENT:
1389 * cached credentials are only supported by
1390 * NTLMSSP_CLIENT_1 for now.
1392 use_cached_creds = false;
1393 FALL_THROUGH;
1394 case NTLMSSP_CLIENT_1:
1395 /* setup the client side */
1397 if (state->set_password != NULL) {
1398 use_cached_creds = false;
1401 if (use_cached_creds) {
1402 struct wbcCredentialCacheParams params;
1403 struct wbcCredentialCacheInfo *info = NULL;
1404 struct wbcAuthErrorInfo *error = NULL;
1405 wbcErr wbc_status;
1407 params.account_name = opt_username;
1408 params.domain_name = opt_domain;
1409 params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
1410 params.num_blobs = 0;
1411 params.blobs = NULL;
1413 wbc_status = wbcCredentialCache(&params, &info,
1414 &error);
1415 wbcFreeMemory(error);
1416 if (!WBC_ERROR_IS_OK(wbc_status)) {
1417 use_cached_creds = false;
1419 wbcFreeMemory(info);
1422 nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
1423 &state->gensec_state);
1424 if (!NT_STATUS_IS_OK(nt_status)) {
1425 printf("BH GENSEC mech failed to start: %s\n",
1426 nt_errstr(nt_status));
1427 talloc_free(mem_ctx);
1428 return;
1431 creds = cli_credentials_init(state->gensec_state);
1432 cli_credentials_set_conf(creds, lp_ctx);
1433 if (opt_username) {
1434 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
1436 if (opt_domain) {
1437 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
1439 if (use_cached_creds) {
1440 gensec_want_feature(state->gensec_state,
1441 GENSEC_FEATURE_NTLM_CCACHE);
1442 } else if (state->set_password) {
1443 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
1444 } else {
1445 cli_credentials_set_password_callback(creds, get_password);
1447 if (opt_workstation) {
1448 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
1451 gensec_set_credentials(state->gensec_state, creds);
1453 break;
1454 case GSS_SPNEGO_SERVER:
1455 case SQUID_2_5_NTLMSSP:
1457 nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
1458 &state->gensec_state);
1459 if (!NT_STATUS_IS_OK(nt_status)) {
1460 printf("BH GENSEC mech failed to start: %s\n",
1461 nt_errstr(nt_status));
1462 talloc_free(mem_ctx);
1463 return;
1465 break;
1467 default:
1468 talloc_free(mem_ctx);
1469 abort();
1472 gensec_want_feature_list(state->gensec_state, want_feature_list);
1474 /* Session info is not complete, do not pass to auth log */
1475 gensec_want_feature(state->gensec_state, GENSEC_FEATURE_NO_AUTHZ_LOG);
1477 switch (stdio_helper_mode) {
1478 case GSS_SPNEGO_CLIENT:
1479 case GSS_SPNEGO_SERVER:
1480 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
1481 if (!in.length) {
1482 first = true;
1484 break;
1485 case NTLMSSP_CLIENT_1:
1486 if (!in.length) {
1487 first = true;
1489 FALL_THROUGH;
1490 case SQUID_2_5_NTLMSSP:
1491 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
1492 break;
1493 default:
1494 talloc_free(mem_ctx);
1495 abort();
1498 if (!NT_STATUS_IS_OK(nt_status)) {
1499 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
1500 printf("BH GENSEC mech failed to start\n");
1501 talloc_free(mem_ctx);
1502 return;
1507 /* update */
1509 if (strncmp(buf, "PW ", 3) == 0) {
1510 state->set_password = talloc_strndup(state,
1511 (const char *)in.data,
1512 in.length);
1514 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
1515 state->set_password,
1516 CRED_SPECIFIED);
1517 printf("OK\n");
1518 talloc_free(mem_ctx);
1519 return;
1522 if (strncmp(buf, "GK", 2) == 0) {
1523 char *base64_key;
1524 DEBUG(10, ("Requested session key\n"));
1525 nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
1526 if(!NT_STATUS_IS_OK(nt_status)) {
1527 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
1528 printf("BH No session key\n");
1529 talloc_free(mem_ctx);
1530 return;
1531 } else {
1532 base64_key = base64_encode_data_blob(state, session_key);
1533 SMB_ASSERT(base64_key != NULL);
1534 printf("GK %s\n", base64_key);
1535 talloc_free(base64_key);
1537 talloc_free(mem_ctx);
1538 return;
1541 if (strncmp(buf, "GF", 2) == 0) {
1542 uint32_t neg_flags;
1544 DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
1546 neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
1547 if (neg_flags == 0) {
1548 printf("BH\n");
1549 talloc_free(mem_ctx);
1550 return;
1553 printf("GF 0x%08x\n", neg_flags);
1554 talloc_free(mem_ctx);
1555 return;
1558 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
1560 /* don't leak 'bad password'/'no such user' info to the network client */
1561 nt_status = nt_status_squash(nt_status);
1563 if (out.length) {
1564 out_base64 = base64_encode_data_blob(mem_ctx, out);
1565 SMB_ASSERT(out_base64 != NULL);
1566 } else {
1567 out_base64 = NULL;
1570 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1571 reply_arg = "*";
1572 if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
1573 reply_code = "YR";
1574 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1575 reply_code = "KK";
1576 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
1577 reply_code = "TT";
1578 } else {
1579 abort();
1583 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
1584 reply_code = "BH NT_STATUS_ACCESS_DENIED";
1585 reply_arg = nt_errstr(nt_status);
1586 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1587 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
1588 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
1589 reply_arg = nt_errstr(nt_status);
1590 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1591 } else if (!NT_STATUS_IS_OK(nt_status)) {
1592 reply_code = "NA";
1593 reply_arg = nt_errstr(nt_status);
1594 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
1595 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
1596 struct auth_session_info *session_info;
1598 nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
1599 if (!NT_STATUS_IS_OK(nt_status)) {
1600 reply_code = "BH Failed to retrive session info";
1601 reply_arg = nt_errstr(nt_status);
1602 DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
1603 } else {
1605 reply_code = "AF";
1606 reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
1607 if (reply_arg == NULL) {
1608 reply_code = "BH out of memory";
1609 reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
1611 talloc_free(session_info);
1613 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
1614 reply_code = "AF";
1615 reply_arg = out_base64;
1616 } else {
1617 abort();
1620 switch (stdio_helper_mode) {
1621 case GSS_SPNEGO_SERVER:
1622 printf("%s %s %s\n", reply_code,
1623 out_base64 ? out_base64 : "*",
1624 reply_arg ? reply_arg : "*");
1625 break;
1626 default:
1627 if (out_base64) {
1628 printf("%s %s\n", reply_code, out_base64);
1629 } else if (reply_arg) {
1630 printf("%s %s\n", reply_code, reply_arg);
1631 } else {
1632 printf("%s\n", reply_code);
1636 talloc_free(mem_ctx);
1637 return;
1640 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1641 struct loadparm_context *lp_ctx,
1642 struct ntlm_auth_state *state,
1643 char *buf, int length, void **private2)
1645 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1646 return;
1649 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
1650 struct loadparm_context *lp_ctx,
1651 struct ntlm_auth_state *state,
1652 char *buf, int length, void **private2)
1654 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1655 return;
1658 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1659 struct loadparm_context *lp_ctx,
1660 struct ntlm_auth_state *state,
1661 char *buf, int length, void **private2)
1663 manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
1664 return;
1667 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1668 struct loadparm_context *lp_ctx,
1669 struct ntlm_auth_state *state,
1670 char *buf, int length, void **private2)
1672 char *request, *parameter;
1673 static DATA_BLOB challenge;
1674 static DATA_BLOB lm_response;
1675 static DATA_BLOB nt_response;
1676 static char *full_username;
1677 static char *username;
1678 static char *domain;
1679 static char *plaintext_password;
1680 static bool ntlm_server_1_user_session_key;
1681 static bool ntlm_server_1_lm_session_key;
1683 if (strequal(buf, ".")) {
1684 if (!full_username && !username) {
1685 printf("Error: No username supplied!\n");
1686 } else if (plaintext_password) {
1687 /* handle this request as plaintext */
1688 if (!full_username) {
1689 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1690 printf("Error: Out of memory in "
1691 "asprintf!\n.\n");
1692 return;
1695 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1696 printf("Authenticated: Yes\n");
1697 } else {
1698 printf("Authenticated: No\n");
1700 } else if (!lm_response.data && !nt_response.data) {
1701 printf("Error: No password supplied!\n");
1702 } else if (!challenge.data) {
1703 printf("Error: No lanman-challenge supplied!\n");
1704 } else {
1705 char *error_string = NULL;
1706 uchar lm_key[8];
1707 uchar user_session_key[16];
1708 uint32_t flags = 0;
1709 NTSTATUS nt_status;
1710 if (full_username && !username) {
1711 fstring fstr_user;
1712 fstring fstr_domain;
1714 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1715 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1716 printf("Error: Could not parse into "
1717 "domain and username\n");
1719 SAFE_FREE(username);
1720 SAFE_FREE(domain);
1721 username = smb_xstrdup(fstr_user);
1722 domain = smb_xstrdup(fstr_domain);
1725 if (opt_password) {
1726 DATA_BLOB nt_session_key, lm_session_key;
1727 struct samr_Password lm_pw, nt_pw;
1728 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1729 ZERO_STRUCT(user_session_key);
1730 ZERO_STRUCT(lm_key);
1732 nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
1733 nt_status = ntlm_password_check(mem_ctx,
1734 true, true, 0,
1735 &challenge,
1736 &lm_response,
1737 &nt_response,
1738 username,
1739 username,
1740 domain,
1741 &lm_pw, &nt_pw,
1742 &nt_session_key,
1743 &lm_session_key);
1744 error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
1745 if (ntlm_server_1_user_session_key) {
1746 if (nt_session_key.length == sizeof(user_session_key)) {
1747 memcpy(user_session_key,
1748 nt_session_key.data,
1749 sizeof(user_session_key));
1752 if (ntlm_server_1_lm_session_key) {
1753 if (lm_session_key.length == sizeof(lm_key)) {
1754 memcpy(lm_key,
1755 lm_session_key.data,
1756 sizeof(lm_key));
1759 TALLOC_FREE(mem_ctx);
1761 } else {
1762 uint8_t authoritative = 0;
1764 if (!domain) {
1765 domain = smb_xstrdup(get_winbind_domain());
1768 if (ntlm_server_1_lm_session_key)
1769 flags |= WBFLAG_PAM_LMKEY;
1771 if (ntlm_server_1_user_session_key)
1772 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1774 nt_status = contact_winbind_auth_crap(username,
1775 domain,
1776 lp_netbios_name(),
1777 &challenge,
1778 &lm_response,
1779 &nt_response,
1780 flags, 0,
1781 lm_key,
1782 user_session_key,
1783 &authoritative,
1784 &error_string,
1785 NULL);
1788 if (!NT_STATUS_IS_OK(nt_status)) {
1789 printf("Authenticated: No\n");
1790 printf("Authentication-Error: %s\n.\n",
1791 error_string);
1792 } else {
1793 char *hex_lm_key;
1794 char *hex_user_session_key;
1796 printf("Authenticated: Yes\n");
1798 if (ntlm_server_1_lm_session_key
1799 && (!all_zero(lm_key,
1800 sizeof(lm_key)))) {
1801 hex_lm_key = hex_encode_talloc(NULL,
1802 (const unsigned char *)lm_key,
1803 sizeof(lm_key));
1804 printf("LANMAN-Session-Key: %s\n",
1805 hex_lm_key);
1806 TALLOC_FREE(hex_lm_key);
1809 if (ntlm_server_1_user_session_key
1810 && (!all_zero(user_session_key,
1811 sizeof(user_session_key)))) {
1812 hex_user_session_key = hex_encode_talloc(NULL,
1813 (const unsigned char *)user_session_key,
1814 sizeof(user_session_key));
1815 printf("User-Session-Key: %s\n",
1816 hex_user_session_key);
1817 TALLOC_FREE(hex_user_session_key);
1820 SAFE_FREE(error_string);
1822 /* clear out the state */
1823 challenge = data_blob_null;
1824 nt_response = data_blob_null;
1825 lm_response = data_blob_null;
1826 SAFE_FREE(full_username);
1827 SAFE_FREE(username);
1828 SAFE_FREE(domain);
1829 SAFE_FREE(plaintext_password);
1830 ntlm_server_1_user_session_key = False;
1831 ntlm_server_1_lm_session_key = False;
1832 printf(".\n");
1834 return;
1837 request = buf;
1839 /* Indicates a base64 encoded structure */
1840 parameter = strstr_m(request, ":: ");
1841 if (!parameter) {
1842 parameter = strstr_m(request, ": ");
1844 if (!parameter) {
1845 DEBUG(0, ("Parameter not found!\n"));
1846 printf("Error: Parameter not found!\n.\n");
1847 return;
1850 parameter[0] ='\0';
1851 parameter++;
1852 parameter[0] ='\0';
1853 parameter++;
1855 } else {
1856 parameter[0] ='\0';
1857 parameter++;
1858 parameter[0] ='\0';
1859 parameter++;
1860 parameter[0] ='\0';
1861 parameter++;
1863 base64_decode_inplace(parameter);
1866 if (strequal(request, "LANMAN-Challenge")) {
1867 challenge = strhex_to_data_blob(NULL, parameter);
1868 if (challenge.length != 8) {
1869 printf("Error: hex decode of %s failed! "
1870 "(got %d bytes, expected 8)\n.\n",
1871 parameter,
1872 (int)challenge.length);
1873 challenge = data_blob_null;
1875 } else if (strequal(request, "NT-Response")) {
1876 nt_response = strhex_to_data_blob(NULL, parameter);
1877 if (nt_response.length < 24) {
1878 printf("Error: hex decode of %s failed! "
1879 "(only got %d bytes, needed at least 24)\n.\n",
1880 parameter,
1881 (int)nt_response.length);
1882 nt_response = data_blob_null;
1884 } else if (strequal(request, "LANMAN-Response")) {
1885 lm_response = strhex_to_data_blob(NULL, parameter);
1886 if (lm_response.length != 24) {
1887 printf("Error: hex decode of %s failed! "
1888 "(got %d bytes, expected 24)\n.\n",
1889 parameter,
1890 (int)lm_response.length);
1891 lm_response = data_blob_null;
1893 } else if (strequal(request, "Password")) {
1894 plaintext_password = smb_xstrdup(parameter);
1895 } else if (strequal(request, "NT-Domain")) {
1896 domain = smb_xstrdup(parameter);
1897 } else if (strequal(request, "Username")) {
1898 username = smb_xstrdup(parameter);
1899 } else if (strequal(request, "Full-Username")) {
1900 full_username = smb_xstrdup(parameter);
1901 } else if (strequal(request, "Request-User-Session-Key")) {
1902 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1903 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1904 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1905 } else {
1906 printf("Error: Unknown request %s\n.\n", request);
1910 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
1911 struct loadparm_context *lp_ctx,
1912 struct ntlm_auth_state *state,
1913 char *buf, int length, void **private2)
1915 char *request, *parameter;
1916 static DATA_BLOB new_nt_pswd;
1917 static DATA_BLOB old_nt_hash_enc;
1918 static DATA_BLOB new_lm_pswd;
1919 static DATA_BLOB old_lm_hash_enc;
1920 static char *full_username = NULL;
1921 static char *username = NULL;
1922 static char *domain = NULL;
1923 static char *newpswd = NULL;
1924 static char *oldpswd = NULL;
1926 if (strequal(buf, ".")) {
1927 if(newpswd && oldpswd) {
1928 uchar old_nt_hash[16];
1929 uchar old_lm_hash[16];
1930 uchar new_nt_hash[16];
1931 uchar new_lm_hash[16];
1933 new_nt_pswd = data_blob(NULL, 516);
1934 old_nt_hash_enc = data_blob(NULL, 16);
1936 /* Calculate the MD4 hash (NT compatible) of the
1937 * password */
1938 E_md4hash(oldpswd, old_nt_hash);
1939 E_md4hash(newpswd, new_nt_hash);
1941 /* E_deshash returns false for 'long'
1942 passwords (> 14 DOS chars).
1944 Therefore, don't send a buffer
1945 encrypted with the truncated hash
1946 (it could allow an even easier
1947 attack on the password)
1949 Likewise, obey the admin's restriction
1952 if (lp_client_lanman_auth() &&
1953 E_deshash(newpswd, new_lm_hash) &&
1954 E_deshash(oldpswd, old_lm_hash)) {
1955 new_lm_pswd = data_blob(NULL, 516);
1956 old_lm_hash_enc = data_blob(NULL, 16);
1957 encode_pw_buffer(new_lm_pswd.data, newpswd,
1958 STR_UNICODE);
1960 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
1961 E_old_pw_hash(new_nt_hash, old_lm_hash,
1962 old_lm_hash_enc.data);
1963 } else {
1964 new_lm_pswd.data = NULL;
1965 new_lm_pswd.length = 0;
1966 old_lm_hash_enc.data = NULL;
1967 old_lm_hash_enc.length = 0;
1970 encode_pw_buffer(new_nt_pswd.data, newpswd,
1971 STR_UNICODE);
1973 arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
1974 E_old_pw_hash(new_nt_hash, old_nt_hash,
1975 old_nt_hash_enc.data);
1978 if (!full_username && !username) {
1979 printf("Error: No username supplied!\n");
1980 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1981 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1982 printf("Error: No NT or LM password "
1983 "blobs supplied!\n");
1984 } else {
1985 char *error_string = NULL;
1987 if (full_username && !username) {
1988 fstring fstr_user;
1989 fstring fstr_domain;
1991 if (!parse_ntlm_auth_domain_user(full_username,
1992 fstr_user,
1993 fstr_domain)) {
1994 /* username might be 'tainted', don't
1995 * print into our new-line
1996 * deleimianted stream */
1997 printf("Error: Could not "
1998 "parse into domain and "
1999 "username\n");
2000 SAFE_FREE(username);
2001 username = smb_xstrdup(full_username);
2002 } else {
2003 SAFE_FREE(username);
2004 SAFE_FREE(domain);
2005 username = smb_xstrdup(fstr_user);
2006 domain = smb_xstrdup(fstr_domain);
2011 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2012 username, domain,
2013 new_nt_pswd,
2014 old_nt_hash_enc,
2015 new_lm_pswd,
2016 old_lm_hash_enc,
2017 &error_string))) {
2018 printf("Password-Change: No\n");
2019 printf("Password-Change-Error: %s\n.\n",
2020 error_string);
2021 } else {
2022 printf("Password-Change: Yes\n");
2025 SAFE_FREE(error_string);
2027 /* clear out the state */
2028 new_nt_pswd = data_blob_null;
2029 old_nt_hash_enc = data_blob_null;
2030 new_lm_pswd = data_blob_null;
2031 old_nt_hash_enc = data_blob_null;
2032 SAFE_FREE(full_username);
2033 SAFE_FREE(username);
2034 SAFE_FREE(domain);
2035 SAFE_FREE(newpswd);
2036 SAFE_FREE(oldpswd);
2037 printf(".\n");
2039 return;
2042 request = buf;
2044 /* Indicates a base64 encoded structure */
2045 parameter = strstr_m(request, ":: ");
2046 if (!parameter) {
2047 parameter = strstr_m(request, ": ");
2049 if (!parameter) {
2050 DEBUG(0, ("Parameter not found!\n"));
2051 printf("Error: Parameter not found!\n.\n");
2052 return;
2055 parameter[0] ='\0';
2056 parameter++;
2057 parameter[0] ='\0';
2058 parameter++;
2059 } else {
2060 parameter[0] ='\0';
2061 parameter++;
2062 parameter[0] ='\0';
2063 parameter++;
2064 parameter[0] ='\0';
2065 parameter++;
2067 base64_decode_inplace(parameter);
2070 if (strequal(request, "new-nt-password-blob")) {
2071 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2072 if (new_nt_pswd.length != 516) {
2073 printf("Error: hex decode of %s failed! "
2074 "(got %d bytes, expected 516)\n.\n",
2075 parameter,
2076 (int)new_nt_pswd.length);
2077 new_nt_pswd = data_blob_null;
2079 } else if (strequal(request, "old-nt-hash-blob")) {
2080 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2081 if (old_nt_hash_enc.length != 16) {
2082 printf("Error: hex decode of %s failed! "
2083 "(got %d bytes, expected 16)\n.\n",
2084 parameter,
2085 (int)old_nt_hash_enc.length);
2086 old_nt_hash_enc = data_blob_null;
2088 } else if (strequal(request, "new-lm-password-blob")) {
2089 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2090 if (new_lm_pswd.length != 516) {
2091 printf("Error: hex decode of %s failed! "
2092 "(got %d bytes, expected 516)\n.\n",
2093 parameter,
2094 (int)new_lm_pswd.length);
2095 new_lm_pswd = data_blob_null;
2098 else if (strequal(request, "old-lm-hash-blob")) {
2099 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2100 if (old_lm_hash_enc.length != 16)
2102 printf("Error: hex decode of %s failed! "
2103 "(got %d bytes, expected 16)\n.\n",
2104 parameter,
2105 (int)old_lm_hash_enc.length);
2106 old_lm_hash_enc = data_blob_null;
2108 } else if (strequal(request, "nt-domain")) {
2109 domain = smb_xstrdup(parameter);
2110 } else if(strequal(request, "username")) {
2111 username = smb_xstrdup(parameter);
2112 } else if(strequal(request, "full-username")) {
2113 username = smb_xstrdup(parameter);
2114 } else if(strequal(request, "new-password")) {
2115 newpswd = smb_xstrdup(parameter);
2116 } else if (strequal(request, "old-password")) {
2117 oldpswd = smb_xstrdup(parameter);
2118 } else {
2119 printf("Error: Unknown request %s\n.\n", request);
2123 static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
2124 struct loadparm_context *lp_ctx,
2125 struct ntlm_auth_state *state,
2126 stdio_helper_function fn, void **private2)
2128 char *buf;
2129 char tmp[INITIAL_BUFFER_SIZE+1];
2130 int length, buf_size = 0;
2131 char *c;
2133 buf = talloc_strdup(state->mem_ctx, "");
2134 if (!buf) {
2135 DEBUG(0, ("Failed to allocate input buffer.\n"));
2136 fprintf(stderr, "ERR\n");
2137 exit(1);
2140 do {
2142 /* this is not a typo - x_fgets doesn't work too well under
2143 * squid */
2144 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2145 if (ferror(stdin)) {
2146 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2147 "(%s)\n", ferror(stdin),
2148 strerror(ferror(stdin))));
2150 exit(1);
2152 exit(0);
2155 buf = talloc_strdup_append_buffer(buf, tmp);
2156 buf_size += INITIAL_BUFFER_SIZE;
2158 if (buf_size > MAX_BUFFER_SIZE) {
2159 DEBUG(2, ("Oversized message\n"));
2160 fprintf(stderr, "ERR\n");
2161 talloc_free(buf);
2162 return;
2165 c = strchr(buf, '\n');
2166 } while (c == NULL);
2168 *c = '\0';
2169 length = c-buf;
2171 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2173 if (buf[0] == '\0') {
2174 DEBUG(2, ("Invalid Request\n"));
2175 fprintf(stderr, "ERR\n");
2176 talloc_free(buf);
2177 return;
2180 fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
2181 talloc_free(buf);
2185 static void squid_stream(enum stdio_helper_mode stdio_mode,
2186 struct loadparm_context *lp_ctx,
2187 stdio_helper_function fn) {
2188 TALLOC_CTX *mem_ctx;
2189 struct ntlm_auth_state *state;
2191 /* initialize FDescs */
2192 setbuf(stdout, NULL);
2193 setbuf(stderr, NULL);
2195 mem_ctx = talloc_init("ntlm_auth");
2196 if (!mem_ctx) {
2197 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2198 fprintf(stderr, "ERR\n");
2199 exit(1);
2202 state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2203 if (!state) {
2204 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2205 fprintf(stderr, "ERR\n");
2206 exit(1);
2209 state->mem_ctx = mem_ctx;
2210 state->helper_mode = stdio_mode;
2212 while(1) {
2213 TALLOC_CTX *frame = talloc_stackframe();
2214 manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
2215 TALLOC_FREE(frame);
2220 /* Authenticate a user with a challenge/response */
2222 static bool check_auth_crap(void)
2224 NTSTATUS nt_status;
2225 uint32_t flags = 0;
2226 char lm_key[8];
2227 char user_session_key[16];
2228 char *hex_lm_key;
2229 char *hex_user_session_key;
2230 char *error_string;
2231 uint8_t authoritative = 0;
2233 setbuf(stdout, NULL);
2235 if (request_lm_key)
2236 flags |= WBFLAG_PAM_LMKEY;
2238 if (request_user_session_key)
2239 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2241 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2243 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2244 opt_workstation,
2245 &opt_challenge,
2246 &opt_lm_response,
2247 &opt_nt_response,
2248 flags, 0,
2249 (unsigned char *)lm_key,
2250 (unsigned char *)user_session_key,
2251 &authoritative,
2252 &error_string, NULL);
2254 if (!NT_STATUS_IS_OK(nt_status)) {
2255 printf("%s (0x%x)\n", error_string,
2256 NT_STATUS_V(nt_status));
2257 SAFE_FREE(error_string);
2258 return False;
2261 if (request_lm_key
2262 && (!all_zero((uint8_t *)lm_key, sizeof(lm_key)))) {
2263 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2264 sizeof(lm_key));
2265 printf("LM_KEY: %s\n", hex_lm_key);
2266 TALLOC_FREE(hex_lm_key);
2268 if (request_user_session_key
2269 && (!all_zero((uint8_t *)user_session_key,
2270 sizeof(user_session_key)))) {
2271 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
2272 sizeof(user_session_key));
2273 printf("NT_KEY: %s\n", hex_user_session_key);
2274 TALLOC_FREE(hex_user_session_key);
2277 return True;
2280 /* Main program */
2282 enum {
2283 OPT_USERNAME = 1000,
2284 OPT_DOMAIN,
2285 OPT_WORKSTATION,
2286 OPT_CHALLENGE,
2287 OPT_RESPONSE,
2288 OPT_LM,
2289 OPT_NT,
2290 OPT_PASSWORD,
2291 OPT_LM_KEY,
2292 OPT_USER_SESSION_KEY,
2293 OPT_DIAGNOSTICS,
2294 OPT_REQUIRE_MEMBERSHIP,
2295 OPT_USE_CACHED_CREDS,
2296 OPT_ALLOW_MSCHAPV2,
2297 OPT_PAM_WINBIND_CONF,
2298 OPT_TARGET_SERVICE,
2299 OPT_TARGET_HOSTNAME,
2300 OPT_OFFLINE_LOGON
2303 int main(int argc, const char **argv)
2305 TALLOC_CTX *frame = talloc_stackframe();
2306 int opt;
2307 const char *helper_protocol = NULL;
2308 int diagnostics = 0;
2310 const char *hex_challenge = NULL;
2311 const char *hex_lm_response = NULL;
2312 const char *hex_nt_response = NULL;
2313 struct loadparm_context *lp_ctx;
2314 poptContext pc;
2316 /* NOTE: DO NOT change this interface without considering the implications!
2317 This is an external interface, which other programs will use to interact
2318 with this helper.
2321 /* We do not use single-letter command abbreviations, because they harm future
2322 interface stability. */
2324 struct poptOption long_options[] = {
2325 POPT_AUTOHELP
2326 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2327 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2328 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2329 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2330 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2331 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2332 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2333 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2334 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2335 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2336 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2337 { "allow-mschapv2", 0, POPT_ARG_NONE, &opt_allow_mschapv2, OPT_ALLOW_MSCHAPV2, "Explicitly allow MSCHAPv2" },
2338 { "offline-logon", 0, POPT_ARG_NONE, &offline_logon,
2339 OPT_OFFLINE_LOGON,
2340 "Use cached passwords when DC is offline"},
2341 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2342 OPT_DIAGNOSTICS,
2343 "Perform diagnostics on the authentication chain"},
2344 { "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" },
2345 { "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" },
2346 { "target-service", 0, POPT_ARG_STRING, &opt_target_service, OPT_TARGET_SERVICE, "Target service (eg http)" },
2347 { "target-hostname", 0, POPT_ARG_STRING, &opt_target_hostname, OPT_TARGET_HOSTNAME, "Target hostname" },
2348 POPT_COMMON_CONFIGFILE
2349 POPT_COMMON_VERSION
2350 POPT_COMMON_OPTION
2351 POPT_TABLEEND
2354 /* Samba client initialisation */
2355 smb_init_locale();
2357 setup_logging("ntlm_auth", DEBUG_STDERR);
2358 fault_setup();
2360 /* Parse options */
2362 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2364 /* Parse command line options */
2366 if (argc == 1) {
2367 poptPrintHelp(pc, stderr, 0);
2368 return 1;
2371 while((opt = poptGetNextOpt(pc)) != -1) {
2372 /* Get generic config options like --configfile */
2375 poptFreeContext(pc);
2377 if (!lp_load_global(get_dyn_CONFIGFILE())) {
2378 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2379 get_dyn_CONFIGFILE(), strerror(errno));
2380 exit(1);
2383 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2384 POPT_CONTEXT_KEEP_FIRST);
2386 while((opt = poptGetNextOpt(pc)) != -1) {
2387 switch (opt) {
2388 case OPT_CHALLENGE:
2389 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2390 if (opt_challenge.length != 8) {
2391 fprintf(stderr, "hex decode of %s failed! "
2392 "(only got %d bytes)\n",
2393 hex_challenge,
2394 (int)opt_challenge.length);
2395 exit(1);
2397 break;
2398 case OPT_LM:
2399 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2400 if (opt_lm_response.length != 24) {
2401 fprintf(stderr, "hex decode of %s failed! "
2402 "(only got %d bytes)\n",
2403 hex_lm_response,
2404 (int)opt_lm_response.length);
2405 exit(1);
2407 break;
2409 case OPT_NT:
2410 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2411 if (opt_nt_response.length < 24) {
2412 fprintf(stderr, "hex decode of %s failed! "
2413 "(only got %d bytes)\n",
2414 hex_nt_response,
2415 (int)opt_nt_response.length);
2416 exit(1);
2418 break;
2420 case OPT_REQUIRE_MEMBERSHIP:
2421 if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2422 require_membership_of_sid = require_membership_of;
2424 break;
2428 if (opt_username) {
2429 char *domain = SMB_STRDUP(opt_username);
2430 char *p = strchr_m(domain, *lp_winbind_separator());
2431 if (p) {
2432 opt_username = p+1;
2433 *p = '\0';
2434 if (opt_domain && !strequal(opt_domain, domain)) {
2435 fprintf(stderr, "Domain specified in username (%s) "
2436 "doesn't match specified domain (%s)!\n\n",
2437 domain, opt_domain);
2438 poptPrintHelp(pc, stderr, 0);
2439 exit(1);
2441 opt_domain = domain;
2442 } else {
2443 SAFE_FREE(domain);
2447 /* Note: if opt_domain is "" then send no domain */
2448 if (opt_domain == NULL) {
2449 opt_domain = get_winbind_domain();
2452 if (opt_workstation == NULL) {
2453 opt_workstation = "";
2456 lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
2457 if (lp_ctx == NULL) {
2458 fprintf(stderr, "loadparm_init_s3() failed!\n");
2459 exit(1);
2462 if (helper_protocol) {
2463 int i;
2464 for (i=0; i<NUM_HELPER_MODES; i++) {
2465 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2466 squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
2467 exit(0);
2470 fprintf(stderr, "unknown helper protocol [%s]\n\n"
2471 "Valid helper protools:\n\n", helper_protocol);
2473 for (i=0; i<NUM_HELPER_MODES; i++) {
2474 fprintf(stderr, "%s\n",
2475 stdio_helper_protocols[i].name);
2478 exit(1);
2481 if (!opt_username || !*opt_username) {
2482 fprintf(stderr, "username must be specified!\n\n");
2483 poptPrintHelp(pc, stderr, 0);
2484 exit(1);
2487 if (opt_challenge.length) {
2488 if (!check_auth_crap()) {
2489 exit(1);
2491 exit(0);
2494 if (!opt_password) {
2495 char pwd[256] = {0};
2496 int rc;
2498 rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
2499 if (rc == 0) {
2500 opt_password = SMB_STRDUP(pwd);
2504 if (diagnostics) {
2505 if (!diagnose_ntlm_auth()) {
2506 return 1;
2508 } else {
2509 fstring user;
2511 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2512 if (!check_plaintext_auth(user, opt_password, True)) {
2513 return 1;
2517 /* Exit code */
2519 poptFreeContext(pc);
2520 TALLOC_FREE(frame);
2521 return 0;