1 /*********************************************************************** 2 * 3 * winbind.c 4 * 5 * WINBIND plugin for pppd. Performs PAP, CHAP, MS-CHAP, MS-CHAPv2 6 * authentication using WINBIND to contact a NT-style PDC. 7 * 8 * Based on the structure of the radius module. 9 * 10 * Copyright (C) 2003 Andrew Bartlet <abartlet (at) samba.org> 11 * 12 * Copyright 1999 Paul Mackerras, Alan Curry. 13 * (pipe read code from passpromt.c) 14 * 15 * Copyright (C) 2002 Roaring Penguin Software Inc. 16 * 17 * Based on a patch for ipppd, which is: 18 * Copyright (C) 1996, Matjaz Godec <gody (at) elgo.si> 19 * Copyright (C) 1996, Lars Fenneberg <in5y050 (at) public.uni-hamburg.de> 20 * Copyright (C) 1997, Miguel A.L. Paraz <map (at) iphil.net> 21 * 22 * Uses radiusclient library, which is: 23 * Copyright (C) 1995,1996,1997,1998 Lars Fenneberg <lf (at) elemental.net> 24 * Copyright (C) 2002 Roaring Penguin Software Inc. 25 * 26 * MPPE support is by Ralf Hofmann, <ralf.hofmann (at) elvido.net>, with 27 * modification from Frank Cusack, <frank (at) google.com>. 28 * 29 * Updated on 2003-12-12 to support updated PPP plugin API from latest CVS 30 * Copyright (C) 2003, Sean E. Millichamp <sean at bruenor dot org> 31 * 32 * This plugin may be distributed according to the terms of the GNU 33 * General Public License, version 2 or (at your option) any later version. 34 * 35 ***********************************************************************/ 36 37 #include "pppd.h" 38 #include "chap-new.h" 39 #include "chap_ms.h" 40 #ifdef MPPE 41 #include "md5.h" 42 #endif 43 #include "fsm.h" 44 #include "ipcp.h" 45 #include <syslog.h> 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 #include <fcntl.h> 49 #include <sys/time.h> 50 #include <sys/wait.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <stdlib.h> 54 #include <errno.h> 55 #include <ctype.h> 56 57 #define BUF_LEN 1024 58 59 #define NOT_AUTHENTICATED 0 60 #define AUTHENTICATED 1 61 62 static char *ntlm_auth = NULL; 63 64 static int set_ntlm_auth(char **argv) 65 { 66 char *p; 67 68 p = argv[0]; 69 if (p[0] != '/') { 70 option_error("ntlm_auth-helper argument must be full path"); 71 return 0; 72 } 73 p = strdup(p); 74 if (p == NULL) { 75 novm("ntlm_auth-helper argument"); 76 return 0; 77 } 78 if (ntlm_auth != NULL) 79 free(ntlm_auth); 80 ntlm_auth = p; 81 return 1; 82 } 83 84 static option_t Options[] = { 85 { "ntlm_auth-helper", o_special, (void *) &set_ntlm_auth, 86 "Path to ntlm_auth executable", OPT_PRIV }, 87 { NULL } 88 }; 89 90 static int 91 winbind_secret_check(void); 92 93 static int winbind_pap_auth(char *user, 94 char *passwd, 95 char **msgp, 96 struct wordlist **paddrs, 97 struct wordlist **popts); 98 static int winbind_chap_verify(char *user, char *ourname, int id, 99 struct chap_digest_type *digest, 100 unsigned char *challenge, 101 unsigned char *response, 102 char *message, int message_space); 103 static int winbind_allowed_address(u_int32_t addr); 104 105 char pppd_version[] = VERSION; 106 107 /********************************************************************** 108 * %FUNCTION: plugin_init 109 * %ARGUMENTS: 110 * None 111 * %RETURNS: 112 * Nothing 113 * %DESCRIPTION: 114 * Initializes WINBIND plugin. 115 ***********************************************************************/ 116 void 117 plugin_init(void) 118 { 119 pap_check_hook = winbind_secret_check; 120 pap_auth_hook = winbind_pap_auth; 121 122 chap_check_hook = winbind_secret_check; 123 chap_verify_hook = winbind_chap_verify; 124 125 allowed_address_hook = winbind_allowed_address; 126 127 /* Don't ask the peer for anything other than MS-CHAP or MS-CHAP V2 */ 128 chap_mdtype_all &= (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT); 129 130 add_options(Options); 131 132 info("WINBIND plugin initialized."); 133 } 134 135 /** 136 Routine to get hex characters and turn them into a 16 byte array. 137 the array can be variable length, and any non-hex-numeric 138 characters are skipped. "0xnn" or "0Xnn" is specially catered 139 for. 140 141 valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n" 142 143 **/ 144 145 /* 146 Unix SMB/CIFS implementation. 147 Samba utility functions 148 149 Copyright (C) Andrew Tridgell 1992-2001 150 Copyright (C) Simo Sorce 2001-2002 151 Copyright (C) Martin Pool 2003 152 153 This program is free software; you can redistribute it and/or modify 154 it under the terms of the GNU General Public License as published by 155 the Free Software Foundation; either version 2 of the License, or 156 (at your option) any later version. 157 158 This program is distributed in the hope that it will be useful, 159 but WITHOUT ANY WARRANTY; without even the implied warranty of 160 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 161 GNU General Public License for more details. 162 163 You should have received a copy of the GNU General Public License 164 along with this program; if not, write to the Free Software 165 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 166 */ 167 168 size_t strhex_to_str(char *p, size_t len, const char *strhex) 169 { 170 size_t i; 171 size_t num_chars = 0; 172 unsigned char lonybble, hinybble; 173 const char *hexchars = "0123456789ABCDEF"; 174 char *p1 = NULL, *p2 = NULL; 175 176 for (i = 0; i < len && strhex[i] != 0; i++) { 177 if (strncmp(hexchars, "0x", 2) == 0) { 178 i++; /* skip two chars */ 179 continue; 180 } 181 182 if (!(p1 = strchr(hexchars, toupper(strhex[i])))) 183 break; 184 185 i++; /* next hex digit */ 186 187 if (!(p2 = strchr(hexchars, toupper(strhex[i])))) 188 break; 189 190 /* get the two nybbles */ 191 hinybble = (p1 - hexchars); 192 lonybble = (p2 - hexchars); 193 194 p[num_chars] = (hinybble << 4) | lonybble; 195 num_chars++; 196 197 p1 = NULL; 198 p2 = NULL; 199 } 200 return num_chars; 201 } 202 203 static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 204 205 /** 206 * Encode a base64 string into a malloc()ed string caller to free. 207 * 208 *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments 209 **/ 210 char * base64_encode(const char *data) 211 { 212 int bits = 0; 213 int char_count = 0; 214 size_t out_cnt = 0; 215 size_t len = strlen(data); 216 size_t output_len = strlen(data) * 2; 217 char *result = malloc(output_len); /* get us plenty of space */ 218 219 while (len-- && out_cnt < (output_len) - 5) { 220 int c = (unsigned char) *(data++); 221 bits += c; 222 char_count++; 223 if (char_count == 3) { 224 result[out_cnt++] = b64[bits >> 18]; 225 result[out_cnt++] = b64[(bits >> 12) & 0x3f]; 226 result[out_cnt++] = b64[(bits >> 6) & 0x3f]; 227 result[out_cnt++] = b64[bits & 0x3f]; 228 bits = 0; 229 char_count = 0; 230 } else { 231 bits <<= 8; 232 } 233 } 234 if (char_count != 0) { 235 bits <<= 16 - (8 * char_count); 236 result[out_cnt++] = b64[bits >> 18]; 237 result[out_cnt++] = b64[(bits >> 12) & 0x3f]; 238 if (char_count == 1) { 239 result[out_cnt++] = '='; 240 result[out_cnt++] = '='; 241 } else { 242 result[out_cnt++] = b64[(bits >> 6) & 0x3f]; 243 result[out_cnt++] = '='; 244 } 245 } 246 result[out_cnt] = '\0'; /* terminate */ 247 return result; 248 } 249 250 unsigned int run_ntlm_auth(const char *username, 251 const char *domain, 252 const char *full_username, 253 const char *plaintext_password, 254 const u_char *challenge, 255 size_t challenge_length, 256 const u_char *lm_response, 257 size_t lm_response_length, 258 const u_char *nt_response, 259 size_t nt_response_length, 260 u_char nt_key[16], 261 char **error_string) 262 { 263 264 pid_t forkret; 265 int child_in[2]; 266 int child_out[2]; 267 int status; 268 269 int authenticated = NOT_AUTHENTICATED; /* not auth */ 270 int got_user_session_key = 0; /* not got key */ 271 272 char buffer[1024]; 273 274 FILE *pipe_in; 275 FILE *pipe_out; 276 277 int i; 278 char *challenge_hex; 279 char *lm_hex_hash; 280 char *nt_hex_hash; 281 282 /* First see if we have a program to run... */ 283 if (ntlm_auth == NULL) 284 return NOT_AUTHENTICATED; 285 286 /* Make first child */ 287 if (pipe(child_out) == -1) { 288 error("pipe creation failed for child OUT!"); 289 return NOT_AUTHENTICATED; 290 } 291 292 if (pipe(child_in) == -1) { 293 error("pipe creation failed for child IN!"); 294 return NOT_AUTHENTICATED; 295 } 296 297 forkret = safe_fork(child_in[0], child_out[1], 2); 298 if (forkret == -1) { 299 if (error_string) { 300 *error_string = strdup("fork failed!"); 301 } 302 303 return NOT_AUTHENTICATED; 304 } 305 306 if (forkret == 0) { 307 /* child process */ 308 close(child_out[0]); 309 close(child_in[1]); 310 311 /* run winbind as the user that invoked pppd */ 312 setgid(getgid()); 313 setuid(getuid()); 314 execl("/bin/sh", "sh", "-c", ntlm_auth, NULL); 315 perror("pppd/winbind: could not exec /bin/sh"); 316 exit(1); 317 } 318 319 /* parent */ 320 close(child_out[1]); 321 close(child_in[0]); 322 323 /* Need to write the User's info onto the pipe */ 324 325 pipe_in = fdopen(child_in[1], "w"); 326 327 pipe_out = fdopen(child_out[0], "r"); 328 329 /* look for session key coming back */ 330 331 if (username) { 332 char *b64_username = base64_encode(username); 333 fprintf(pipe_in, "Username:: %s\n", b64_username); 334 free(b64_username); 335 } 336 337 if (domain) { 338 char *b64_domain = base64_encode(domain); 339 fprintf(pipe_in, "NT-Domain:: %s\n", b64_domain); 340 free(b64_domain); 341 } 342 343 if (full_username) { 344 char *b64_full_username = base64_encode(full_username); 345 fprintf(pipe_in, "Full-Username:: %s\n", b64_full_username); 346 free(b64_full_username); 347 } 348 349 if (plaintext_password) { 350 char *b64_plaintext_password = base64_encode(plaintext_password); 351 fprintf(pipe_in, "Password:: %s\n", b64_plaintext_password); 352 free(b64_plaintext_password); 353 } 354 355 if (challenge_length) { 356 fprintf(pipe_in, "Request-User-Session-Key: yes\n"); 357 358 challenge_hex = malloc(challenge_length*2+1); 359 360 for (i = 0; i < challenge_length; i++) 361 sprintf(challenge_hex + i * 2, "%02X", challenge[i]); 362 363 fprintf(pipe_in, "LANMAN-Challenge: %s\n", challenge_hex); 364 free(challenge_hex); 365 } 366 367 if (lm_response_length) { 368 lm_hex_hash = malloc(lm_response_length*2+1); 369 370 for (i = 0; i < lm_response_length; i++) 371 sprintf(lm_hex_hash + i * 2, "%02X", lm_response[i]); 372 373 fprintf(pipe_in, "LANMAN-response: %s\n", lm_hex_hash); 374 free(lm_hex_hash); 375 } 376 377 if (nt_response_length) { 378 nt_hex_hash = malloc(nt_response_length*2+1); 379 380 for (i = 0; i < nt_response_length; i++) 381 sprintf(nt_hex_hash + i * 2, "%02X", nt_response[i]); 382 383 fprintf(pipe_in, "NT-response: %s\n", nt_hex_hash); 384 free(nt_hex_hash); 385 } 386 387 fprintf(pipe_in, ".\n"); 388 fflush(pipe_in); 389 390 while (fgets(buffer, sizeof(buffer)-1, pipe_out) != NULL) { 391 char *message, *parameter; 392 if (buffer[strlen(buffer)-1] != '\n') { 393 break; 394 } 395 buffer[strlen(buffer)-1] = '\0'; 396 message = buffer; 397 398 if (!(parameter = strstr(buffer, ": "))) { 399 break; 400 } 401 402 parameter[0] = '\0'; 403 parameter++; 404 parameter[0] = '\0'; 405 parameter++; 406 407 if (strcmp(message, ".") == 0) { 408 /* end of sequence */ 409 break; 410 } else if (strcasecmp(message, "Authenticated") == 0) { 411 if (strcasecmp(parameter, "Yes") == 0) { 412 authenticated = AUTHENTICATED; 413 } else { 414 notice("Winbind has declined authentication for user!"); 415 authenticated = NOT_AUTHENTICATED; 416 } 417 } else if (strcasecmp(message, "User-session-key") == 0) { 418 /* length is the number of characters to parse */ 419 if (nt_key) { 420 if (strhex_to_str(nt_key, 32, parameter) == 16) { 421 got_user_session_key = 1; 422 } else { 423 notice("NT session key for user was not 16 bytes!"); 424 } 425 } 426 } else if (strcasecmp(message, "Error") == 0) { 427 authenticated = NOT_AUTHENTICATED; 428 if (error_string) 429 *error_string = strdup(parameter); 430 } else if (strcasecmp(message, "Authentication-Error") == 0) { 431 authenticated = NOT_AUTHENTICATED; 432 if (error_string) 433 *error_string = strdup(parameter); 434 } else { 435 notice("unrecognised input from ntlm_auth helper - %s: %s", message, parameter); 436 } 437 } 438 439 /* parent */ 440 if (close(child_out[0]) == -1) { 441 notice("error closing pipe?!? for child OUT[0]"); 442 return NOT_AUTHENTICATED; 443 } 444 445 /* parent */ 446 if (close(child_in[1]) == -1) { 447 notice("error closing pipe?!? for child IN[1]"); 448 return NOT_AUTHENTICATED; 449 } 450 451 while ((wait(&status) == -1) && errno == EINTR) 452 ; 453 454 if ((authenticated == AUTHENTICATED) && nt_key && !got_user_session_key) { 455 notice("Did not get user session key, despite being authenticated!"); 456 return NOT_AUTHENTICATED; 457 } 458 return authenticated; 459 } 460 461 /********************************************************************** 462 * %FUNCTION: winbind_secret_check 463 * %ARGUMENTS: 464 * None 465 * %RETURNS: 466 * 0 if we don't have an ntlm_auth program to run, otherwise 1. 467 * %DESCRIPTION: 468 * Tells pppd that we will try to authenticate the peer, and not to 469 * worry about looking in /etc/ppp/ *-secrets 470 ***********************************************************************/ 471 static int 472 winbind_secret_check(void) 473 { 474 return ntlm_auth != NULL; 475 } 476 477 /********************************************************************** 478 * %FUNCTION: winbind_pap_auth 479 * %ARGUMENTS: 480 * user -- user-name of peer 481 * passwd -- password supplied by peer 482 * msgp -- Message which will be sent in PAP response 483 * paddrs -- set to a list of possible peer IP addresses 484 * popts -- set to a list of additional pppd options 485 * %RETURNS: 486 * 1 if we can authenticate, -1 if we cannot. 487 * %DESCRIPTION: 488 * Performs PAP authentication using WINBIND 489 ***********************************************************************/ 490 static int 491 winbind_pap_auth(char *user, 492 char *password, 493 char **msgp, 494 struct wordlist **paddrs, 495 struct wordlist **popts) 496 { 497 if (run_ntlm_auth(NULL, NULL, user, password, NULL, 0, NULL, 0, NULL, 0, NULL, msgp) == AUTHENTICATED) { 498 return 1; 499 } 500 return -1; 501 } 502 503 /********************************************************************** 504 * %FUNCTION: winbind_chap_auth 505 * %ARGUMENTS: 506 * user -- user-name of peer 507 * remmd -- hash received from peer 508 * remmd_len -- length of remmd 509 * cstate -- pppd's chap_state structure 510 * %RETURNS: 511 * AUTHENTICATED (1) if we can authenticate, NOT_AUTHENTICATED (0) if we cannot. 512 * %DESCRIPTION: 513 * Performs MS-CHAP and MS-CHAPv2 authentication using WINBIND. 514 ***********************************************************************/ 515 516 static int 517 winbind_chap_verify(char *user, char *ourname, int id, 518 struct chap_digest_type *digest, 519 unsigned char *challenge, 520 unsigned char *response, 521 char *message, int message_space) 522 { 523 int challenge_len, response_len; 524 char domainname[256]; 525 char *domain; 526 char *username; 527 char *p; 528 char saresponse[MS_AUTH_RESPONSE_LENGTH+1]; 529 530 /* The first byte of each of these strings contains their length */ 531 challenge_len = *challenge++; 532 response_len = *response++; 533 534 /* remove domain from "domain\username" */ 535 if ((username = strrchr(user, '\\')) != NULL) 536 ++username; 537 else 538 username = user; 539 540 strlcpy(domainname, user, sizeof(domainname)); 541 542 /* remove domain from "domain\username" */ 543 if ((p = strrchr(domainname, '\\')) != NULL) { 544 *p = '\0'; 545 domain = domainname; 546 } else { 547 domain = NULL; 548 } 549 550 /* generate MD based on negotiated type */ 551 switch (digest->code) { 552 553 case CHAP_MICROSOFT: 554 { 555 char *error_string = NULL; 556 u_char *nt_response = NULL; 557 u_char *lm_response = NULL; 558 int nt_response_size = 0; 559 int lm_response_size = 0; 560 MS_ChapResponse *rmd = (MS_ChapResponse *) response; 561 u_char session_key[16]; 562 563 if (response_len != MS_CHAP_RESPONSE_LEN) 564 break; /* not even the right length */ 565 566 /* Determine which part of response to verify against */ 567 if (rmd->UseNT[0]) { 568 nt_response = rmd->NTResp; 569 nt_response_size = sizeof(rmd->NTResp); 570 } else { 571 #ifdef MSLANMAN 572 lm_response = rmd->LANManResp; 573 lm_response_size = sizeof(rmd->LANManResp); 574 #else 575 /* Should really propagate this into the error packet. */ 576 notice("Peer request for LANMAN auth not supported"); 577 return NOT_AUTHENTICATED; 578 #endif /* MSLANMAN */ 579 } 580 581 /* ship off to winbind, and check */ 582 583 if (run_ntlm_auth(username, 584 domain, 585 NULL, 586 NULL, 587 challenge, 588 challenge_len, 589 lm_response, 590 lm_response ? lm_response_size: 0, 591 nt_response, 592 nt_response ? nt_response_size: 0, 593 session_key, 594 &error_string) == AUTHENTICATED) { 595 mppe_set_keys(challenge, session_key); 596 slprintf(message, message_space, "Access granted"); 597 return AUTHENTICATED; 598 599 } else { 600 if (error_string) { 601 notice(error_string); 602 free(error_string); 603 } 604 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0", 605 challenge_len, challenge); 606 return NOT_AUTHENTICATED; 607 } 608 break; 609 } 610 611 case CHAP_MICROSOFT_V2: 612 { 613 MS_Chap2Response *rmd = (MS_Chap2Response *) response; 614 u_char Challenge[8]; 615 u_char session_key[MD4_SIGNATURE_SIZE]; 616 char *error_string = NULL; 617 618 if (response_len != MS_CHAP2_RESPONSE_LEN) 619 break; /* not even the right length */ 620 621 ChallengeHash(rmd->PeerChallenge, challenge, user, Challenge); 622 623 /* ship off to winbind, and check */ 624 625 if (run_ntlm_auth(username, 626 domain, 627 NULL, 628 NULL, 629 Challenge, 630 8, 631 NULL, 632 0, 633 rmd->NTResp, 634 sizeof(rmd->NTResp), 635 636 session_key, 637 &error_string) == AUTHENTICATED) { 638 639 GenerateAuthenticatorResponse(session_key, 640 rmd->NTResp, rmd->PeerChallenge, 641 challenge, user, 642 saresponse); 643 mppe_set_keys2(session_key, rmd->NTResp, MS_CHAP2_AUTHENTICATOR); 644 if (rmd->Flags[0]) { 645 slprintf(message, message_space, "S=%s", saresponse); 646 } else { 647 slprintf(message, message_space, "S=%s M=%s", 648 saresponse, "Access granted"); 649 } 650 return AUTHENTICATED; 651 652 } else { 653 if (error_string) { 654 notice(error_string); 655 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s", 656 challenge_len, challenge, error_string); 657 free(error_string); 658 } else { 659 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s", 660 challenge_len, challenge, "Access denied"); 661 } 662 return NOT_AUTHENTICATED; 663 } 664 break; 665 } 666 667 default: 668 error("WINBIND: Challenge type %u unsupported", digest->code); 669 } 670 return NOT_AUTHENTICATED; 671 } 672 673 static int 674 winbind_allowed_address(u_int32_t addr) 675 { 676 ipcp_options *wo = &ipcp_wantoptions[0]; 677 if (wo->hisaddr !=0 && wo->hisaddr == addr) { 678 return 1; 679 } 680 return -1; 681 } 682