source: trunk-3.0/source/utils/ntlm_auth.c@ 101

Last change on this file since 101 was 22, checked in by Yuri Dario, 18 years ago

Source code upgrade to 3.0.25pre2.

File size: 66.0 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3
4 Winbind status program.
5
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
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24*/
25
26#include "includes.h"
27#include "utils/ntlm_auth.h"
28
29#undef DBGC_CLASS
30#define DBGC_CLASS DBGC_WINBIND
31
32#define SQUID_BUFFER_SIZE 2010
33
34enum stdio_helper_mode {
35 SQUID_2_4_BASIC,
36 SQUID_2_5_BASIC,
37 SQUID_2_5_NTLMSSP,
38 NTLMSSP_CLIENT_1,
39 GSS_SPNEGO,
40 GSS_SPNEGO_CLIENT,
41 NTLM_SERVER_1,
42 NTLM_CHANGE_PASSWORD_1,
43 NUM_HELPER_MODES
44};
45
46typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
47 char *buf, int length);
48
49static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
50 char *buf, int length);
51
52static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
53 char *buf, int length);
54
55static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
56 char *buf, int length);
57
58static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
59 char *buf, int length);
60
61static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
62 char *buf, int length);
63
64static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
65 char *buf, int length);
66
67static void manage_ntlm_change_password_1_request(enum stdio_helper_mode helper_mode, char *buf, int length);
68
69static const struct {
70 enum stdio_helper_mode mode;
71 const char *name;
72 stdio_helper_function fn;
73} stdio_helper_protocols[] = {
74 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
75 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
76 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
77 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
78 { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request},
79 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
80 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
81 { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
82 { NUM_HELPER_MODES, NULL, NULL}
83};
84
85extern int winbindd_fd;
86
87const char *opt_username;
88const char *opt_domain;
89const char *opt_workstation;
90const char *opt_password;
91static DATA_BLOB opt_challenge;
92static DATA_BLOB opt_lm_response;
93static DATA_BLOB opt_nt_response;
94static int request_lm_key;
95static int request_user_session_key;
96static int use_cached_creds;
97
98static const char *require_membership_of;
99static const char *require_membership_of_sid;
100
101static char winbind_separator(void)
102{
103 struct winbindd_response response;
104 static BOOL got_sep;
105 static char sep;
106
107 if (got_sep)
108 return sep;
109
110 ZERO_STRUCT(response);
111
112 /* Send off request */
113
114 if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
115 NSS_STATUS_SUCCESS) {
116 d_printf("could not obtain winbind separator!\n");
117 return *lp_winbind_separator();
118 }
119
120 sep = response.data.info.winbind_separator;
121 got_sep = True;
122
123 if (!sep) {
124 d_printf("winbind separator was NULL!\n");
125 return *lp_winbind_separator();
126 }
127
128 return sep;
129}
130
131const char *get_winbind_domain(void)
132{
133 struct winbindd_response response;
134
135 static fstring winbind_domain;
136 if (*winbind_domain) {
137 return winbind_domain;
138 }
139
140 ZERO_STRUCT(response);
141
142 /* Send off request */
143
144 if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
145 NSS_STATUS_SUCCESS) {
146 DEBUG(0, ("could not obtain winbind domain name!\n"));
147 return lp_workgroup();
148 }
149
150 fstrcpy(winbind_domain, response.data.domain_name);
151
152 return winbind_domain;
153
154}
155
156const char *get_winbind_netbios_name(void)
157{
158 struct winbindd_response response;
159
160 static fstring winbind_netbios_name;
161
162 if (*winbind_netbios_name) {
163 return winbind_netbios_name;
164 }
165
166 ZERO_STRUCT(response);
167
168 /* Send off request */
169
170 if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) !=
171 NSS_STATUS_SUCCESS) {
172 DEBUG(0, ("could not obtain winbind netbios name!\n"));
173 return global_myname();
174 }
175
176 fstrcpy(winbind_netbios_name, response.data.netbios_name);
177
178 return winbind_netbios_name;
179
180}
181
182DATA_BLOB get_challenge(void)
183{
184 static DATA_BLOB chal;
185 if (opt_challenge.length)
186 return opt_challenge;
187
188 chal = data_blob(NULL, 8);
189
190 generate_random_buffer(chal.data, chal.length);
191 return chal;
192}
193
194/* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
195 form DOMAIN/user into a domain and a user */
196
197static BOOL parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
198 fstring user)
199{
200
201 char *p = strchr(domuser,winbind_separator());
202
203 if (!p) {
204 return False;
205 }
206
207 fstrcpy(user, p+1);
208 fstrcpy(domain, domuser);
209 domain[PTR_DIFF(p, domuser)] = 0;
210 strupper_m(domain);
211
212 return True;
213}
214
215static BOOL get_require_membership_sid(void) {
216 struct winbindd_request request;
217 struct winbindd_response response;
218
219 if (!require_membership_of) {
220 return True;
221 }
222
223 if (require_membership_of_sid) {
224 return True;
225 }
226
227 /* Otherwise, ask winbindd for the name->sid request */
228
229 ZERO_STRUCT(request);
230 ZERO_STRUCT(response);
231
232 if (!parse_ntlm_auth_domain_user(require_membership_of,
233 request.data.name.dom_name,
234 request.data.name.name)) {
235 DEBUG(0, ("Could not parse %s into seperate domain/name parts!\n",
236 require_membership_of));
237 return False;
238 }
239
240 if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
241 NSS_STATUS_SUCCESS) {
242 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
243 require_membership_of));
244 return False;
245 }
246
247 require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
248
249 if (require_membership_of_sid)
250 return True;
251
252 return False;
253}
254/* Authenticate a user with a plaintext password */
255
256static BOOL check_plaintext_auth(const char *user, const char *pass,
257 BOOL stdout_diagnostics)
258{
259 struct winbindd_request request;
260 struct winbindd_response response;
261 NSS_STATUS result;
262
263 if (!get_require_membership_sid()) {
264 return False;
265 }
266
267 /* Send off request */
268
269 ZERO_STRUCT(request);
270 ZERO_STRUCT(response);
271
272 fstrcpy(request.data.auth.user, user);
273 fstrcpy(request.data.auth.pass, pass);
274 if (require_membership_of_sid)
275 pstrcpy(request.data.auth.require_membership_of_sid, require_membership_of_sid);
276
277 result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
278
279 /* Display response */
280
281 if (stdout_diagnostics) {
282 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
283 d_printf("Reading winbind reply failed! (0x01)\n");
284 }
285
286 d_printf("%s: %s (0x%x)\n",
287 response.data.auth.nt_status_string,
288 response.data.auth.error_string,
289 response.data.auth.nt_status);
290 } else {
291 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
292 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
293 }
294
295 DEBUG(3, ("%s: %s (0x%x)\n",
296 response.data.auth.nt_status_string,
297 response.data.auth.error_string,
298 response.data.auth.nt_status));
299 }
300
301 return (result == NSS_STATUS_SUCCESS);
302}
303
304/* authenticate a user with an encrypted username/password */
305
306NTSTATUS contact_winbind_auth_crap(const char *username,
307 const char *domain,
308 const char *workstation,
309 const DATA_BLOB *challenge,
310 const DATA_BLOB *lm_response,
311 const DATA_BLOB *nt_response,
312 uint32 flags,
313 uint8 lm_key[8],
314 uint8 user_session_key[16],
315 char **error_string,
316 char **unix_name)
317{
318 NTSTATUS nt_status;
319 NSS_STATUS result;
320 struct winbindd_request request;
321 struct winbindd_response response;
322
323 if (!get_require_membership_sid()) {
324 return NT_STATUS_INVALID_PARAMETER;
325 }
326
327 ZERO_STRUCT(request);
328 ZERO_STRUCT(response);
329
330 request.flags = flags;
331
332 request.data.auth_crap.logon_parameters = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
333
334 if (require_membership_of_sid)
335 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
336
337 fstrcpy(request.data.auth_crap.user, username);
338 fstrcpy(request.data.auth_crap.domain, domain);
339
340 fstrcpy(request.data.auth_crap.workstation,
341 workstation);
342
343 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
344
345 if (lm_response && lm_response->length) {
346 memcpy(request.data.auth_crap.lm_resp,
347 lm_response->data,
348 MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
349 request.data.auth_crap.lm_resp_len = lm_response->length;
350 }
351
352 if (nt_response && nt_response->length) {
353 memcpy(request.data.auth_crap.nt_resp,
354 nt_response->data,
355 MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp)));
356 request.data.auth_crap.nt_resp_len = nt_response->length;
357 }
358
359 result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
360
361 /* Display response */
362
363 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
364 nt_status = NT_STATUS_UNSUCCESSFUL;
365 if (error_string)
366 *error_string = smb_xstrdup("Reading winbind reply failed!");
367 free_response(&response);
368 return nt_status;
369 }
370
371 nt_status = (NT_STATUS(response.data.auth.nt_status));
372 if (!NT_STATUS_IS_OK(nt_status)) {
373 if (error_string)
374 *error_string = smb_xstrdup(response.data.auth.error_string);
375 free_response(&response);
376 return nt_status;
377 }
378
379 if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
380 memcpy(lm_key, response.data.auth.first_8_lm_hash,
381 sizeof(response.data.auth.first_8_lm_hash));
382 }
383 if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
384 memcpy(user_session_key, response.data.auth.user_session_key,
385 sizeof(response.data.auth.user_session_key));
386 }
387
388 if (flags & WBFLAG_PAM_UNIX_NAME) {
389 *unix_name = SMB_STRDUP((char *)response.extra_data.data);
390 if (!*unix_name) {
391 free_response(&response);
392 return NT_STATUS_NO_MEMORY;
393 }
394 }
395
396 free_response(&response);
397 return nt_status;
398}
399
400/* contact server to change user password using auth crap */
401static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
402 const char *domain,
403 const DATA_BLOB new_nt_pswd,
404 const DATA_BLOB old_nt_hash_enc,
405 const DATA_BLOB new_lm_pswd,
406 const DATA_BLOB old_lm_hash_enc,
407 char **error_string)
408{
409 NTSTATUS nt_status;
410 NSS_STATUS result;
411 struct winbindd_request request;
412 struct winbindd_response response;
413
414 if (!get_require_membership_sid())
415 {
416 if(error_string)
417 *error_string = smb_xstrdup("Can't get membership sid.");
418 return NT_STATUS_INVALID_PARAMETER;
419 }
420
421 ZERO_STRUCT(request);
422 ZERO_STRUCT(response);
423
424 if(username != NULL)
425 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
426 if(domain != NULL)
427 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
428
429 if(new_nt_pswd.length)
430 {
431 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
432 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
433 }
434
435 if(old_nt_hash_enc.length)
436 {
437 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));
438 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
439 }
440
441 if(new_lm_pswd.length)
442 {
443 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
444 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
445 }
446
447 if(old_lm_hash_enc.length)
448 {
449 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));
450 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
451 }
452
453 result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
454
455 /* Display response */
456
457 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
458 {
459 nt_status = NT_STATUS_UNSUCCESSFUL;
460 if (error_string)
461 *error_string = smb_xstrdup("Reading winbind reply failed!");
462 free_response(&response);
463 return nt_status;
464 }
465
466 nt_status = (NT_STATUS(response.data.auth.nt_status));
467 if (!NT_STATUS_IS_OK(nt_status))
468 {
469 if (error_string)
470 *error_string = smb_xstrdup(response.data.auth.error_string);
471 free_response(&response);
472 return nt_status;
473 }
474
475 free_response(&response);
476
477 return nt_status;
478}
479
480static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
481{
482 static const char zeros[16] = { 0, };
483 NTSTATUS nt_status;
484 char *error_string;
485 uint8 lm_key[8];
486 uint8 user_sess_key[16];
487 char *unix_name;
488
489 nt_status = contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain,
490 ntlmssp_state->workstation,
491 &ntlmssp_state->chal,
492 &ntlmssp_state->lm_resp,
493 &ntlmssp_state->nt_resp,
494 WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
495 lm_key, user_sess_key,
496 &error_string, &unix_name);
497
498 if (NT_STATUS_IS_OK(nt_status)) {
499 if (memcmp(lm_key, zeros, 8) != 0) {
500 *lm_session_key = data_blob(NULL, 16);
501 memcpy(lm_session_key->data, lm_key, 8);
502 memset(lm_session_key->data+8, '\0', 8);
503 }
504
505 if (memcmp(user_sess_key, zeros, 16) != 0) {
506 *user_session_key = data_blob(user_sess_key, 16);
507 }
508 ntlmssp_state->auth_context = talloc_strdup(ntlmssp_state->mem_ctx, unix_name);
509 SAFE_FREE(unix_name);
510 } else {
511 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
512 ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
513 ntlmssp_state->domain, ntlmssp_state->user,
514 ntlmssp_state->workstation,
515 error_string ? error_string : "unknown error (NULL)"));
516 ntlmssp_state->auth_context = NULL;
517 }
518 return nt_status;
519}
520
521static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
522{
523 NTSTATUS nt_status;
524 uint8 lm_pw[16], nt_pw[16];
525
526 nt_lm_owf_gen (opt_password, nt_pw, lm_pw);
527
528 nt_status = ntlm_password_check(ntlmssp_state->mem_ctx,
529 &ntlmssp_state->chal,
530 &ntlmssp_state->lm_resp,
531 &ntlmssp_state->nt_resp,
532 NULL, NULL,
533 ntlmssp_state->user,
534 ntlmssp_state->user,
535 ntlmssp_state->domain,
536 lm_pw, nt_pw, user_session_key, lm_session_key);
537
538 if (NT_STATUS_IS_OK(nt_status)) {
539 ntlmssp_state->auth_context = talloc_asprintf(ntlmssp_state->mem_ctx,
540 "%s%c%s", ntlmssp_state->domain,
541 *lp_winbind_separator(),
542 ntlmssp_state->user);
543 } else {
544 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
545 ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation,
546 nt_errstr(nt_status)));
547 ntlmssp_state->auth_context = NULL;
548 }
549 return nt_status;
550}
551
552static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_state)
553{
554 NTSTATUS status;
555 if ( (opt_username == NULL) || (opt_domain == NULL) ) {
556 status = NT_STATUS_UNSUCCESSFUL;
557 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
558 return NT_STATUS_INVALID_PARAMETER;
559 }
560
561 status = ntlmssp_client_start(client_ntlmssp_state);
562
563 if (!NT_STATUS_IS_OK(status)) {
564 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
565 nt_errstr(status)));
566 ntlmssp_end(client_ntlmssp_state);
567 return status;
568 }
569
570 status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
571
572 if (!NT_STATUS_IS_OK(status)) {
573 DEBUG(1, ("Could not set username: %s\n",
574 nt_errstr(status)));
575 ntlmssp_end(client_ntlmssp_state);
576 return status;
577 }
578
579 status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
580
581 if (!NT_STATUS_IS_OK(status)) {
582 DEBUG(1, ("Could not set domain: %s\n",
583 nt_errstr(status)));
584 ntlmssp_end(client_ntlmssp_state);
585 return status;
586 }
587
588 if (opt_password) {
589 status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
590
591 if (!NT_STATUS_IS_OK(status)) {
592 DEBUG(1, ("Could not set password: %s\n",
593 nt_errstr(status)));
594 ntlmssp_end(client_ntlmssp_state);
595 return status;
596 }
597 }
598
599 return NT_STATUS_OK;
600}
601
602static NTSTATUS ntlm_auth_start_ntlmssp_server(NTLMSSP_STATE **ntlmssp_state)
603{
604 NTSTATUS status = ntlmssp_server_start(ntlmssp_state);
605
606 if (!NT_STATUS_IS_OK(status)) {
607 DEBUG(1, ("Could not start NTLMSSP server: %s\n",
608 nt_errstr(status)));
609 return status;
610 }
611
612 /* Have we been given a local password, or should we ask winbind? */
613 if (opt_password) {
614 (*ntlmssp_state)->check_password = local_pw_check;
615 (*ntlmssp_state)->get_domain = lp_workgroup;
616 (*ntlmssp_state)->get_global_myname = global_myname;
617 } else {
618 (*ntlmssp_state)->check_password = winbind_pw_check;
619 (*ntlmssp_state)->get_domain = get_winbind_domain;
620 (*ntlmssp_state)->get_global_myname = get_winbind_netbios_name;
621 }
622 return NT_STATUS_OK;
623}
624
625/*******************************************************************
626 Used by firefox to drive NTLM auth to IIS servers.
627*******************************************************************/
628
629static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
630 DATA_BLOB *reply)
631{
632 struct winbindd_request wb_request;
633 struct winbindd_response wb_response;
634 NSS_STATUS result;
635
636 /* get winbindd to do the ntlmssp step on our behalf */
637 ZERO_STRUCT(wb_request);
638 ZERO_STRUCT(wb_response);
639
640 fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
641 "%s%c%s", opt_domain, winbind_separator(), opt_username);
642 wb_request.data.ccache_ntlm_auth.uid = geteuid();
643 wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
644 wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
645 wb_request.extra_len = initial_msg.length + challenge_msg.length;
646
647 if (wb_request.extra_len > 0) {
648 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
649 if (wb_request.extra_data.data == NULL) {
650 return NT_STATUS_NO_MEMORY;
651 }
652
653 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
654 memcpy(wb_request.extra_data.data + initial_msg.length,
655 challenge_msg.data, challenge_msg.length);
656 }
657
658 result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
659 SAFE_FREE(wb_request.extra_data.data);
660
661 if (result != NSS_STATUS_SUCCESS) {
662 free_response(&wb_response);
663 return NT_STATUS_UNSUCCESSFUL;
664 }
665
666 if (reply) {
667 *reply = data_blob(wb_response.extra_data.data,
668 wb_response.data.ccache_ntlm_auth.auth_blob_len);
669 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
670 reply->data == NULL) {
671 free_response(&wb_response);
672 return NT_STATUS_NO_MEMORY;
673 }
674 }
675
676 free_response(&wb_response);
677 return NT_STATUS_MORE_PROCESSING_REQUIRED;
678}
679
680static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
681 char *buf, int length)
682{
683 static NTLMSSP_STATE *ntlmssp_state = NULL;
684 static char* want_feature_list = NULL;
685 static uint32 neg_flags = 0;
686 static BOOL have_session_key = False;
687 static DATA_BLOB session_key;
688 DATA_BLOB request, reply;
689 NTSTATUS nt_status;
690
691 if (strlen(buf) < 2) {
692 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
693 x_fprintf(x_stdout, "BH\n");
694 return;
695 }
696
697 if (strlen(buf) > 3) {
698 if(strncmp(buf, "SF ", 3) == 0){
699 DEBUG(10, ("Setting flags to negotioate\n"));
700 SAFE_FREE(want_feature_list);
701 want_feature_list = SMB_STRNDUP(buf+3, strlen(buf)-3);
702 x_fprintf(x_stdout, "OK\n");
703 return;
704 }
705 request = base64_decode_data_blob(buf + 3);
706 } else {
707 request = data_blob(NULL, 0);
708 }
709
710 if ((strncmp(buf, "PW ", 3) == 0)) {
711 /* The calling application wants us to use a local password (rather than winbindd) */
712
713 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
714
715 if (opt_password == NULL) {
716 DEBUG(1, ("Out of memory\n"));
717 x_fprintf(x_stdout, "BH\n");
718 data_blob_free(&request);
719 return;
720 }
721
722 x_fprintf(x_stdout, "OK\n");
723 data_blob_free(&request);
724 return;
725 }
726
727 if (strncmp(buf, "YR", 2) == 0) {
728 if (ntlmssp_state)
729 ntlmssp_end(&ntlmssp_state);
730 } else if (strncmp(buf, "KK", 2) == 0) {
731
732 } else if (strncmp(buf, "GF", 2) == 0) {
733 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
734 x_fprintf(x_stdout, "GF 0x%08lx\n", have_session_key?neg_flags:0l);
735 data_blob_free(&request);
736 return;
737 } else if (strncmp(buf, "GK", 2) == 0) {
738 DEBUG(10, ("Requested NTLMSSP session key\n"));
739 if(have_session_key) {
740 char *key64 = base64_encode_data_blob(session_key);
741 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
742 SAFE_FREE(key64);
743 } else {
744 x_fprintf(x_stdout, "BH\n");
745 }
746
747 data_blob_free(&request);
748 return;
749 } else {
750 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
751 x_fprintf(x_stdout, "BH\n");
752 return;
753 }
754
755 if (!ntlmssp_state) {
756 if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
757 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
758 return;
759 }
760 ntlmssp_want_feature_list(ntlmssp_state, want_feature_list);
761 }
762
763 DEBUG(10, ("got NTLMSSP packet:\n"));
764 dump_data(10, (const char *)request.data, request.length);
765
766 nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
767
768 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
769 char *reply_base64 = base64_encode_data_blob(reply);
770 x_fprintf(x_stdout, "TT %s\n", reply_base64);
771 SAFE_FREE(reply_base64);
772 data_blob_free(&reply);
773 DEBUG(10, ("NTLMSSP challenge\n"));
774 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
775 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
776 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
777
778 ntlmssp_end(&ntlmssp_state);
779 } else if (!NT_STATUS_IS_OK(nt_status)) {
780 x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
781 DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
782 } else {
783 x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context);
784 DEBUG(10, ("NTLMSSP OK!\n"));
785
786 if(have_session_key)
787 data_blob_free(&session_key);
788 session_key = data_blob(ntlmssp_state->session_key.data,
789 ntlmssp_state->session_key.length);
790 neg_flags = ntlmssp_state->neg_flags;
791 have_session_key = True;
792 }
793
794 data_blob_free(&request);
795}
796
797static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
798 char *buf, int length)
799{
800 /* The statics here are *HORRIBLE* and this entire concept
801 needs to be rewritten. Essentially it's using these statics
802 as the state in a state machine. BLEEEGH ! JRA. */
803
804 static NTLMSSP_STATE *ntlmssp_state = NULL;
805 static DATA_BLOB initial_message;
806 static char* want_feature_list = NULL;
807 static uint32 neg_flags = 0;
808 static BOOL have_session_key = False;
809 static DATA_BLOB session_key;
810 DATA_BLOB request, reply;
811 NTSTATUS nt_status;
812 BOOL first = False;
813
814 if (!opt_username || !*opt_username) {
815 x_fprintf(x_stderr, "username must be specified!\n\n");
816 exit(1);
817 }
818
819 if (strlen(buf) < 2) {
820 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
821 x_fprintf(x_stdout, "BH\n");
822 return;
823 }
824
825 if (strlen(buf) > 3) {
826 if(strncmp(buf, "SF ", 3) == 0) {
827 DEBUG(10, ("Looking for flags to negotiate\n"));
828 SAFE_FREE(want_feature_list);
829 want_feature_list = SMB_STRNDUP(buf+3, strlen(buf)-3);
830 x_fprintf(x_stdout, "OK\n");
831 return;
832 }
833 request = base64_decode_data_blob(buf + 3);
834 } else {
835 request = data_blob(NULL, 0);
836 }
837
838 if (strncmp(buf, "PW ", 3) == 0) {
839 /* We asked for a password and obviously got it :-) */
840
841 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
842
843 if (opt_password == NULL) {
844 DEBUG(1, ("Out of memory\n"));
845 x_fprintf(x_stdout, "BH\n");
846 data_blob_free(&request);
847 return;
848 }
849
850 x_fprintf(x_stdout, "OK\n");
851 data_blob_free(&request);
852 return;
853 }
854
855 if (!ntlmssp_state && use_cached_creds) {
856 /* check whether credentials are usable. */
857 DATA_BLOB empty_blob = data_blob(NULL, 0);
858
859 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
860 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
861 /* failed to use cached creds */
862 use_cached_creds = False;
863 }
864 }
865
866 if (opt_password == NULL && !use_cached_creds) {
867
868 /* Request a password from the calling process. After
869 sending it, the calling process should retry asking for the negotiate. */
870
871 DEBUG(10, ("Requesting password\n"));
872 x_fprintf(x_stdout, "PW\n");
873 return;
874 }
875
876 if (strncmp(buf, "YR", 2) == 0) {
877 if (ntlmssp_state)
878 ntlmssp_end(&ntlmssp_state);
879 } else if (strncmp(buf, "TT", 2) == 0) {
880
881 } else if (strncmp(buf, "GF", 2) == 0) {
882 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
883 x_fprintf(x_stdout, "GF 0x%08lx\n", have_session_key?neg_flags:0l);
884 data_blob_free(&request);
885 return;
886 } else if (strncmp(buf, "GK", 2) == 0 ) {
887 DEBUG(10, ("Requested session key\n"));
888
889 if(have_session_key) {
890 char *key64 = base64_encode_data_blob(session_key);
891 x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
892 SAFE_FREE(key64);
893 }
894 else {
895 x_fprintf(x_stdout, "BH\n");
896 }
897
898 data_blob_free(&request);
899 return;
900 } else {
901 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
902 x_fprintf(x_stdout, "BH\n");
903 return;
904 }
905
906 if (!ntlmssp_state) {
907 if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_client(&ntlmssp_state))) {
908 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
909 return;
910 }
911 ntlmssp_want_feature_list(ntlmssp_state, want_feature_list);
912 first = True;
913 initial_message = data_blob(NULL, 0);
914 }
915
916 DEBUG(10, ("got NTLMSSP packet:\n"));
917 dump_data(10, (const char *)request.data, request.length);
918
919 if (use_cached_creds && !opt_password && !first) {
920 nt_status = do_ccache_ntlm_auth(initial_message, request, &reply);
921 } else {
922 nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
923 }
924
925 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
926 char *reply_base64 = base64_encode_data_blob(reply);
927 if (first) {
928 x_fprintf(x_stdout, "YR %s\n", reply_base64);
929 } else {
930 x_fprintf(x_stdout, "KK %s\n", reply_base64);
931 }
932 SAFE_FREE(reply_base64);
933 if (first) {
934 initial_message = reply;
935 } else {
936 data_blob_free(&reply);
937 }
938 DEBUG(10, ("NTLMSSP challenge\n"));
939 } else if (NT_STATUS_IS_OK(nt_status)) {
940 char *reply_base64 = base64_encode_data_blob(reply);
941 x_fprintf(x_stdout, "AF %s\n", reply_base64);
942 SAFE_FREE(reply_base64);
943
944 if(have_session_key)
945 data_blob_free(&session_key);
946
947 session_key = data_blob(ntlmssp_state->session_key.data,
948 ntlmssp_state->session_key.length);
949 neg_flags = ntlmssp_state->neg_flags;
950 have_session_key = True;
951
952 DEBUG(10, ("NTLMSSP OK!\n"));
953 if (ntlmssp_state)
954 ntlmssp_end(&ntlmssp_state);
955 } else {
956 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
957 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
958 if (ntlmssp_state)
959 ntlmssp_end(&ntlmssp_state);
960 }
961
962 data_blob_free(&request);
963}
964
965static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
966 char *buf, int length)
967{
968 char *user, *pass;
969 user=buf;
970
971 pass=(char *)memchr(buf,' ',length);
972 if (!pass) {
973 DEBUG(2, ("Password not found. Denying access\n"));
974 x_fprintf(x_stdout, "ERR\n");
975 return;
976 }
977 *pass='\0';
978 pass++;
979
980 if (stdio_helper_mode == SQUID_2_5_BASIC) {
981 rfc1738_unescape(user);
982 rfc1738_unescape(pass);
983 }
984
985 if (check_plaintext_auth(user, pass, False)) {
986 x_fprintf(x_stdout, "OK\n");
987 } else {
988 x_fprintf(x_stdout, "ERR\n");
989 }
990}
991
992static void offer_gss_spnego_mechs(void) {
993
994 DATA_BLOB token;
995 SPNEGO_DATA spnego;
996 ssize_t len;
997 char *reply_base64;
998
999 pstring principal;
1000 pstring myname_lower;
1001
1002 ZERO_STRUCT(spnego);
1003
1004 pstrcpy(myname_lower, global_myname());
1005 strlower_m(myname_lower);
1006
1007 pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm());
1008
1009 /* Server negTokenInit (mech offerings) */
1010 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1011 spnego.negTokenInit.mechTypes = SMB_XMALLOC_ARRAY(const char *, 2);
1012#ifdef HAVE_KRB5
1013 spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
1014 spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
1015 spnego.negTokenInit.mechTypes[2] = NULL;
1016#else
1017 spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
1018 spnego.negTokenInit.mechTypes[1] = NULL;
1019#endif
1020
1021
1022 spnego.negTokenInit.mechListMIC = data_blob(principal,
1023 strlen(principal));
1024
1025 len = write_spnego_data(&token, &spnego);
1026 free_spnego_data(&spnego);
1027
1028 if (len == -1) {
1029 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1030 x_fprintf(x_stdout, "BH\n");
1031 return;
1032 }
1033
1034 reply_base64 = base64_encode_data_blob(token);
1035 x_fprintf(x_stdout, "TT %s *\n", reply_base64);
1036
1037 SAFE_FREE(reply_base64);
1038 data_blob_free(&token);
1039 DEBUG(10, ("sent SPNEGO negTokenInit\n"));
1040 return;
1041}
1042
1043static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
1044 char *buf, int length)
1045{
1046 static NTLMSSP_STATE *ntlmssp_state = NULL;
1047 SPNEGO_DATA request, response;
1048 DATA_BLOB token;
1049 NTSTATUS status;
1050 ssize_t len;
1051
1052 char *user = NULL;
1053 char *domain = NULL;
1054
1055 const char *reply_code;
1056 char *reply_base64;
1057 pstring reply_argument;
1058
1059 if (strlen(buf) < 2) {
1060 DEBUG(1, ("SPENGO query [%s] invalid", buf));
1061 x_fprintf(x_stdout, "BH\n");
1062 return;
1063 }
1064
1065 if (strncmp(buf, "YR", 2) == 0) {
1066 if (ntlmssp_state)
1067 ntlmssp_end(&ntlmssp_state);
1068 } else if (strncmp(buf, "KK", 2) == 0) {
1069
1070 } else {
1071 DEBUG(1, ("SPENGO query [%s] invalid", buf));
1072 x_fprintf(x_stdout, "BH\n");
1073 return;
1074 }
1075
1076 if ( (strlen(buf) == 2)) {
1077
1078 /* no client data, get the negTokenInit offering
1079 mechanisms */
1080
1081 offer_gss_spnego_mechs();
1082 return;
1083 }
1084
1085 /* All subsequent requests have a blob. This might be negTokenInit or negTokenTarg */
1086
1087 if (strlen(buf) <= 3) {
1088 DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
1089 x_fprintf(x_stdout, "BH\n");
1090 return;
1091 }
1092
1093 token = base64_decode_data_blob(buf + 3);
1094 len = read_spnego_data(token, &request);
1095 data_blob_free(&token);
1096
1097 if (len == -1) {
1098 DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
1099 x_fprintf(x_stdout, "BH\n");
1100 return;
1101 }
1102
1103 if (request.type == SPNEGO_NEG_TOKEN_INIT) {
1104
1105 /* Second request from Client. This is where the
1106 client offers its mechanism to use. */
1107
1108 if ( (request.negTokenInit.mechTypes == NULL) ||
1109 (request.negTokenInit.mechTypes[0] == NULL) ) {
1110 DEBUG(1, ("Client did not offer any mechanism"));
1111 x_fprintf(x_stdout, "BH\n");
1112 return;
1113 }
1114
1115 status = NT_STATUS_UNSUCCESSFUL;
1116 if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
1117
1118 if ( request.negTokenInit.mechToken.data == NULL ) {
1119 DEBUG(1, ("Client did not provide NTLMSSP data\n"));
1120 x_fprintf(x_stdout, "BH\n");
1121 return;
1122 }
1123
1124 if ( ntlmssp_state != NULL ) {
1125 DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
1126 "already got one\n"));
1127 x_fprintf(x_stdout, "BH\n");
1128 ntlmssp_end(&ntlmssp_state);
1129 return;
1130 }
1131
1132 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
1133 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1134 return;
1135 }
1136
1137 DEBUG(10, ("got NTLMSSP packet:\n"));
1138 dump_data(10, (const char *)request.negTokenInit.mechToken.data,
1139 request.negTokenInit.mechToken.length);
1140
1141 response.type = SPNEGO_NEG_TOKEN_TARG;
1142 response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP);
1143 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
1144
1145 status = ntlmssp_update(ntlmssp_state,
1146 request.negTokenInit.mechToken,
1147 &response.negTokenTarg.responseToken);
1148 }
1149
1150#ifdef HAVE_KRB5
1151 if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
1152
1153 TALLOC_CTX *mem_ctx = talloc_init("manage_gss_spnego_request");
1154 char *principal;
1155 DATA_BLOB ap_rep;
1156 DATA_BLOB session_key;
1157
1158 if ( request.negTokenInit.mechToken.data == NULL ) {
1159 DEBUG(1, ("Client did not provide Kerberos data\n"));
1160 x_fprintf(x_stdout, "BH\n");
1161 return;
1162 }
1163
1164 response.type = SPNEGO_NEG_TOKEN_TARG;
1165 response.negTokenTarg.supportedMech = SMB_STRDUP(OID_KERBEROS5_OLD);
1166 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
1167 response.negTokenTarg.responseToken = data_blob(NULL, 0);
1168
1169 status = ads_verify_ticket(mem_ctx, lp_realm(), 0,
1170 &request.negTokenInit.mechToken,
1171 &principal, NULL, &ap_rep,
1172 &session_key);
1173
1174 talloc_destroy(mem_ctx);
1175
1176 /* Now in "principal" we have the name we are
1177 authenticated as. */
1178
1179 if (NT_STATUS_IS_OK(status)) {
1180
1181 domain = strchr_m(principal, '@');
1182
1183 if (domain == NULL) {
1184 DEBUG(1, ("Did not get a valid principal "
1185 "from ads_verify_ticket\n"));
1186 x_fprintf(x_stdout, "BH\n");
1187 return;
1188 }
1189
1190 *domain++ = '\0';
1191 domain = SMB_STRDUP(domain);
1192 user = SMB_STRDUP(principal);
1193
1194 data_blob_free(&ap_rep);
1195
1196 SAFE_FREE(principal);
1197 }
1198 }
1199#endif
1200
1201 } else {
1202
1203 if ( (request.negTokenTarg.supportedMech == NULL) ||
1204 ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
1205 /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
1206 is the only one we support that sends this stuff */
1207 DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
1208 request.negTokenTarg.supportedMech));
1209 x_fprintf(x_stdout, "BH\n");
1210 return;
1211 }
1212
1213 if (request.negTokenTarg.responseToken.data == NULL) {
1214 DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
1215 x_fprintf(x_stdout, "BH\n");
1216 return;
1217 }
1218
1219 status = ntlmssp_update(ntlmssp_state,
1220 request.negTokenTarg.responseToken,
1221 &response.negTokenTarg.responseToken);
1222
1223 response.type = SPNEGO_NEG_TOKEN_TARG;
1224 response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP);
1225 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
1226
1227 if (NT_STATUS_IS_OK(status)) {
1228 user = SMB_STRDUP(ntlmssp_state->user);
1229 domain = SMB_STRDUP(ntlmssp_state->domain);
1230 ntlmssp_end(&ntlmssp_state);
1231 }
1232 }
1233
1234 free_spnego_data(&request);
1235
1236 if (NT_STATUS_IS_OK(status)) {
1237 response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
1238 reply_code = "AF";
1239 pstr_sprintf(reply_argument, "%s\\%s", domain, user);
1240 } else if (NT_STATUS_EQUAL(status,
1241 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1242 response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1243 reply_code = "TT";
1244 pstr_sprintf(reply_argument, "*");
1245 } else {
1246 response.negTokenTarg.negResult = SPNEGO_REJECT;
1247 reply_code = "NA";
1248 pstrcpy(reply_argument, nt_errstr(status));
1249 }
1250
1251 SAFE_FREE(user);
1252 SAFE_FREE(domain);
1253
1254 len = write_spnego_data(&token, &response);
1255 free_spnego_data(&response);
1256
1257 if (len == -1) {
1258 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1259 x_fprintf(x_stdout, "BH\n");
1260 return;
1261 }
1262
1263 reply_base64 = base64_encode_data_blob(token);
1264
1265 x_fprintf(x_stdout, "%s %s %s\n",
1266 reply_code, reply_base64, reply_argument);
1267
1268 SAFE_FREE(reply_base64);
1269 data_blob_free(&token);
1270
1271 return;
1272}
1273
1274static NTLMSSP_STATE *client_ntlmssp_state = NULL;
1275
1276static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego)
1277{
1278 NTSTATUS status;
1279 DATA_BLOB null_blob = data_blob(NULL, 0);
1280 DATA_BLOB to_server;
1281 char *to_server_base64;
1282 const char *my_mechs[] = {OID_NTLMSSP, NULL};
1283
1284 DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1285
1286 if (client_ntlmssp_state != NULL) {
1287 DEBUG(1, ("Request for initial SPNEGO request where "
1288 "we already have a state\n"));
1289 return False;
1290 }
1291
1292 if (!client_ntlmssp_state) {
1293 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1294 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1295 return False;
1296 }
1297 }
1298
1299
1300 if (opt_password == NULL) {
1301
1302 /* Request a password from the calling process. After
1303 sending it, the calling process should retry with
1304 the negTokenInit. */
1305
1306 DEBUG(10, ("Requesting password\n"));
1307 x_fprintf(x_stdout, "PW\n");
1308 return True;
1309 }
1310
1311 spnego.type = SPNEGO_NEG_TOKEN_INIT;
1312 spnego.negTokenInit.mechTypes = my_mechs;
1313 spnego.negTokenInit.reqFlags = 0;
1314 spnego.negTokenInit.mechListMIC = null_blob;
1315
1316 status = ntlmssp_update(client_ntlmssp_state, null_blob,
1317 &spnego.negTokenInit.mechToken);
1318
1319 if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1320 NT_STATUS_IS_OK(status)) ) {
1321 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1322 nt_errstr(status)));
1323 ntlmssp_end(&client_ntlmssp_state);
1324 return False;
1325 }
1326
1327 write_spnego_data(&to_server, &spnego);
1328 data_blob_free(&spnego.negTokenInit.mechToken);
1329
1330 to_server_base64 = base64_encode_data_blob(to_server);
1331 data_blob_free(&to_server);
1332 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1333 SAFE_FREE(to_server_base64);
1334 return True;
1335}
1336
1337static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
1338{
1339 NTSTATUS status;
1340 DATA_BLOB null_blob = data_blob(NULL, 0);
1341 DATA_BLOB request;
1342 DATA_BLOB to_server;
1343 char *to_server_base64;
1344
1345 DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1346
1347 if (client_ntlmssp_state == NULL) {
1348 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1349 x_fprintf(x_stdout, "BH\n");
1350 return;
1351 }
1352
1353 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1354 x_fprintf(x_stdout, "NA\n");
1355 ntlmssp_end(&client_ntlmssp_state);
1356 return;
1357 }
1358
1359 if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1360 x_fprintf(x_stdout, "AF\n");
1361 ntlmssp_end(&client_ntlmssp_state);
1362 return;
1363 }
1364
1365 status = ntlmssp_update(client_ntlmssp_state,
1366 spnego.negTokenTarg.responseToken,
1367 &request);
1368
1369 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1370 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
1371 "ntlmssp_client_update, got: %s\n",
1372 nt_errstr(status)));
1373 x_fprintf(x_stdout, "BH\n");
1374 data_blob_free(&request);
1375 ntlmssp_end(&client_ntlmssp_state);
1376 return;
1377 }
1378
1379 spnego.type = SPNEGO_NEG_TOKEN_TARG;
1380 spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1381 spnego.negTokenTarg.supportedMech = (char *)OID_NTLMSSP;
1382 spnego.negTokenTarg.responseToken = request;
1383 spnego.negTokenTarg.mechListMIC = null_blob;
1384
1385 write_spnego_data(&to_server, &spnego);
1386 data_blob_free(&request);
1387
1388 to_server_base64 = base64_encode_data_blob(to_server);
1389 data_blob_free(&to_server);
1390 x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1391 SAFE_FREE(to_server_base64);
1392 return;
1393}
1394
1395#ifdef HAVE_KRB5
1396
1397static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
1398{
1399 char *principal;
1400 DATA_BLOB tkt, to_server;
1401 DATA_BLOB session_key_krb5 = data_blob(NULL, 0);
1402 SPNEGO_DATA reply;
1403 char *reply_base64;
1404 int retval;
1405
1406 const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1407 ssize_t len;
1408
1409 if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
1410 (spnego.negTokenInit.mechListMIC.length == 0) ) {
1411 DEBUG(1, ("Did not get a principal for krb5\n"));
1412 return False;
1413 }
1414
1415 principal = (char *)SMB_MALLOC(
1416 spnego.negTokenInit.mechListMIC.length+1);
1417
1418 if (principal == NULL) {
1419 DEBUG(1, ("Could not malloc principal\n"));
1420 return False;
1421 }
1422
1423 memcpy(principal, spnego.negTokenInit.mechListMIC.data,
1424 spnego.negTokenInit.mechListMIC.length);
1425 principal[spnego.negTokenInit.mechListMIC.length] = '\0';
1426
1427 retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL, NULL);
1428
1429 if (retval) {
1430
1431 pstring user;
1432
1433 /* Let's try to first get the TGT, for that we need a
1434 password. */
1435
1436 if (opt_password == NULL) {
1437 DEBUG(10, ("Requesting password\n"));
1438 x_fprintf(x_stdout, "PW\n");
1439 return True;
1440 }
1441
1442 pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
1443
1444 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1445 DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1446 return False;
1447 }
1448
1449 retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL, NULL);
1450
1451 if (retval) {
1452 DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1453 return False;
1454 }
1455 }
1456
1457 data_blob_free(&session_key_krb5);
1458
1459 ZERO_STRUCT(reply);
1460
1461 reply.type = SPNEGO_NEG_TOKEN_INIT;
1462 reply.negTokenInit.mechTypes = my_mechs;
1463 reply.negTokenInit.reqFlags = 0;
1464 reply.negTokenInit.mechToken = tkt;
1465 reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
1466
1467 len = write_spnego_data(&to_server, &reply);
1468 data_blob_free(&tkt);
1469
1470 if (len == -1) {
1471 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1472 return False;
1473 }
1474
1475 reply_base64 = base64_encode_data_blob(to_server);
1476 x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1477
1478 SAFE_FREE(reply_base64);
1479 data_blob_free(&to_server);
1480 DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1481 return True;
1482}
1483
1484static void manage_client_krb5_targ(SPNEGO_DATA spnego)
1485{
1486 switch (spnego.negTokenTarg.negResult) {
1487 case SPNEGO_ACCEPT_INCOMPLETE:
1488 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1489 x_fprintf(x_stdout, "BH\n");
1490 break;
1491 case SPNEGO_ACCEPT_COMPLETED:
1492 DEBUG(10, ("Accept completed\n"));
1493 x_fprintf(x_stdout, "AF\n");
1494 break;
1495 case SPNEGO_REJECT:
1496 DEBUG(10, ("Rejected\n"));
1497 x_fprintf(x_stdout, "NA\n");
1498 break;
1499 default:
1500 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1501 x_fprintf(x_stdout, "AF\n");
1502 }
1503}
1504
1505#endif
1506
1507static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
1508 char *buf, int length)
1509{
1510 DATA_BLOB request;
1511 SPNEGO_DATA spnego;
1512 ssize_t len;
1513
1514 if (!opt_username || !*opt_username) {
1515 x_fprintf(x_stderr, "username must be specified!\n\n");
1516 exit(1);
1517 }
1518
1519 if (strlen(buf) <= 3) {
1520 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
1521 x_fprintf(x_stdout, "BH\n");
1522 return;
1523 }
1524
1525 request = base64_decode_data_blob(buf+3);
1526
1527 if (strncmp(buf, "PW ", 3) == 0) {
1528
1529 /* We asked for a password and obviously got it :-) */
1530
1531 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
1532
1533 if (opt_password == NULL) {
1534 DEBUG(1, ("Out of memory\n"));
1535 x_fprintf(x_stdout, "BH\n");
1536 data_blob_free(&request);
1537 return;
1538 }
1539
1540 x_fprintf(x_stdout, "OK\n");
1541 data_blob_free(&request);
1542 return;
1543 }
1544
1545 if ( (strncmp(buf, "TT ", 3) != 0) &&
1546 (strncmp(buf, "AF ", 3) != 0) &&
1547 (strncmp(buf, "NA ", 3) != 0) ) {
1548 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
1549 x_fprintf(x_stdout, "BH\n");
1550 data_blob_free(&request);
1551 return;
1552 }
1553
1554 /* So we got a server challenge to generate a SPNEGO
1555 client-to-server request... */
1556
1557 len = read_spnego_data(request, &spnego);
1558 data_blob_free(&request);
1559
1560 if (len == -1) {
1561 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
1562 x_fprintf(x_stdout, "BH\n");
1563 return;
1564 }
1565
1566 if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
1567
1568 /* The server offers a list of mechanisms */
1569
1570 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
1571
1572 while (*mechType != NULL) {
1573
1574#ifdef HAVE_KRB5
1575 if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
1576 (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
1577 if (manage_client_krb5_init(spnego))
1578 goto out;
1579 }
1580#endif
1581
1582 if (strcmp(*mechType, OID_NTLMSSP) == 0) {
1583 if (manage_client_ntlmssp_init(spnego))
1584 goto out;
1585 }
1586
1587 mechType++;
1588 }
1589
1590 DEBUG(1, ("Server offered no compatible mechanism\n"));
1591 x_fprintf(x_stdout, "BH\n");
1592 return;
1593 }
1594
1595 if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
1596
1597 if (spnego.negTokenTarg.supportedMech == NULL) {
1598 /* On accept/reject Windows does not send the
1599 mechanism anymore. Handle that here and
1600 shut down the mechanisms. */
1601
1602 switch (spnego.negTokenTarg.negResult) {
1603 case SPNEGO_ACCEPT_COMPLETED:
1604 x_fprintf(x_stdout, "AF\n");
1605 break;
1606 case SPNEGO_REJECT:
1607 x_fprintf(x_stdout, "NA\n");
1608 break;
1609 default:
1610 DEBUG(1, ("Got a negTokenTarg with no mech and an "
1611 "unknown negResult: %d\n",
1612 spnego.negTokenTarg.negResult));
1613 x_fprintf(x_stdout, "BH\n");
1614 }
1615
1616 ntlmssp_end(&client_ntlmssp_state);
1617 goto out;
1618 }
1619
1620 if (strcmp(spnego.negTokenTarg.supportedMech,
1621 OID_NTLMSSP) == 0) {
1622 manage_client_ntlmssp_targ(spnego);
1623 goto out;
1624 }
1625
1626#if HAVE_KRB5
1627 if (strcmp(spnego.negTokenTarg.supportedMech,
1628 OID_KERBEROS5_OLD) == 0) {
1629 manage_client_krb5_targ(spnego);
1630 goto out;
1631 }
1632#endif
1633
1634 }
1635
1636 DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
1637 x_fprintf(x_stdout, "BH\n");
1638 return;
1639
1640 out:
1641 free_spnego_data(&spnego);
1642 return;
1643}
1644
1645static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
1646 char *buf, int length)
1647{
1648 char *request, *parameter;
1649 static DATA_BLOB challenge;
1650 static DATA_BLOB lm_response;
1651 static DATA_BLOB nt_response;
1652 static char *full_username;
1653 static char *username;
1654 static char *domain;
1655 static char *plaintext_password;
1656 static BOOL ntlm_server_1_user_session_key;
1657 static BOOL ntlm_server_1_lm_session_key;
1658
1659 if (strequal(buf, ".")) {
1660 if (!full_username && !username) {
1661 x_fprintf(x_stdout, "Error: No username supplied!\n");
1662 } else if (plaintext_password) {
1663 /* handle this request as plaintext */
1664 if (!full_username) {
1665 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1666 x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1667 return;
1668 }
1669 }
1670 if (check_plaintext_auth(full_username, plaintext_password, False)) {
1671 x_fprintf(x_stdout, "Authenticated: Yes\n");
1672 } else {
1673 x_fprintf(x_stdout, "Authenticated: No\n");
1674 }
1675 } else if (!lm_response.data && !nt_response.data) {
1676 x_fprintf(x_stdout, "Error: No password supplied!\n");
1677 } else if (!challenge.data) {
1678 x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
1679 } else {
1680 char *error_string = NULL;
1681 uchar lm_key[8];
1682 uchar user_session_key[16];
1683 uint32 flags = 0;
1684
1685 if (full_username && !username) {
1686 fstring fstr_user;
1687 fstring fstr_domain;
1688
1689 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1690 /* username might be 'tainted', don't print into our new-line deleimianted stream */
1691 x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
1692 }
1693 SAFE_FREE(username);
1694 SAFE_FREE(domain);
1695 username = smb_xstrdup(fstr_user);
1696 domain = smb_xstrdup(fstr_domain);
1697 }
1698
1699 if (!domain) {
1700 domain = smb_xstrdup(get_winbind_domain());
1701 }
1702
1703 if (ntlm_server_1_lm_session_key)
1704 flags |= WBFLAG_PAM_LMKEY;
1705
1706 if (ntlm_server_1_user_session_key)
1707 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1708
1709 if (!NT_STATUS_IS_OK(
1710 contact_winbind_auth_crap(username,
1711 domain,
1712 global_myname(),
1713 &challenge,
1714 &lm_response,
1715 &nt_response,
1716 flags,
1717 lm_key,
1718 user_session_key,
1719 &error_string,
1720 NULL))) {
1721
1722 x_fprintf(x_stdout, "Authenticated: No\n");
1723 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
1724 SAFE_FREE(error_string);
1725 } else {
1726 static char zeros[16];
1727 char *hex_lm_key;
1728 char *hex_user_session_key;
1729
1730 x_fprintf(x_stdout, "Authenticated: Yes\n");
1731
1732 if (ntlm_server_1_lm_session_key
1733 && (memcmp(zeros, lm_key,
1734 sizeof(lm_key)) != 0)) {
1735 hex_lm_key = hex_encode(NULL,
1736 (const unsigned char *)lm_key,
1737 sizeof(lm_key));
1738 x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
1739 TALLOC_FREE(hex_lm_key);
1740 }
1741
1742 if (ntlm_server_1_user_session_key
1743 && (memcmp(zeros, user_session_key,
1744 sizeof(user_session_key)) != 0)) {
1745 hex_user_session_key = hex_encode(NULL,
1746 (const unsigned char *)user_session_key,
1747 sizeof(user_session_key));
1748 x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
1749 TALLOC_FREE(hex_user_session_key);
1750 }
1751 }
1752 }
1753 /* clear out the state */
1754 challenge = data_blob(NULL, 0);
1755 nt_response = data_blob(NULL, 0);
1756 lm_response = data_blob(NULL, 0);
1757 SAFE_FREE(full_username);
1758 SAFE_FREE(username);
1759 SAFE_FREE(domain);
1760 SAFE_FREE(plaintext_password);
1761 ntlm_server_1_user_session_key = False;
1762 ntlm_server_1_lm_session_key = False;
1763 x_fprintf(x_stdout, ".\n");
1764
1765 return;
1766 }
1767
1768 request = buf;
1769
1770 /* Indicates a base64 encoded structure */
1771 parameter = strstr_m(request, ":: ");
1772 if (!parameter) {
1773 parameter = strstr_m(request, ": ");
1774
1775 if (!parameter) {
1776 DEBUG(0, ("Parameter not found!\n"));
1777 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1778 return;
1779 }
1780
1781 parameter[0] ='\0';
1782 parameter++;
1783 parameter[0] ='\0';
1784 parameter++;
1785
1786 } else {
1787 parameter[0] ='\0';
1788 parameter++;
1789 parameter[0] ='\0';
1790 parameter++;
1791 parameter[0] ='\0';
1792 parameter++;
1793
1794 base64_decode_inplace(parameter);
1795 }
1796
1797 if (strequal(request, "LANMAN-Challenge")) {
1798 challenge = strhex_to_data_blob(NULL, parameter);
1799 if (challenge.length != 8) {
1800 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
1801 parameter,
1802 (int)challenge.length);
1803 challenge = data_blob(NULL, 0);
1804 }
1805 } else if (strequal(request, "NT-Response")) {
1806 nt_response = strhex_to_data_blob(NULL, parameter);
1807 if (nt_response.length < 24) {
1808 x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
1809 parameter,
1810 (int)nt_response.length);
1811 nt_response = data_blob(NULL, 0);
1812 }
1813 } else if (strequal(request, "LANMAN-Response")) {
1814 lm_response = strhex_to_data_blob(NULL, parameter);
1815 if (lm_response.length != 24) {
1816 x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
1817 parameter,
1818 (int)lm_response.length);
1819 lm_response = data_blob(NULL, 0);
1820 }
1821 } else if (strequal(request, "Password")) {
1822 plaintext_password = smb_xstrdup(parameter);
1823 } else if (strequal(request, "NT-Domain")) {
1824 domain = smb_xstrdup(parameter);
1825 } else if (strequal(request, "Username")) {
1826 username = smb_xstrdup(parameter);
1827 } else if (strequal(request, "Full-Username")) {
1828 full_username = smb_xstrdup(parameter);
1829 } else if (strequal(request, "Request-User-Session-Key")) {
1830 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1831 } else if (strequal(request, "Request-LanMan-Session-Key")) {
1832 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1833 } else {
1834 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
1835 }
1836}
1837
1838static void manage_ntlm_change_password_1_request(enum stdio_helper_mode helper_mode, char *buf, int length)
1839{
1840 char *request, *parameter;
1841 static DATA_BLOB new_nt_pswd;
1842 static DATA_BLOB old_nt_hash_enc;
1843 static DATA_BLOB new_lm_pswd;
1844 static DATA_BLOB old_lm_hash_enc;
1845 static char *full_username = NULL;
1846 static char *username = NULL;
1847 static char *domain = NULL;
1848 static char *newpswd = NULL;
1849 static char *oldpswd = NULL;
1850
1851 if (strequal(buf, ".")) {
1852 if(newpswd && oldpswd) {
1853 uchar old_nt_hash[16];
1854 uchar old_lm_hash[16];
1855 uchar new_nt_hash[16];
1856 uchar new_lm_hash[16];
1857
1858 new_nt_pswd = data_blob(NULL, 516);
1859 old_nt_hash_enc = data_blob(NULL, 16);
1860
1861 /* Calculate the MD4 hash (NT compatible) of the
1862 * password */
1863 E_md4hash(oldpswd, old_nt_hash);
1864 E_md4hash(newpswd, new_nt_hash);
1865
1866 /* E_deshash returns false for 'long'
1867 passwords (> 14 DOS chars).
1868
1869 Therefore, don't send a buffer
1870 encrypted with the truncated hash
1871 (it could allow an even easier
1872 attack on the password)
1873
1874 Likewise, obey the admin's restriction
1875 */
1876
1877 if (lp_client_lanman_auth() &&
1878 E_deshash(newpswd, new_lm_hash) &&
1879 E_deshash(oldpswd, old_lm_hash)) {
1880 new_lm_pswd = data_blob(NULL, 516);
1881 old_lm_hash_enc = data_blob(NULL, 16);
1882 encode_pw_buffer(new_lm_pswd.data, newpswd,
1883 STR_UNICODE);
1884
1885 SamOEMhash(new_lm_pswd.data, old_nt_hash, 516);
1886 E_old_pw_hash(new_nt_hash, old_lm_hash,
1887 old_lm_hash_enc.data);
1888 } else {
1889 new_lm_pswd.data = NULL;
1890 new_lm_pswd.length = 0;
1891 old_lm_hash_enc.data = NULL;
1892 old_lm_hash_enc.length = 0;
1893 }
1894
1895 encode_pw_buffer(new_nt_pswd.data, newpswd,
1896 STR_UNICODE);
1897
1898 SamOEMhash(new_nt_pswd.data, old_nt_hash, 516);
1899 E_old_pw_hash(new_nt_hash, old_nt_hash,
1900 old_nt_hash_enc.data);
1901 }
1902
1903 if (!full_username && !username) {
1904 x_fprintf(x_stdout, "Error: No username supplied!\n");
1905 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1906 (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1907 x_fprintf(x_stdout, "Error: No NT or LM password "
1908 "blobs supplied!\n");
1909 } else {
1910 char *error_string = NULL;
1911
1912 if (full_username && !username) {
1913 fstring fstr_user;
1914 fstring fstr_domain;
1915
1916 if (!parse_ntlm_auth_domain_user(full_username,
1917 fstr_user,
1918 fstr_domain)) {
1919 /* username might be 'tainted', don't
1920 * print into our new-line
1921 * deleimianted stream */
1922 x_fprintf(x_stdout, "Error: Could not "
1923 "parse into domain and "
1924 "username\n");
1925 SAFE_FREE(username);
1926 username = smb_xstrdup(full_username);
1927 } else {
1928 SAFE_FREE(username);
1929 SAFE_FREE(domain);
1930 username = smb_xstrdup(fstr_user);
1931 domain = smb_xstrdup(fstr_domain);
1932 }
1933
1934 }
1935
1936 if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1937 username, domain,
1938 new_nt_pswd,
1939 old_nt_hash_enc,
1940 new_lm_pswd,
1941 old_lm_hash_enc,
1942 &error_string))) {
1943 x_fprintf(x_stdout, "Password-Change: No\n");
1944 x_fprintf(x_stdout, "Password-Change-Error: "
1945 "%s\n.\n", error_string);
1946 } else {
1947 x_fprintf(x_stdout, "Password-Change: Yes\n");
1948 }
1949
1950 SAFE_FREE(error_string);
1951 }
1952 /* clear out the state */
1953 new_nt_pswd = data_blob(NULL, 0);
1954 old_nt_hash_enc = data_blob(NULL, 0);
1955 new_lm_pswd = data_blob(NULL, 0);
1956 old_nt_hash_enc = data_blob(NULL, 0);
1957 SAFE_FREE(full_username);
1958 SAFE_FREE(username);
1959 SAFE_FREE(domain);
1960 SAFE_FREE(newpswd);
1961 SAFE_FREE(oldpswd);
1962 x_fprintf(x_stdout, ".\n");
1963
1964 return;
1965 }
1966
1967 request = buf;
1968
1969 /* Indicates a base64 encoded structure */
1970 parameter = strstr_m(request, ":: ");
1971 if (!parameter) {
1972 parameter = strstr_m(request, ": ");
1973
1974 if (!parameter) {
1975 DEBUG(0, ("Parameter not found!\n"));
1976 x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1977 return;
1978 }
1979
1980 parameter[0] ='\0';
1981 parameter++;
1982 parameter[0] ='\0';
1983 parameter++;
1984 } else {
1985 parameter[0] ='\0';
1986 parameter++;
1987 parameter[0] ='\0';
1988 parameter++;
1989 parameter[0] ='\0';
1990 parameter++;
1991
1992 base64_decode_inplace(parameter);
1993 }
1994
1995 if (strequal(request, "new-nt-password-blob")) {
1996 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
1997 if (new_nt_pswd.length != 516) {
1998 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
1999 "(got %d bytes, expected 516)\n.\n",
2000 parameter,
2001 (int)new_nt_pswd.length);
2002 new_nt_pswd = data_blob(NULL, 0);
2003 }
2004 } else if (strequal(request, "old-nt-hash-blob")) {
2005 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2006 if (old_nt_hash_enc.length != 16) {
2007 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2008 "(got %d bytes, expected 16)\n.\n",
2009 parameter,
2010 (int)old_nt_hash_enc.length);
2011 old_nt_hash_enc = data_blob(NULL, 0);
2012 }
2013 } else if (strequal(request, "new-lm-password-blob")) {
2014 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2015 if (new_lm_pswd.length != 516) {
2016 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2017 "(got %d bytes, expected 516)\n.\n",
2018 parameter,
2019 (int)new_lm_pswd.length);
2020 new_lm_pswd = data_blob(NULL, 0);
2021 }
2022 }
2023 else if (strequal(request, "old-lm-hash-blob")) {
2024 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2025 if (old_lm_hash_enc.length != 16)
2026 {
2027 x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2028 "(got %d bytes, expected 16)\n.\n",
2029 parameter,
2030 (int)old_lm_hash_enc.length);
2031 old_lm_hash_enc = data_blob(NULL, 0);
2032 }
2033 } else if (strequal(request, "nt-domain")) {
2034 domain = smb_xstrdup(parameter);
2035 } else if(strequal(request, "username")) {
2036 username = smb_xstrdup(parameter);
2037 } else if(strequal(request, "full-username")) {
2038 username = smb_xstrdup(parameter);
2039 } else if(strequal(request, "new-password")) {
2040 newpswd = smb_xstrdup(parameter);
2041 } else if (strequal(request, "old-password")) {
2042 oldpswd = smb_xstrdup(parameter);
2043 } else {
2044 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2045 }
2046}
2047
2048static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn)
2049{
2050 char buf[SQUID_BUFFER_SIZE+1];
2051 int length;
2052 char *c;
2053 static BOOL err;
2054
2055 /* this is not a typo - x_fgets doesn't work too well under squid */
2056 if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
2057 if (ferror(stdin)) {
2058 DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
2059 strerror(ferror(stdin))));
2060
2061 exit(1); /* BIIG buffer */
2062 }
2063 exit(0);
2064 }
2065
2066 c=(char *)memchr(buf,'\n',sizeof(buf)-1);
2067 if (c) {
2068 *c = '\0';
2069 length = c-buf;
2070 } else {
2071 err = 1;
2072 return;
2073 }
2074 if (err) {
2075 DEBUG(2, ("Oversized message\n"));
2076 x_fprintf(x_stderr, "ERR\n");
2077 err = 0;
2078 return;
2079 }
2080
2081 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2082
2083 if (buf[0] == '\0') {
2084 DEBUG(2, ("Invalid Request\n"));
2085 x_fprintf(x_stderr, "ERR\n");
2086 return;
2087 }
2088
2089 fn(helper_mode, buf, length);
2090}
2091
2092
2093static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
2094 /* initialize FDescs */
2095 x_setbuf(x_stdout, NULL);
2096 x_setbuf(x_stderr, NULL);
2097 while(1) {
2098 manage_squid_request(stdio_mode, fn);
2099 }
2100}
2101
2102
2103/* Authenticate a user with a challenge/response */
2104
2105static BOOL check_auth_crap(void)
2106{
2107 NTSTATUS nt_status;
2108 uint32 flags = 0;
2109 char lm_key[8];
2110 char user_session_key[16];
2111 char *hex_lm_key;
2112 char *hex_user_session_key;
2113 char *error_string;
2114 static uint8 zeros[16];
2115
2116 x_setbuf(x_stdout, NULL);
2117
2118 if (request_lm_key)
2119 flags |= WBFLAG_PAM_LMKEY;
2120
2121 if (request_user_session_key)
2122 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2123
2124 flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2125
2126 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
2127 opt_workstation,
2128 &opt_challenge,
2129 &opt_lm_response,
2130 &opt_nt_response,
2131 flags,
2132 (unsigned char *)lm_key,
2133 (unsigned char *)user_session_key,
2134 &error_string, NULL);
2135
2136 if (!NT_STATUS_IS_OK(nt_status)) {
2137 x_fprintf(x_stdout, "%s (0x%x)\n",
2138 error_string,
2139 NT_STATUS_V(nt_status));
2140 SAFE_FREE(error_string);
2141 return False;
2142 }
2143
2144 if (request_lm_key
2145 && (memcmp(zeros, lm_key,
2146 sizeof(lm_key)) != 0)) {
2147 hex_lm_key = hex_encode(NULL, (const unsigned char *)lm_key,
2148 sizeof(lm_key));
2149 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2150 TALLOC_FREE(hex_lm_key);
2151 }
2152 if (request_user_session_key
2153 && (memcmp(zeros, user_session_key,
2154 sizeof(user_session_key)) != 0)) {
2155 hex_user_session_key = hex_encode(NULL, (const unsigned char *)user_session_key,
2156 sizeof(user_session_key));
2157 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2158 TALLOC_FREE(hex_user_session_key);
2159 }
2160
2161 return True;
2162}
2163
2164/* Main program */
2165
2166enum {
2167 OPT_USERNAME = 1000,
2168 OPT_DOMAIN,
2169 OPT_WORKSTATION,
2170 OPT_CHALLENGE,
2171 OPT_RESPONSE,
2172 OPT_LM,
2173 OPT_NT,
2174 OPT_PASSWORD,
2175 OPT_LM_KEY,
2176 OPT_USER_SESSION_KEY,
2177 OPT_DIAGNOSTICS,
2178 OPT_REQUIRE_MEMBERSHIP,
2179 OPT_USE_CACHED_CREDS
2180};
2181
2182 int main(int argc, const char **argv)
2183{
2184 int opt;
2185 static const char *helper_protocol;
2186 static int diagnostics;
2187
2188 static const char *hex_challenge;
2189 static const char *hex_lm_response;
2190 static const char *hex_nt_response;
2191
2192 poptContext pc;
2193
2194 /* NOTE: DO NOT change this interface without considering the implications!
2195 This is an external interface, which other programs will use to interact
2196 with this helper.
2197 */
2198
2199 /* We do not use single-letter command abbreviations, because they harm future
2200 interface stability. */
2201
2202 struct poptOption long_options[] = {
2203 POPT_AUTOHELP
2204 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2205 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2206 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2207 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2208 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2209 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2210 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2211 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
2212 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2213 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2214 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2215 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
2216 { "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" },
2217 POPT_COMMON_SAMBA
2218 POPT_TABLEEND
2219 };
2220
2221 /* Samba client initialisation */
2222 load_case_tables();
2223
2224 dbf = x_stderr;
2225
2226 /* Samba client initialisation */
2227
2228 if (!lp_load(dyn_CONFIGFILE, True, False, False, True)) {
2229 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2230 dyn_CONFIGFILE, strerror(errno));
2231 exit(1);
2232 }
2233
2234 /* Parse options */
2235
2236 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2237
2238 /* Parse command line options */
2239
2240 if (argc == 1) {
2241 poptPrintHelp(pc, stderr, 0);
2242 return 1;
2243 }
2244
2245 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
2246 POPT_CONTEXT_KEEP_FIRST);
2247
2248 while((opt = poptGetNextOpt(pc)) != -1) {
2249 switch (opt) {
2250 case OPT_CHALLENGE:
2251 opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2252 if (opt_challenge.length != 8) {
2253 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2254 hex_challenge,
2255 (int)opt_challenge.length);
2256 exit(1);
2257 }
2258 break;
2259 case OPT_LM:
2260 opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2261 if (opt_lm_response.length != 24) {
2262 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2263 hex_lm_response,
2264 (int)opt_lm_response.length);
2265 exit(1);
2266 }
2267 break;
2268
2269 case OPT_NT:
2270 opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2271 if (opt_nt_response.length < 24) {
2272 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n",
2273 hex_nt_response,
2274 (int)opt_nt_response.length);
2275 exit(1);
2276 }
2277 break;
2278
2279 case OPT_REQUIRE_MEMBERSHIP:
2280 if (StrnCaseCmp("S-", require_membership_of, 2) == 0) {
2281 require_membership_of_sid = require_membership_of;
2282 }
2283 break;
2284 }
2285 }
2286
2287 if (opt_username) {
2288 char *domain = SMB_STRDUP(opt_username);
2289 char *p = strchr_m(domain, *lp_winbind_separator());
2290 if (p) {
2291 opt_username = p+1;
2292 *p = '\0';
2293 if (opt_domain && !strequal(opt_domain, domain)) {
2294 x_fprintf(x_stderr, "Domain specified in username (%s) "
2295 "doesn't match specified domain (%s)!\n\n",
2296 domain, opt_domain);
2297 poptPrintHelp(pc, stderr, 0);
2298 exit(1);
2299 }
2300 opt_domain = domain;
2301 } else {
2302 SAFE_FREE(domain);
2303 }
2304 }
2305
2306 /* Note: if opt_domain is "" then send no domain */
2307 if (opt_domain == NULL) {
2308 opt_domain = get_winbind_domain();
2309 }
2310
2311 if (opt_workstation == NULL) {
2312 opt_workstation = "";
2313 }
2314
2315 if (helper_protocol) {
2316 int i;
2317 for (i=0; i<NUM_HELPER_MODES; i++) {
2318 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2319 squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
2320 exit(0);
2321 }
2322 }
2323 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2324
2325 for (i=0; i<NUM_HELPER_MODES; i++) {
2326 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2327 }
2328
2329 exit(1);
2330 }
2331
2332 if (!opt_username || !*opt_username) {
2333 x_fprintf(x_stderr, "username must be specified!\n\n");
2334 poptPrintHelp(pc, stderr, 0);
2335 exit(1);
2336 }
2337
2338 if (opt_challenge.length) {
2339 if (!check_auth_crap()) {
2340 exit(1);
2341 }
2342 exit(0);
2343 }
2344
2345 if (!opt_password) {
2346 opt_password = getpass("password: ");
2347 }
2348
2349 if (diagnostics) {
2350 if (!diagnose_ntlm_auth()) {
2351 return 1;
2352 }
2353 } else {
2354 fstring user;
2355
2356 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2357 if (!check_plaintext_auth(user, opt_password, True)) {
2358 return 1;
2359 }
2360 }
2361
2362 /* Exit code */
2363
2364 poptFreeContext(pc);
2365 return 0;
2366}
Note: See TracBrowser for help on using the repository browser.