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 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 3 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program. If not, see <http://www.gnu.org/licenses/>. 22*/ 23 24#include "includes.h" 25#include "system/filesys.h" 26#include "lib/cmdline/popt_common.h" 27#include "lib/ldb/include/ldb.h" 28#include "auth/credentials/credentials.h" 29#include "auth/gensec/gensec.h" 30#include "auth/auth.h" 31#include "librpc/gen_ndr/ndr_netlogon.h" 32#include "auth/auth_sam.h" 33#include "libcli/auth/libcli_auth.h" 34#include "libcli/security/security.h" 35#include "lib/events/events.h" 36#include "lib/messaging/messaging.h" 37#include "lib/messaging/irpc.h" 38#include "auth/ntlmssp/ntlmssp.h" 39#include "param/param.h" 40 41#define INITIAL_BUFFER_SIZE 300 42#define MAX_BUFFER_SIZE 63000 43 44enum stdio_helper_mode { 45 SQUID_2_4_BASIC, 46 SQUID_2_5_BASIC, 47 SQUID_2_5_NTLMSSP, 48 NTLMSSP_CLIENT_1, 49 GSS_SPNEGO_CLIENT, 50 GSS_SPNEGO_SERVER, 51 NTLM_SERVER_1, 52 NUM_HELPER_MODES 53}; 54 55#define NTLM_AUTH_FLAG_USER_SESSION_KEY 0x0004 56#define NTLM_AUTH_FLAG_LMKEY 0x0008 57 58 59typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode, 60 struct loadparm_context *lp_ctx, 61 char *buf, int length, void **private1, 62 unsigned int mux_id, void **private2); 63 64static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode, 65 struct loadparm_context *lp_ctx, 66 char *buf, int length, void **private1, 67 unsigned int mux_id, void **private2); 68 69static void manage_gensec_request (enum stdio_helper_mode stdio_helper_mode, 70 struct loadparm_context *lp_ctx, 71 char *buf, int length, void **private1, 72 unsigned int mux_id, void **private2); 73 74static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode, 75 struct loadparm_context *lp_ctx, 76 char *buf, int length, void **private1, 77 unsigned int mux_id, void **private2); 78 79static void manage_squid_request(struct loadparm_context *lp_ctx, 80 enum stdio_helper_mode helper_mode, 81 stdio_helper_function fn, void **private2); 82 83static const struct { 84 enum stdio_helper_mode mode; 85 const char *name; 86 stdio_helper_function fn; 87} stdio_helper_protocols[] = { 88 { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request}, 89 { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request}, 90 { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_gensec_request}, 91 { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gensec_request}, 92 { GSS_SPNEGO_SERVER, "gss-spnego", manage_gensec_request}, 93 { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_gensec_request}, 94 { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request}, 95 { NUM_HELPER_MODES, NULL, NULL} 96}; 97 98extern int winbindd_fd; 99 100static const char *opt_username; 101static const char *opt_domain; 102static const char *opt_workstation; 103static const char *opt_password; 104static int opt_multiplex; 105static int use_cached_creds; 106 107 108static void mux_printf(unsigned int mux_id, const char *format, ...) PRINTF_ATTRIBUTE(2, 3); 109 110static void mux_printf(unsigned int mux_id, const char *format, ...) 111{ 112 va_list ap; 113 114 if (opt_multiplex) { 115 x_fprintf(x_stdout, "%d ", mux_id); 116 } 117 118 va_start(ap, format); 119 x_vfprintf(x_stdout, format, ap); 120 va_end(ap); 121} 122 123 124 125/* Copy of parse_domain_user from winbindd_util.c. Parse a string of the 126 form DOMAIN/user into a domain and a user */ 127 128static bool parse_ntlm_auth_domain_user(const char *domuser, char **domain, 129 char **user, char winbind_separator) 130{ 131 132 char *p = strchr(domuser, winbind_separator); 133 134 if (!p) { 135 return false; 136 } 137 138 *user = smb_xstrdup(p+1); 139 *domain = smb_xstrdup(domuser); 140 (*domain)[PTR_DIFF(p, domuser)] = 0; 141 142 return true; 143} 144 145/** 146 * Decode a base64 string into a DATA_BLOB - simple and slow algorithm 147 **/ 148static DATA_BLOB base64_decode_data_blob(TALLOC_CTX *mem_ctx, const char *s) 149{ 150 DATA_BLOB ret = data_blob_talloc(mem_ctx, s, strlen(s)+1); 151 ret.length = ldb_base64_decode((char *)ret.data); 152 return ret; 153} 154 155/** 156 * Encode a base64 string into a talloc()ed string caller to free. 157 **/ 158static char *base64_encode_data_blob(TALLOC_CTX *mem_ctx, DATA_BLOB data) 159{ 160 return ldb_base64_encode(mem_ctx, (const char *)data.data, data.length); 161} 162 163/** 164 * Decode a base64 string in-place - wrapper for the above 165 **/ 166static void base64_decode_inplace(char *s) 167{ 168 ldb_base64_decode(s); 169} 170 171 172 173/* Authenticate a user with a plaintext password */ 174 175static bool check_plaintext_auth(const char *user, const char *pass, 176 bool stdout_diagnostics) 177{ 178 return (strcmp(pass, opt_password) == 0); 179} 180 181/* authenticate a user with an encrypted username/password */ 182 183static NTSTATUS local_pw_check_specified(struct loadparm_context *lp_ctx, 184 const char *username, 185 const char *domain, 186 const char *workstation, 187 const DATA_BLOB *challenge, 188 const DATA_BLOB *lm_response, 189 const DATA_BLOB *nt_response, 190 uint32_t flags, 191 DATA_BLOB *lm_session_key, 192 DATA_BLOB *user_session_key, 193 char **error_string, 194 char **unix_name) 195{ 196 NTSTATUS nt_status; 197 struct samr_Password lm_pw, nt_pw; 198 struct samr_Password *lm_pwd, *nt_pwd; 199 TALLOC_CTX *mem_ctx = talloc_init("local_pw_check_specified"); 200 if (!mem_ctx) { 201 nt_status = NT_STATUS_NO_MEMORY; 202 } else { 203 204 E_md4hash(opt_password, nt_pw.hash); 205 if (E_deshash(opt_password, lm_pw.hash)) { 206 lm_pwd = &lm_pw; 207 } else { 208 lm_pwd = NULL; 209 } 210 nt_pwd = &nt_pw; 211 212 213 nt_status = ntlm_password_check(mem_ctx, 214 lp_lanman_auth(lp_ctx), 215 lp_ntlm_auth(lp_ctx), 216 MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | 217 MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT, 218 challenge, 219 lm_response, 220 nt_response, 221 username, 222 username, 223 domain, 224 lm_pwd, nt_pwd, user_session_key, lm_session_key); 225 226 if (NT_STATUS_IS_OK(nt_status)) { 227 if (unix_name) { 228 asprintf(unix_name, 229 "%s%c%s", domain, 230 *lp_winbind_separator(lp_ctx), 231 username); 232 } 233 } else { 234 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", 235 domain, username, workstation, 236 nt_errstr(nt_status))); 237 } 238 talloc_free(mem_ctx); 239 } 240 if (error_string) { 241 *error_string = strdup(nt_errstr(nt_status)); 242 } 243 return nt_status; 244 245 246} 247 248static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode, 249 struct loadparm_context *lp_ctx, 250 char *buf, int length, void **private1, 251 unsigned int mux_id, void **private2) 252{ 253 char *user, *pass; 254 user=buf; 255 256 pass = memchr(buf, ' ', length); 257 if (!pass) { 258 DEBUG(2, ("Password not found. Denying access\n")); 259 mux_printf(mux_id, "ERR\n"); 260 return; 261 } 262 *pass='\0'; 263 pass++; 264 265 if (stdio_helper_mode == SQUID_2_5_BASIC) { 266 rfc1738_unescape(user); 267 rfc1738_unescape(pass); 268 } 269 270 if (check_plaintext_auth(user, pass, false)) { 271 mux_printf(mux_id, "OK\n"); 272 } else { 273 mux_printf(mux_id, "ERR\n"); 274 } 275} 276 277/* This is a bit hairy, but the basic idea is to do a password callback 278 to the calling application. The callback comes from within gensec */ 279 280static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode, 281 struct loadparm_context *lp_ctx, 282 char *buf, int length, void **private1, 283 unsigned int mux_id, void **password) 284{ 285 DATA_BLOB in; 286 if (strlen(buf) < 2) { 287 DEBUG(1, ("query [%s] invalid", buf)); 288 mux_printf(mux_id, "BH Query invalid\n"); 289 return; 290 } 291 292 if (strlen(buf) > 3) { 293 in = base64_decode_data_blob(NULL, buf + 3); 294 } else { 295 in = data_blob(NULL, 0); 296 } 297 298 if (strncmp(buf, "PW ", 3) == 0) { 299 300 *password = talloc_strndup(*private1 /* hopefully the right gensec context, useful to use for talloc */, 301 (const char *)in.data, in.length); 302 303 if (*password == NULL) { 304 DEBUG(1, ("Out of memory\n")); 305 mux_printf(mux_id, "BH Out of memory\n"); 306 data_blob_free(&in); 307 return; 308 } 309 310 mux_printf(mux_id, "OK\n"); 311 data_blob_free(&in); 312 return; 313 } 314 DEBUG(1, ("Asked for (and expected) a password\n")); 315 mux_printf(mux_id, "BH Expected a password\n"); 316 data_blob_free(&in); 317} 318 319/** 320 * Callback for password credentials. This is not async, and when 321 * GENSEC and the credentials code is made async, it will look rather 322 * different. 323 */ 324 325static const char *get_password(struct cli_credentials *credentials) 326{ 327 char *password = NULL; 328 329 /* Ask for a password */ 330 mux_printf((unsigned int)(uintptr_t)credentials->priv_data, "PW\n"); 331 credentials->priv_data = NULL; 332 333 manage_squid_request(cmdline_lp_ctx, NUM_HELPER_MODES /* bogus */, manage_gensec_get_pw_request, (void **)&password); 334 return password; 335} 336 337/** 338 Check if a string is part of a list. 339**/ 340static bool in_list(const char *s, const char *list, bool casesensitive) 341{ 342 char *tok; 343 size_t tok_len = 1024; 344 const char *p=list; 345 346 if (!list) 347 return false; 348 349 tok = (char *)malloc(tok_len); 350 if (!tok) { 351 return false; 352 } 353 354 while (next_token(&p, tok, LIST_SEP, tok_len)) { 355 if ((casesensitive?strcmp:strcasecmp_m)(tok,s) == 0) { 356 free(tok); 357 return true; 358 } 359 } 360 free(tok); 361 return false; 362} 363 364static void gensec_want_feature_list(struct gensec_security *state, char* feature_list) 365{ 366 if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) { 367 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n")); 368 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY); 369 } 370 if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) { 371 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n")); 372 gensec_want_feature(state, GENSEC_FEATURE_SIGN); 373 } 374 if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) { 375 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n")); 376 gensec_want_feature(state, GENSEC_FEATURE_SEAL); 377 } 378} 379 380static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode, 381 struct loadparm_context *lp_ctx, 382 char *buf, int length, void **private1, 383 unsigned int mux_id, void **private2) 384{ 385 DATA_BLOB in; 386 DATA_BLOB out = data_blob(NULL, 0); 387 char *out_base64 = NULL; 388 const char *reply_arg = NULL; 389 struct gensec_ntlm_state { 390 struct gensec_security *gensec_state; 391 const char *set_password; 392 }; 393 struct gensec_ntlm_state *state; 394 struct tevent_context *ev; 395 struct messaging_context *msg; 396 397 NTSTATUS nt_status; 398 bool first = false; 399 const char *reply_code; 400 struct cli_credentials *creds; 401 402 static char *want_feature_list = NULL; 403 static DATA_BLOB session_key; 404 405 TALLOC_CTX *mem_ctx; 406 407 if (*private1) { 408 state = (struct gensec_ntlm_state *)*private1; 409 } else { 410 state = talloc_zero(NULL, struct gensec_ntlm_state); 411 if (!state) { 412 mux_printf(mux_id, "BH No Memory\n"); 413 exit(1); 414 } 415 *private1 = state; 416 if (opt_password) { 417 state->set_password = opt_password; 418 } 419 } 420 421 if (strlen(buf) < 2) { 422 DEBUG(1, ("query [%s] invalid", buf)); 423 mux_printf(mux_id, "BH Query invalid\n"); 424 return; 425 } 426 427 if (strlen(buf) > 3) { 428 if(strncmp(buf, "SF ", 3) == 0) { 429 DEBUG(10, ("Setting flags to negotiate\n")); 430 talloc_free(want_feature_list); 431 want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3); 432 mux_printf(mux_id, "OK\n"); 433 return; 434 } 435 in = base64_decode_data_blob(NULL, buf + 3); 436 } else { 437 in = data_blob(NULL, 0); 438 } 439 440 if (strncmp(buf, "YR", 2) == 0) { 441 if (state->gensec_state) { 442 talloc_free(state->gensec_state); 443 state->gensec_state = NULL; 444 } 445 } else if ( (strncmp(buf, "OK", 2) == 0)) { 446 /* Just return BH, like ntlm_auth from Samba 3 does. */ 447 mux_printf(mux_id, "BH Command expected\n"); 448 data_blob_free(&in); 449 return; 450 } else if ( (strncmp(buf, "TT ", 3) != 0) && 451 (strncmp(buf, "KK ", 3) != 0) && 452 (strncmp(buf, "AF ", 3) != 0) && 453 (strncmp(buf, "NA ", 3) != 0) && 454 (strncmp(buf, "UG", 2) != 0) && 455 (strncmp(buf, "PW ", 3) != 0) && 456 (strncmp(buf, "GK", 2) != 0) && 457 (strncmp(buf, "GF", 2) != 0)) { 458 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf)); 459 mux_printf(mux_id, "BH SPNEGO request invalid\n"); 460 data_blob_free(&in); 461 return; 462 } 463 464 ev = s4_event_context_init(state); 465 if (!ev) { 466 exit(1); 467 } 468 469 mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx"); 470 471 /* setup gensec */ 472 if (!(state->gensec_state)) { 473 switch (stdio_helper_mode) { 474 case GSS_SPNEGO_CLIENT: 475 case NTLMSSP_CLIENT_1: 476 /* setup the client side */ 477 478 nt_status = gensec_client_start(NULL, &state->gensec_state, ev, 479 lp_gensec_settings(NULL, lp_ctx)); 480 if (!NT_STATUS_IS_OK(nt_status)) { 481 talloc_free(mem_ctx); 482 exit(1); 483 } 484 485 break; 486 case GSS_SPNEGO_SERVER: 487 case SQUID_2_5_NTLMSSP: 488 { 489 const char *winbind_method[] = { "winbind", NULL }; 490 struct auth_context *auth_context; 491 492 msg = messaging_client_init(state, lp_messaging_path(state, lp_ctx), 493 lp_iconv_convenience(lp_ctx), ev); 494 if (!msg) { 495 talloc_free(mem_ctx); 496 exit(1); 497 } 498 nt_status = auth_context_create_methods(mem_ctx, 499 winbind_method, 500 ev, 501 msg, 502 lp_ctx, 503 &auth_context); 504 505 if (!NT_STATUS_IS_OK(nt_status)) { 506 talloc_free(mem_ctx); 507 exit(1); 508 } 509 510 if (!NT_STATUS_IS_OK(gensec_server_start(state, ev, 511 lp_gensec_settings(state, lp_ctx), 512 auth_context, &state->gensec_state))) { 513 talloc_free(mem_ctx); 514 exit(1); 515 } 516 break; 517 } 518 default: 519 talloc_free(mem_ctx); 520 abort(); 521 } 522 523 creds = cli_credentials_init(state->gensec_state); 524 cli_credentials_set_conf(creds, lp_ctx); 525 if (opt_username) { 526 cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED); 527 } 528 if (opt_domain) { 529 cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED); 530 } 531 if (state->set_password) { 532 cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED); 533 } else { 534 cli_credentials_set_password_callback(creds, get_password); 535 creds->priv_data = (void*)(uintptr_t)mux_id; 536 } 537 if (opt_workstation) { 538 cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED); 539 } 540 541 switch (stdio_helper_mode) { 542 case GSS_SPNEGO_SERVER: 543 case SQUID_2_5_NTLMSSP: 544 cli_credentials_set_machine_account(creds, lp_ctx); 545 break; 546 default: 547 break; 548 } 549 550 gensec_set_credentials(state->gensec_state, creds); 551 gensec_want_feature_list(state->gensec_state, want_feature_list); 552 553 switch (stdio_helper_mode) { 554 case GSS_SPNEGO_CLIENT: 555 case GSS_SPNEGO_SERVER: 556 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO); 557 if (!in.length) { 558 first = true; 559 } 560 break; 561 case NTLMSSP_CLIENT_1: 562 if (!in.length) { 563 first = true; 564 } 565 /* fall through */ 566 case SQUID_2_5_NTLMSSP: 567 nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP); 568 break; 569 default: 570 talloc_free(mem_ctx); 571 abort(); 572 } 573 574 if (!NT_STATUS_IS_OK(nt_status)) { 575 DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status))); 576 mux_printf(mux_id, "BH GENSEC mech failed to start\n"); 577 talloc_free(mem_ctx); 578 return; 579 } 580 581 } 582 583 /* update */ 584 585 if (strncmp(buf, "PW ", 3) == 0) { 586 state->set_password = talloc_strndup(state, 587 (const char *)in.data, 588 in.length); 589 590 cli_credentials_set_password(gensec_get_credentials(state->gensec_state), 591 state->set_password, 592 CRED_SPECIFIED); 593 mux_printf(mux_id, "OK\n"); 594 data_blob_free(&in); 595 talloc_free(mem_ctx); 596 return; 597 } 598 599 if (strncmp(buf, "UG", 2) == 0) { 600 int i; 601 char *grouplist = NULL; 602 struct auth_session_info *session_info; 603 604 nt_status = gensec_session_info(state->gensec_state, &session_info); 605 if (!NT_STATUS_IS_OK(nt_status)) { 606 DEBUG(1, ("gensec_session_info failed: %s\n", nt_errstr(nt_status))); 607 mux_printf(mux_id, "BH %s\n", nt_errstr(nt_status)); 608 data_blob_free(&in); 609 talloc_free(mem_ctx); 610 return; 611 } 612 613 /* get the string onto the context */ 614 grouplist = talloc_strdup(mem_ctx, ""); 615 616 for (i=0; i<session_info->security_token->num_sids; i++) { 617 struct security_token *token = session_info->security_token; 618 const char *sidstr = dom_sid_string(session_info, 619 token->sids[i]); 620 grouplist = talloc_asprintf_append_buffer(grouplist, "%s,", sidstr); 621 } 622 623 mux_printf(mux_id, "GL %s\n", grouplist); 624 talloc_free(session_info); 625 data_blob_free(&in); 626 talloc_free(mem_ctx); 627 return; 628 } 629 630 if (strncmp(buf, "GK", 2) == 0) { 631 char *base64_key; 632 DEBUG(10, ("Requested session key\n")); 633 nt_status = gensec_session_key(state->gensec_state, &session_key); 634 if(!NT_STATUS_IS_OK(nt_status)) { 635 DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status))); 636 mux_printf(mux_id, "BH No session key\n"); 637 talloc_free(mem_ctx); 638 return; 639 } else { 640 base64_key = base64_encode_data_blob(state, session_key); 641 mux_printf(mux_id, "GK %s\n", base64_key); 642 talloc_free(base64_key); 643 } 644 talloc_free(mem_ctx); 645 return; 646 } 647 648 if (strncmp(buf, "GF", 2) == 0) { 649 struct gensec_ntlmssp_state *gensec_ntlmssp_state; 650 uint32_t neg_flags; 651 652 gensec_ntlmssp_state = talloc_get_type(state->gensec_state->private_data, 653 struct gensec_ntlmssp_state); 654 neg_flags = gensec_ntlmssp_state->neg_flags; 655 656 DEBUG(10, ("Requested negotiated feature flags\n")); 657 mux_printf(mux_id, "GF 0x%08x\n", neg_flags); 658 return; 659 } 660 661 nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out); 662 663 /* don't leak 'bad password'/'no such user' info to the network client */ 664 nt_status = auth_nt_status_squash(nt_status); 665 666 if (out.length) { 667 out_base64 = base64_encode_data_blob(mem_ctx, out); 668 } else { 669 out_base64 = NULL; 670 } 671 672 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { 673 reply_arg = "*"; 674 if (first) { 675 reply_code = "YR"; 676 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) { 677 reply_code = "KK"; 678 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) { 679 reply_code = "TT"; 680 } else { 681 abort(); 682 } 683 684 685 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) { 686 reply_code = "BH NT_STATUS_ACCESS_DENIED"; 687 reply_arg = nt_errstr(nt_status); 688 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); 689 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) { 690 reply_code = "BH NT_STATUS_UNSUCCESSFUL"; 691 reply_arg = nt_errstr(nt_status); 692 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); 693 } else if (!NT_STATUS_IS_OK(nt_status)) { 694 reply_code = "NA"; 695 reply_arg = nt_errstr(nt_status); 696 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); 697 } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) { 698 struct auth_session_info *session_info; 699 700 nt_status = gensec_session_info(state->gensec_state, &session_info); 701 if (!NT_STATUS_IS_OK(nt_status)) { 702 reply_code = "BH Failed to retrive session info"; 703 reply_arg = nt_errstr(nt_status); 704 DEBUG(1, ("GENSEC failed to retreive the session info: %s\n", nt_errstr(nt_status))); 705 } else { 706 707 reply_code = "AF"; 708 reply_arg = talloc_asprintf(state->gensec_state, 709 "%s%s%s", session_info->server_info->domain_name, 710 lp_winbind_separator(lp_ctx), session_info->server_info->account_name); 711 talloc_free(session_info); 712 } 713 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) { 714 reply_code = "AF"; 715 reply_arg = out_base64; 716 } else { 717 abort(); 718 } 719 720 switch (stdio_helper_mode) { 721 case GSS_SPNEGO_SERVER: 722 mux_printf(mux_id, "%s %s %s\n", reply_code, 723 out_base64 ? out_base64 : "*", 724 reply_arg ? reply_arg : "*"); 725 break; 726 default: 727 if (out_base64) { 728 mux_printf(mux_id, "%s %s\n", reply_code, out_base64); 729 } else if (reply_arg) { 730 mux_printf(mux_id, "%s %s\n", reply_code, reply_arg); 731 } else { 732 mux_printf(mux_id, "%s\n", reply_code); 733 } 734 } 735 736 talloc_free(mem_ctx); 737 return; 738} 739 740static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode, 741 struct loadparm_context *lp_ctx, 742 char *buf, int length, void **private1, 743 unsigned int mux_id, void **private2) 744{ 745 char *request, *parameter; 746 static DATA_BLOB challenge; 747 static DATA_BLOB lm_response; 748 static DATA_BLOB nt_response; 749 static char *full_username; 750 static char *username; 751 static char *domain; 752 static char *plaintext_password; 753 static bool ntlm_server_1_user_session_key; 754 static bool ntlm_server_1_lm_session_key; 755 756 if (strequal(buf, ".")) { 757 if (!full_username && !username) { 758 mux_printf(mux_id, "Error: No username supplied!\n"); 759 } else if (plaintext_password) { 760 /* handle this request as plaintext */ 761 if (!full_username) { 762 if (asprintf(&full_username, "%s%c%s", domain, *lp_winbind_separator(lp_ctx), username) == -1) { 763 mux_printf(mux_id, "Error: Out of memory in asprintf!\n.\n"); 764 return; 765 } 766 } 767 if (check_plaintext_auth(full_username, plaintext_password, false)) { 768 mux_printf(mux_id, "Authenticated: Yes\n"); 769 } else { 770 mux_printf(mux_id, "Authenticated: No\n"); 771 } 772 } else if (!lm_response.data && !nt_response.data) { 773 mux_printf(mux_id, "Error: No password supplied!\n"); 774 } else if (!challenge.data) { 775 mux_printf(mux_id, "Error: No lanman-challenge supplied!\n"); 776 } else { 777 char *error_string = NULL; 778 DATA_BLOB lm_key; 779 DATA_BLOB user_session_key; 780 uint32_t flags = 0; 781 782 if (full_username && !username) { 783 SAFE_FREE(username); 784 SAFE_FREE(domain); 785 if (!parse_ntlm_auth_domain_user(full_username, &username, 786 &domain, 787 *lp_winbind_separator(lp_ctx))) { 788 /* username might be 'tainted', don't print into our new-line deleimianted stream */ 789 mux_printf(mux_id, "Error: Could not parse into domain and username\n"); 790 } 791 } 792 793 if (!domain) { 794 domain = smb_xstrdup(lp_workgroup(lp_ctx)); 795 } 796 797 if (ntlm_server_1_lm_session_key) 798 flags |= NTLM_AUTH_FLAG_LMKEY; 799 800 if (ntlm_server_1_user_session_key) 801 flags |= NTLM_AUTH_FLAG_USER_SESSION_KEY; 802 803 if (!NT_STATUS_IS_OK( 804 local_pw_check_specified(lp_ctx, 805 username, 806 domain, 807 lp_netbios_name(lp_ctx), 808 &challenge, 809 &lm_response, 810 &nt_response, 811 flags, 812 &lm_key, 813 &user_session_key, 814 &error_string, 815 NULL))) { 816 817 mux_printf(mux_id, "Authenticated: No\n"); 818 mux_printf(mux_id, "Authentication-Error: %s\n.\n", error_string); 819 SAFE_FREE(error_string); 820 } else { 821 static char zeros[16]; 822 char *hex_lm_key; 823 char *hex_user_session_key; 824 825 mux_printf(mux_id, "Authenticated: Yes\n"); 826 827 if (ntlm_server_1_lm_session_key 828 && lm_key.length 829 && (memcmp(zeros, lm_key.data, 830 lm_key.length) != 0)) { 831 hex_encode(lm_key.data, 832 lm_key.length, 833 &hex_lm_key); 834 mux_printf(mux_id, "LANMAN-Session-Key: %s\n", hex_lm_key); 835 SAFE_FREE(hex_lm_key); 836 } 837 838 if (ntlm_server_1_user_session_key 839 && user_session_key.length 840 && (memcmp(zeros, user_session_key.data, 841 user_session_key.length) != 0)) { 842 hex_encode(user_session_key.data, 843 user_session_key.length, 844 &hex_user_session_key); 845 mux_printf(mux_id, "User-Session-Key: %s\n", hex_user_session_key); 846 SAFE_FREE(hex_user_session_key); 847 } 848 } 849 } 850 /* clear out the state */ 851 challenge = data_blob(NULL, 0); 852 nt_response = data_blob(NULL, 0); 853 lm_response = data_blob(NULL, 0); 854 SAFE_FREE(full_username); 855 SAFE_FREE(username); 856 SAFE_FREE(domain); 857 SAFE_FREE(plaintext_password); 858 ntlm_server_1_user_session_key = false; 859 ntlm_server_1_lm_session_key = false; 860 mux_printf(mux_id, ".\n"); 861 862 return; 863 } 864 865 request = buf; 866 867 /* Indicates a base64 encoded structure */ 868 parameter = strstr(request, ":: "); 869 if (!parameter) { 870 parameter = strstr(request, ": "); 871 872 if (!parameter) { 873 DEBUG(0, ("Parameter not found!\n")); 874 mux_printf(mux_id, "Error: Parameter not found!\n.\n"); 875 return; 876 } 877 878 parameter[0] ='\0'; 879 parameter++; 880 parameter[0] ='\0'; 881 parameter++; 882 883 } else { 884 parameter[0] ='\0'; 885 parameter++; 886 parameter[0] ='\0'; 887 parameter++; 888 parameter[0] ='\0'; 889 parameter++; 890 891 base64_decode_inplace(parameter); 892 } 893 894 if (strequal(request, "LANMAN-Challenge")) { 895 challenge = strhex_to_data_blob(NULL, parameter); 896 if (challenge.length != 8) { 897 mux_printf(mux_id, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n", 898 parameter, 899 (int)challenge.length); 900 challenge = data_blob(NULL, 0); 901 } 902 } else if (strequal(request, "NT-Response")) { 903 nt_response = strhex_to_data_blob(NULL, parameter); 904 if (nt_response.length < 24) { 905 mux_printf(mux_id, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n", 906 parameter, 907 (int)nt_response.length); 908 nt_response = data_blob(NULL, 0); 909 } 910 } else if (strequal(request, "LANMAN-Response")) { 911 lm_response = strhex_to_data_blob(NULL, parameter); 912 if (lm_response.length != 24) { 913 mux_printf(mux_id, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n", 914 parameter, 915 (int)lm_response.length); 916 lm_response = data_blob(NULL, 0); 917 } 918 } else if (strequal(request, "Password")) { 919 plaintext_password = smb_xstrdup(parameter); 920 } else if (strequal(request, "NT-Domain")) { 921 domain = smb_xstrdup(parameter); 922 } else if (strequal(request, "Username")) { 923 username = smb_xstrdup(parameter); 924 } else if (strequal(request, "Full-Username")) { 925 full_username = smb_xstrdup(parameter); 926 } else if (strequal(request, "Request-User-Session-Key")) { 927 ntlm_server_1_user_session_key = strequal(parameter, "Yes"); 928 } else if (strequal(request, "Request-LanMan-Session-Key")) { 929 ntlm_server_1_lm_session_key = strequal(parameter, "Yes"); 930 } else { 931 mux_printf(mux_id, "Error: Unknown request %s\n.\n", request); 932 } 933} 934 935static void manage_squid_request(struct loadparm_context *lp_ctx, enum stdio_helper_mode helper_mode, 936 stdio_helper_function fn, void **private2) 937{ 938 char *buf; 939 char tmp[INITIAL_BUFFER_SIZE+1]; 940 unsigned int mux_id = 0; 941 int length, buf_size = 0; 942 char *c; 943 struct mux_private { 944 unsigned int max_mux; 945 void **private_pointers; 946 }; 947 948 static struct mux_private *mux_private; 949 static void *normal_private; 950 void **private1; 951 952 buf = talloc_strdup(NULL, ""); 953 954 if (buf == NULL) { 955 DEBUG(0, ("Failed to allocate memory for reading the input " 956 "buffer.\n")); 957 x_fprintf(x_stdout, "ERR\n"); 958 return; 959 } 960 961 do { 962 /* this is not a typo - x_fgets doesn't work too well under 963 * squid */ 964 if (fgets(tmp, INITIAL_BUFFER_SIZE, stdin) == NULL) { 965 if (ferror(stdin)) { 966 DEBUG(1, ("fgets() failed! dying..... errno=%d " 967 "(%s)\n", ferror(stdin), 968 strerror(ferror(stdin)))); 969 970 exit(1); /* BIIG buffer */ 971 } 972 exit(0); 973 } 974 975 buf = talloc_strdup_append_buffer(buf, tmp); 976 buf_size += INITIAL_BUFFER_SIZE; 977 978 if (buf_size > MAX_BUFFER_SIZE) { 979 DEBUG(0, ("Invalid Request (too large)\n")); 980 x_fprintf(x_stdout, "ERR\n"); 981 talloc_free(buf); 982 return; 983 } 984 985 c = strchr(buf, '\n'); 986 } while (c == NULL); 987 988 *c = '\0'; 989 length = c-buf; 990 991 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length)); 992 993 if (buf[0] == '\0') { 994 DEBUG(0, ("Invalid Request (empty)\n")); 995 x_fprintf(x_stdout, "ERR\n"); 996 talloc_free(buf); 997 return; 998 } 999 1000 if (opt_multiplex) { 1001 if (sscanf(buf, "%u ", &mux_id) != 1) { 1002 DEBUG(0, ("Invalid Request - no multiplex id\n")); 1003 x_fprintf(x_stdout, "ERR\n"); 1004 talloc_free(buf); 1005 return; 1006 } 1007 if (!mux_private) { 1008 mux_private = talloc(NULL, struct mux_private); 1009 mux_private->max_mux = 0; 1010 mux_private->private_pointers = NULL; 1011 } 1012 1013 c=strchr(buf,' '); 1014 if (!c) { 1015 DEBUG(0, ("Invalid Request - no data after multiplex id\n")); 1016 x_fprintf(x_stdout, "ERR\n"); 1017 talloc_free(buf); 1018 return; 1019 } 1020 c++; 1021 if (mux_id >= mux_private->max_mux) { 1022 unsigned int prev_max = mux_private->max_mux; 1023 mux_private->max_mux = mux_id + 1; 1024 mux_private->private_pointers 1025 = talloc_realloc(mux_private, 1026 mux_private->private_pointers, 1027 void *, mux_private->max_mux); 1028 memset(&mux_private->private_pointers[prev_max], '\0', 1029 (sizeof(*mux_private->private_pointers) * (mux_private->max_mux - prev_max))); 1030 }; 1031 1032 private1 = &mux_private->private_pointers[mux_id]; 1033 } else { 1034 c = buf; 1035 private1 = &normal_private; 1036 } 1037 1038 fn(helper_mode, lp_ctx, c, length, private1, mux_id, private2); 1039 talloc_free(buf); 1040} 1041 1042static void squid_stream(struct loadparm_context *lp_ctx, 1043 enum stdio_helper_mode stdio_mode, 1044 stdio_helper_function fn) { 1045 /* initialize FDescs */ 1046 x_setbuf(x_stdout, NULL); 1047 x_setbuf(x_stderr, NULL); 1048 while(1) { 1049 manage_squid_request(lp_ctx, stdio_mode, fn, NULL); 1050 } 1051} 1052 1053 1054/* Main program */ 1055 1056enum { 1057 OPT_USERNAME = 1000, 1058 OPT_DOMAIN, 1059 OPT_WORKSTATION, 1060 OPT_CHALLENGE, 1061 OPT_RESPONSE, 1062 OPT_LM, 1063 OPT_NT, 1064 OPT_PASSWORD, 1065 OPT_LM_KEY, 1066 OPT_USER_SESSION_KEY, 1067 OPT_DIAGNOSTICS, 1068 OPT_REQUIRE_MEMBERSHIP, 1069 OPT_MULTIPLEX, 1070 OPT_USE_CACHED_CREDS, 1071}; 1072 1073int main(int argc, const char **argv) 1074{ 1075 static const char *helper_protocol; 1076 int opt; 1077 1078 poptContext pc; 1079 1080 /* NOTE: DO NOT change this interface without considering the implications! 1081 This is an external interface, which other programs will use to interact 1082 with this helper. 1083 */ 1084 1085 /* We do not use single-letter command abbreviations, because they harm future 1086 interface stability. */ 1087 1088 struct poptOption long_options[] = { 1089 POPT_AUTOHELP 1090 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"}, 1091 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"}, 1092 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"}, 1093 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_PASSWORD, "Username"}, 1094 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"}, 1095 { "multiplex", 0, POPT_ARG_NONE, &opt_multiplex, OPT_MULTIPLEX, "Multiplex Mode"}, 1096 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "silently ignored for compatibility reasons"}, 1097 POPT_COMMON_SAMBA 1098 POPT_COMMON_VERSION 1099 { NULL } 1100 }; 1101 1102 /* Samba client initialisation */ 1103 1104 setup_logging(NULL, DEBUG_STDERR); 1105 1106 /* Parse options */ 1107 1108 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0); 1109 1110 /* Parse command line options */ 1111 1112 if (argc == 1) { 1113 poptPrintHelp(pc, stderr, 0); 1114 return 1; 1115 } 1116 1117 pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 1118 POPT_CONTEXT_KEEP_FIRST); 1119 1120 while((opt = poptGetNextOpt(pc)) != -1) { 1121 if (opt < -1) { 1122 break; 1123 } 1124 } 1125 if (opt < -1) { 1126 fprintf(stderr, "%s: %s\n", 1127 poptBadOption(pc, POPT_BADOPTION_NOALIAS), 1128 poptStrerror(opt)); 1129 return 1; 1130 } 1131 1132 gensec_init(cmdline_lp_ctx); 1133 1134 if (opt_domain == NULL) { 1135 opt_domain = lp_workgroup(cmdline_lp_ctx); 1136 } 1137 1138 if (helper_protocol) { 1139 int i; 1140 for (i=0; i<NUM_HELPER_MODES; i++) { 1141 if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) { 1142 squid_stream(cmdline_lp_ctx, stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn); 1143 exit(0); 1144 } 1145 } 1146 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol); 1147 1148 for (i=0; i<NUM_HELPER_MODES; i++) { 1149 x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name); 1150 } 1151 1152 exit(1); 1153 } 1154 1155 if (!opt_username) { 1156 x_fprintf(x_stderr, "username must be specified!\n\n"); 1157 poptPrintHelp(pc, stderr, 0); 1158 exit(1); 1159 } 1160 1161 if (opt_workstation == NULL) { 1162 opt_workstation = lp_netbios_name(cmdline_lp_ctx); 1163 } 1164 1165 if (!opt_password) { 1166 opt_password = getpass("password: "); 1167 } 1168 1169 { 1170 char *user; 1171 1172 asprintf(&user, "%s%c%s", opt_domain, *lp_winbind_separator(cmdline_lp_ctx), opt_username); 1173 if (!check_plaintext_auth(user, opt_password, true)) { 1174 return 1; 1175 } 1176 } 1177 1178 /* Exit code */ 1179 1180 poptFreeContext(pc); 1181 return 0; 1182} 1183