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@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@elgo.si> 19* Copyright (C) 1996, Lars Fenneberg <in5y050@public.uni-hamburg.de> 20* Copyright (C) 1997, Miguel A.L. Paraz <map@iphil.net> 21* 22* Uses radiusclient library, which is: 23* Copyright (C) 1995,1996,1997,1998 Lars Fenneberg <lf@elemental.net> 24* Copyright (C) 2002 Roaring Penguin Software Inc. 25* 26* MPPE support is by Ralf Hofmann, <ralf.hofmann@elvido.net>, with 27* modification from Frank Cusack, <frank@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 62static char *ntlm_auth = NULL; 63 64static 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 84static 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 90static int 91winbind_secret_check(void); 92 93static int winbind_pap_auth(char *user, 94 char *passwd, 95 char **msgp, 96 struct wordlist **paddrs, 97 struct wordlist **popts); 98static 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); 103static int winbind_allowed_address(u_int32_t addr); 104 105char pppd_version[] = VERSION; 106 107/********************************************************************** 108* %FUNCTION: plugin_init 109* %ARGUMENTS: 110* None 111* %RETURNS: 112* Nothing 113* %DESCRIPTION: 114* Initializes WINBIND plugin. 115***********************************************************************/ 116void 117plugin_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 168size_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 203static 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 **/ 210char * base64_encode(const char *data) 211{ 212 size_t out_cnt = 0; 213 size_t len = strlen(data); 214 size_t output_len = 4 * ((len + 2) / 3) + 2; 215 const unsigned char *ptr = (const unsigned char *) data; 216 char *result = malloc(output_len); /* get us plenty of space */ 217 unsigned int bits; 218 219 for (; len >= 3; len -= 3) { 220 bits = (ptr[0] << 16) + (ptr[1] << 8) + ptr[2]; 221 ptr += 3; 222 result[out_cnt++] = b64[bits >> 18]; 223 result[out_cnt++] = b64[(bits >> 12) & 0x3f]; 224 result[out_cnt++] = b64[(bits >> 6) & 0x3f]; 225 result[out_cnt++] = b64[bits & 0x3f]; 226 } 227 if (len != 0) { 228 bits = ptr[0] << 16; 229 if (len > 1) 230 bits |= ptr[1] << 8; 231 result[out_cnt++] = b64[bits >> 18]; 232 result[out_cnt++] = b64[(bits >> 12) & 0x3f]; 233 result[out_cnt++] = (len > 1)? b64[(bits >> 6) & 0x3f]: '='; 234 result[out_cnt++] = '='; 235 } 236 237 result[out_cnt] = '\0'; /* terminate */ 238 return result; 239} 240 241unsigned int run_ntlm_auth(const char *username, 242 const char *domain, 243 const char *full_username, 244 const char *plaintext_password, 245 const u_char *challenge, 246 size_t challenge_length, 247 const u_char *lm_response, 248 size_t lm_response_length, 249 const u_char *nt_response, 250 size_t nt_response_length, 251 u_char nt_key[16], 252 char **error_string) 253{ 254 255 pid_t forkret; 256 int child_in[2]; 257 int child_out[2]; 258 int status; 259 260 int authenticated = NOT_AUTHENTICATED; /* not auth */ 261 int got_user_session_key = 0; /* not got key */ 262 263 char buffer[1024]; 264 265 FILE *pipe_in; 266 FILE *pipe_out; 267 268 int i; 269 char *challenge_hex; 270 char *lm_hex_hash; 271 char *nt_hex_hash; 272 273 /* First see if we have a program to run... */ 274 if (ntlm_auth == NULL) 275 return NOT_AUTHENTICATED; 276 277 /* Make first child */ 278 if (pipe(child_out) == -1) { 279 error("pipe creation failed for child OUT!"); 280 return NOT_AUTHENTICATED; 281 } 282 283 if (pipe(child_in) == -1) { 284 error("pipe creation failed for child IN!"); 285 return NOT_AUTHENTICATED; 286 } 287 288 forkret = safe_fork(child_in[0], child_out[1], 2); 289 if (forkret == -1) { 290 if (error_string) { 291 *error_string = strdup("fork failed!"); 292 } 293 294 return NOT_AUTHENTICATED; 295 } 296 297 if (forkret == 0) { 298 /* child process */ 299 uid_t uid; 300 301 close(child_out[0]); 302 close(child_in[1]); 303 304 /* run winbind as the user that invoked pppd */ 305 setgid(getgid()); 306 uid = getuid(); 307 if (setuid(uid) == -1 || getuid() != uid) 308 fatal("pppd/winbind: could not setuid to %d: %m", uid); 309 execl("/bin/sh", "sh", "-c", ntlm_auth, NULL); 310 fatal("pppd/winbind: could not exec /bin/sh: %m"); 311 } 312 313 /* parent */ 314 close(child_out[1]); 315 close(child_in[0]); 316 317 /* Need to write the User's info onto the pipe */ 318 319 pipe_in = fdopen(child_in[1], "w"); 320 321 pipe_out = fdopen(child_out[0], "r"); 322 323 /* look for session key coming back */ 324 325 if (username) { 326 char *b64_username = base64_encode(username); 327 fprintf(pipe_in, "Username:: %s\n", b64_username); 328 free(b64_username); 329 } 330 331 if (domain) { 332 char *b64_domain = base64_encode(domain); 333 fprintf(pipe_in, "NT-Domain:: %s\n", b64_domain); 334 free(b64_domain); 335 } 336 337 if (full_username) { 338 char *b64_full_username = base64_encode(full_username); 339 fprintf(pipe_in, "Full-Username:: %s\n", b64_full_username); 340 free(b64_full_username); 341 } 342 343 if (plaintext_password) { 344 char *b64_plaintext_password = base64_encode(plaintext_password); 345 fprintf(pipe_in, "Password:: %s\n", b64_plaintext_password); 346 free(b64_plaintext_password); 347 } 348 349 if (challenge_length) { 350 fprintf(pipe_in, "Request-User-Session-Key: yes\n"); 351 352 challenge_hex = malloc(challenge_length*2+1); 353 354 for (i = 0; i < challenge_length; i++) 355 sprintf(challenge_hex + i * 2, "%02X", challenge[i]); 356 357 fprintf(pipe_in, "LANMAN-Challenge: %s\n", challenge_hex); 358 free(challenge_hex); 359 } 360 361 if (lm_response_length) { 362 lm_hex_hash = malloc(lm_response_length*2+1); 363 364 for (i = 0; i < lm_response_length; i++) 365 sprintf(lm_hex_hash + i * 2, "%02X", lm_response[i]); 366 367 fprintf(pipe_in, "LANMAN-response: %s\n", lm_hex_hash); 368 free(lm_hex_hash); 369 } 370 371 if (nt_response_length) { 372 nt_hex_hash = malloc(nt_response_length*2+1); 373 374 for (i = 0; i < nt_response_length; i++) 375 sprintf(nt_hex_hash + i * 2, "%02X", nt_response[i]); 376 377 fprintf(pipe_in, "NT-response: %s\n", nt_hex_hash); 378 free(nt_hex_hash); 379 } 380 381 fprintf(pipe_in, ".\n"); 382 fflush(pipe_in); 383 384 while (fgets(buffer, sizeof(buffer)-1, pipe_out) != NULL) { 385 char *message, *parameter; 386 if (buffer[strlen(buffer)-1] != '\n') { 387 break; 388 } 389 buffer[strlen(buffer)-1] = '\0'; 390 message = buffer; 391 392 if (!(parameter = strstr(buffer, ": "))) { 393 break; 394 } 395 396 parameter[0] = '\0'; 397 parameter++; 398 parameter[0] = '\0'; 399 parameter++; 400 401 if (strcmp(message, ".") == 0) { 402 /* end of sequence */ 403 break; 404 } else if (strcasecmp(message, "Authenticated") == 0) { 405 if (strcasecmp(parameter, "Yes") == 0) { 406 authenticated = AUTHENTICATED; 407 } else { 408 notice("Winbind has declined authentication for user!"); 409 authenticated = NOT_AUTHENTICATED; 410 } 411 } else if (strcasecmp(message, "User-session-key") == 0) { 412 /* length is the number of characters to parse */ 413 if (nt_key) { 414 if (strhex_to_str(nt_key, 32, parameter) == 16) { 415 got_user_session_key = 1; 416 } else { 417 notice("NT session key for user was not 16 bytes!"); 418 } 419 } 420 } else if (strcasecmp(message, "Error") == 0) { 421 authenticated = NOT_AUTHENTICATED; 422 if (error_string) 423 *error_string = strdup(parameter); 424 } else if (strcasecmp(message, "Authentication-Error") == 0) { 425 authenticated = NOT_AUTHENTICATED; 426 if (error_string) 427 *error_string = strdup(parameter); 428 } else { 429 notice("unrecognised input from ntlm_auth helper - %s: %s", message, parameter); 430 } 431 } 432 433 /* parent */ 434 if (close(child_out[0]) == -1) { 435 notice("error closing pipe?!? for child OUT[0]"); 436 return NOT_AUTHENTICATED; 437 } 438 439 /* parent */ 440 if (close(child_in[1]) == -1) { 441 notice("error closing pipe?!? for child IN[1]"); 442 return NOT_AUTHENTICATED; 443 } 444 445 while ((wait(&status) == -1) && errno == EINTR) 446 ; 447 448 if ((authenticated == AUTHENTICATED) && nt_key && !got_user_session_key) { 449 notice("Did not get user session key, despite being authenticated!"); 450 return NOT_AUTHENTICATED; 451 } 452 return authenticated; 453} 454 455/********************************************************************** 456* %FUNCTION: winbind_secret_check 457* %ARGUMENTS: 458* None 459* %RETURNS: 460* 0 if we don't have an ntlm_auth program to run, otherwise 1. 461* %DESCRIPTION: 462* Tells pppd that we will try to authenticate the peer, and not to 463* worry about looking in /etc/ppp/ *-secrets 464***********************************************************************/ 465static int 466winbind_secret_check(void) 467{ 468 return ntlm_auth != NULL; 469} 470 471/********************************************************************** 472* %FUNCTION: winbind_pap_auth 473* %ARGUMENTS: 474* user -- user-name of peer 475* passwd -- password supplied by peer 476* msgp -- Message which will be sent in PAP response 477* paddrs -- set to a list of possible peer IP addresses 478* popts -- set to a list of additional pppd options 479* %RETURNS: 480* 1 if we can authenticate, -1 if we cannot. 481* %DESCRIPTION: 482* Performs PAP authentication using WINBIND 483***********************************************************************/ 484static int 485winbind_pap_auth(char *user, 486 char *password, 487 char **msgp, 488 struct wordlist **paddrs, 489 struct wordlist **popts) 490{ 491 if (run_ntlm_auth(NULL, NULL, user, password, NULL, 0, NULL, 0, NULL, 0, NULL, msgp) == AUTHENTICATED) { 492 return 1; 493 } 494 return -1; 495} 496 497/********************************************************************** 498* %FUNCTION: winbind_chap_auth 499* %ARGUMENTS: 500* user -- user-name of peer 501* remmd -- hash received from peer 502* remmd_len -- length of remmd 503* cstate -- pppd's chap_state structure 504* %RETURNS: 505* AUTHENTICATED (1) if we can authenticate, NOT_AUTHENTICATED (0) if we cannot. 506* %DESCRIPTION: 507* Performs MS-CHAP and MS-CHAPv2 authentication using WINBIND. 508***********************************************************************/ 509 510static int 511winbind_chap_verify(char *user, char *ourname, int id, 512 struct chap_digest_type *digest, 513 unsigned char *challenge, 514 unsigned char *response, 515 char *message, int message_space) 516{ 517 int challenge_len, response_len; 518 char domainname[256]; 519 char *domain; 520 char *username; 521 char *p; 522 char saresponse[MS_AUTH_RESPONSE_LENGTH+1]; 523 524 /* The first byte of each of these strings contains their length */ 525 challenge_len = *challenge++; 526 response_len = *response++; 527 528 /* remove domain from "domain\username" */ 529 if ((username = strrchr(user, '\\')) != NULL) 530 ++username; 531 else 532 username = user; 533 534 strlcpy(domainname, user, sizeof(domainname)); 535 536 /* remove domain from "domain\username" */ 537 if ((p = strrchr(domainname, '\\')) != NULL) { 538 *p = '\0'; 539 domain = domainname; 540 } else { 541 domain = NULL; 542 } 543 544 /* generate MD based on negotiated type */ 545 switch (digest->code) { 546 547 case CHAP_MICROSOFT: 548 { 549 char *error_string = NULL; 550 u_char *nt_response = NULL; 551 u_char *lm_response = NULL; 552 int nt_response_size = 0; 553 int lm_response_size = 0; 554 u_char session_key[16]; 555 556 if (response_len != MS_CHAP_RESPONSE_LEN) 557 break; /* not even the right length */ 558 559 /* Determine which part of response to verify against */ 560 if (response[MS_CHAP_USENT]) { 561 nt_response = &response[MS_CHAP_NTRESP]; 562 nt_response_size = MS_CHAP_NTRESP_LEN; 563 } else { 564#ifdef MSLANMAN 565 lm_response = &response[MS_CHAP_LANMANRESP]; 566 lm_response_size = MS_CHAP_LANMANRESP_LEN; 567#else 568 /* Should really propagate this into the error packet. */ 569 notice("Peer request for LANMAN auth not supported"); 570 return NOT_AUTHENTICATED; 571#endif /* MSLANMAN */ 572 } 573 574 /* ship off to winbind, and check */ 575 576 if (run_ntlm_auth(username, 577 domain, 578 NULL, 579 NULL, 580 challenge, challenge_len, 581 lm_response, lm_response_size, 582 nt_response, nt_response_size, 583 session_key, 584 &error_string) == AUTHENTICATED) { 585 mppe_set_keys(challenge, session_key); 586 slprintf(message, message_space, "Access granted"); 587 return AUTHENTICATED; 588 589 } else { 590 if (error_string) { 591 notice(error_string); 592 free(error_string); 593 } 594 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0", 595 challenge_len, challenge); 596 return NOT_AUTHENTICATED; 597 } 598 break; 599 } 600 601 case CHAP_MICROSOFT_V2: 602 { 603 u_char Challenge[8]; 604 u_char session_key[MD4_SIGNATURE_SIZE]; 605 char *error_string = NULL; 606 607 if (response_len != MS_CHAP2_RESPONSE_LEN) 608 break; /* not even the right length */ 609 610 ChallengeHash(&response[MS_CHAP2_PEER_CHALLENGE], challenge, 611 user, Challenge); 612 613 /* ship off to winbind, and check */ 614 615 if (run_ntlm_auth(username, 616 domain, 617 NULL, 618 NULL, 619 Challenge, 8, 620 NULL, 0, 621 &response[MS_CHAP2_NTRESP], 622 MS_CHAP2_NTRESP_LEN, 623 session_key, 624 &error_string) == AUTHENTICATED) { 625 626 GenerateAuthenticatorResponse(session_key, 627 &response[MS_CHAP2_NTRESP], 628 &response[MS_CHAP2_PEER_CHALLENGE], 629 challenge, user, saresponse); 630 mppe_set_keys2(session_key, &response[MS_CHAP2_NTRESP], 631 MS_CHAP2_AUTHENTICATOR); 632 if (response[MS_CHAP2_FLAGS]) { 633 slprintf(message, message_space, "S=%s", saresponse); 634 } else { 635 slprintf(message, message_space, "S=%s M=%s", 636 saresponse, "Access granted"); 637 } 638 return AUTHENTICATED; 639 640 } else { 641 if (error_string) { 642 notice(error_string); 643 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s", 644 challenge_len, challenge, error_string); 645 free(error_string); 646 } else { 647 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s", 648 challenge_len, challenge, "Access denied"); 649 } 650 return NOT_AUTHENTICATED; 651 } 652 break; 653 } 654 655 default: 656 error("WINBIND: Challenge type %u unsupported", digest->code); 657 } 658 return NOT_AUTHENTICATED; 659} 660 661static int 662winbind_allowed_address(u_int32_t addr) 663{ 664 ipcp_options *wo = &ipcp_wantoptions[0]; 665 if (wo->hisaddr !=0 && wo->hisaddr == addr) { 666 return 1; 667 } 668 return -1; 669} 670