1/* 2 * pptpmanager.c 3 * 4 * Manages the PoPToP sessions. 5 * 6 * $Id: pptpmanager.c,v 1.14 2005/12/29 09:59:49 quozl Exp $ 7 */ 8 9#ifdef HAVE_CONFIG_H 10#include "config.h" 11#endif 12 13#ifdef __linux__ 14#define _GNU_SOURCE 1 /* broken arpa/inet.h */ 15#endif 16 17#include "our_syslog.h" 18 19#include <errno.h> 20#include <netdb.h> 21#include <signal.h> 22#include <stdio.h> 23#include <string.h> 24#include <stdlib.h> 25#include <sys/types.h> 26#include <sys/socket.h> 27#include <netinet/in.h> 28#include <arpa/inet.h> 29#include <sys/un.h> 30#include <sys/wait.h> 31#include <unistd.h> 32#include <time.h> 33#include <sys/time.h> 34#include <fcntl.h> 35 36#if HAVE_LIBWRAP 37/* re-include, just in case HAVE_SYSLOG_H wasn't defined */ 38#include <syslog.h> 39#include <tcpd.h> 40 41int allow_severity = LOG_WARNING; 42int deny_severity = LOG_WARNING; 43#endif 44 45#ifdef __UCLIBC__ 46#define socklen_t int 47#endif 48 49#include "configfile.h" 50#include "defaults.h" 51#include "pptpctrl.h" 52#include "pptpdefs.h" 53#include "pptpmanager.h" 54#include "compat.h" 55 56/* command line arg variables */ 57extern char *ppp_binary; 58extern char *pppdoptstr; 59extern char *speedstr; 60extern char *bindaddr; 61extern int pptp_debug; 62extern int pptp_noipparam; 63extern int pptp_logwtmp; 64extern int pptp_delegate; 65 66/* option for timeout on starting ctrl connection */ 67extern int pptp_stimeout; 68 69extern int pptp_connections; 70 71/* local function prototypes */ 72static void connectCall(int clientSocket, int clientNumber); 73static int createHostSocket(int *hostSocket); 74 75/* this end's call identifier */ 76uint16_t unique_call_id = 0; 77 78/* slots - begin */ 79 80/* data about connection slots */ 81struct slot { 82 pid_t pid; 83 char *local; 84 char *remote; 85} *slots; 86 87/* number of connection slots allocated */ 88int slot_count; 89 90static void slot_iterate(struct slot *slots, int count, void (*callback) (struct slot *slot)) 91{ 92 int i; 93 for(i=0; i<count; i++) 94 (*callback)(&slots[i]); 95} 96 97static void slot_slot_init(struct slot *slot) 98{ 99 slot->pid = 0; 100 slot->local = NULL; 101 slot->remote = NULL; 102} 103 104void slot_init(int count) 105{ 106 slot_count = count; 107 slots = (struct slot *) calloc(slot_count, sizeof(struct slot)); 108 slot_iterate(slots, slot_count, slot_slot_init); 109} 110 111static void slot_slot_free(struct slot *slot) 112{ 113 slot->pid = 0; 114 if (slot->local) free(slot->local); 115 slot->local = NULL; 116 if (slot->remote) free(slot->remote); 117 slot->remote = NULL; 118} 119 120void slot_free() 121{ 122 slot_iterate(slots, slot_count, slot_slot_free); 123 free(slots); 124 slots = NULL; 125 slot_count = 0; 126} 127 128void slot_set_local(int i, char *ip) 129{ 130 struct slot *slot = &slots[i]; 131 if (slot->local) free(slot->local); 132 slot->local = strdup(ip); 133} 134 135void slot_set_remote(int i, char *ip) 136{ 137 struct slot *slot = &slots[i]; 138 if (slot->remote) free(slot->remote); 139 slot->remote = strdup(ip); 140} 141 142void slot_set_pid(int i, pid_t pid) 143{ 144 struct slot *slot = &slots[i]; 145 slot->pid = pid; 146} 147 148int slot_find_by_pid(pid_t pid) 149{ 150 int i; 151 for(i=0; i<slot_count; i++) { 152 struct slot *slot = &slots[i]; 153 if (slot->pid == pid) return i; 154 } 155 return -1; 156} 157 158int slot_find_empty() 159{ 160 return slot_find_by_pid(0); 161} 162 163char *slot_get_local(int i) 164{ 165 struct slot *slot = &slots[i]; 166 return slot->local; 167} 168 169char *slot_get_remote(int i) 170{ 171 struct slot *slot = &slots[i]; 172 return slot->remote; 173} 174 175/* slots - end */ 176 177static void sigchld_responder(int sig) 178{ 179 int child, status; 180 181 while ((child = waitpid(-1, &status, WNOHANG)) > 0) { 182 if (pptp_delegate) { 183 if (pptp_debug) syslog(LOG_DEBUG, "MGR: Reaped child %d", child); 184 } else { 185 int i; 186 i = slot_find_by_pid(child); 187 if (i != -1) { 188 slot_set_pid(i, 0); 189 if (pptp_debug) syslog(LOG_DEBUG, "MGR: Reaped child %d", child); 190 } else { 191 syslog(LOG_INFO, "MGR: Reaped unknown child %d", child); 192 } 193 } 194 } 195} 196 197int pptp_manager(int argc, char **argv) 198{ 199 int firstOpen = -1; 200 int ctrl_pid; 201 socklen_t addrsize; 202 203 int hostSocket; 204 fd_set connSet; 205 206 int rc, sig_fd; 207 208 rc = sigpipe_create(); 209 if (rc < 0) { 210 syslog(LOG_ERR, "MGR: unable to setup sigchld pipe!"); 211 syslog_perror("sigpipe_create"); 212 exit(-1); 213 } 214 215 sigpipe_assign(SIGCHLD); 216 sigpipe_assign(SIGTERM); 217 sig_fd = sigpipe_fd(); 218 219 /* openlog() not required, done in pptpd.c */ 220 221 syslog(LOG_INFO, "MGR: Manager process started"); 222 223 if (!pptp_delegate) { 224 syslog(LOG_INFO, "MGR: Maximum of %d connections available", 225 pptp_connections); 226 } 227 228 /* Connect the host socket and activate it for listening */ 229 if (createHostSocket(&hostSocket) < 0) { 230 syslog(LOG_ERR, "MGR: Couldn't create host socket"); 231 syslog_perror("createHostSocket"); 232 exit(-1); 233 } 234 235 while (1) { 236 int max_fd; 237 FD_ZERO(&connSet); 238 if (pptp_delegate) { 239 FD_SET(hostSocket, &connSet); 240 } else { 241 firstOpen = slot_find_empty(); 242 if (firstOpen == -1) { 243 syslog(LOG_ERR, "MGR: No free connection slots or IPs - no more clients can connect!"); 244 } else { 245 FD_SET(hostSocket, &connSet); 246 } 247 } 248 max_fd = hostSocket; 249 250 FD_SET(sig_fd, &connSet); 251 if (max_fd < sig_fd) max_fd = sig_fd; 252 253 while (1) { 254 if (select(max_fd + 1, &connSet, NULL, NULL, NULL) != -1) break; 255 if (errno == EINTR) continue; 256 syslog(LOG_ERR, "MGR: Error with manager select()!"); 257 syslog_perror("select"); 258 exit(-1); 259 } 260 261 if (FD_ISSET(sig_fd, &connSet)) { /* SIGCHLD */ 262 int signum = sigpipe_read(); 263 if (signum == SIGCHLD) 264 sigchld_responder(signum); 265 else if (signum == SIGTERM) 266 return signum; 267 } 268 269 if (FD_ISSET(hostSocket, &connSet)) { /* A call came! */ 270 int clientSocket; 271 struct sockaddr_in client_addr; 272 273 /* Accept call and launch PPTPCTRL */ 274 addrsize = sizeof(client_addr); 275 clientSocket = accept(hostSocket, (struct sockaddr *) &client_addr, &addrsize); 276 277#if HAVE_LIBWRAP 278 if (clientSocket != -1) { 279 struct request_info r; 280 request_init(&r, RQ_DAEMON, "pptpd", RQ_FILE, clientSocket, NULL); 281 fromhost(&r); 282 if (!hosts_access(&r)) { 283 /* send a permission denied message? this is a tcp wrapper 284 * type deny so probably best to just drop it immediately like 285 * this, as tcp wrappers usually do. 286 */ 287 close(clientSocket); 288 /* this would never be file descriptor 0, so use it as a error 289 * value 290 */ 291 clientSocket = 0; 292 } 293 } 294#endif 295 if (clientSocket == -1) { 296 /* accept failed, but life goes on... */ 297 syslog(LOG_ERR, "MGR: accept() failed"); 298 syslog_perror("accept"); 299 } else if (clientSocket != 0) { 300 fd_set rfds; 301 struct timeval tv; 302 struct pptp_header ph; 303 304 /* TODO: this select below prevents 305 other connections from being 306 processed during the wait for the 307 first data packet from the 308 client. */ 309 310 /* 311 * DOS protection: get a peek at the first packet 312 * and do some checks on it before we continue. 313 * A 10 second timeout on the first packet seems reasonable 314 * to me, if anything looks sus, throw it away. 315 */ 316 317 FD_ZERO(&rfds); 318 FD_SET(clientSocket, &rfds); 319 tv.tv_sec = pptp_stimeout; 320 tv.tv_usec = 0; 321 if (select(clientSocket + 1, &rfds, NULL, NULL, &tv) <= 0) { 322 syslog(LOG_ERR, "MGR: dropped slow initial connection"); 323 close(clientSocket); 324 continue; 325 } 326 327 if (recv(clientSocket, &ph, sizeof(ph), MSG_PEEK) != 328 sizeof(ph)) { 329 syslog(LOG_ERR, "MGR: dropped small initial connection"); 330 close(clientSocket); 331 continue; 332 } 333 334 ph.length = ntohs(ph.length); 335 ph.pptp_type = ntohs(ph.pptp_type); 336 ph.magic = ntohl(ph.magic); 337 ph.ctrl_type = ntohs(ph.ctrl_type); 338 339 if (ph.length <= 0 || ph.length > PPTP_MAX_CTRL_PCKT_SIZE) { 340 syslog(LOG_WARNING, "MGR: initial packet length %d outside " 341 "(0 - %d)", ph.length, PPTP_MAX_CTRL_PCKT_SIZE); 342 goto dos_exit; 343 } 344 345 if (ph.magic != PPTP_MAGIC_COOKIE) { 346 syslog(LOG_WARNING, "MGR: initial packet bad magic"); 347 goto dos_exit; 348 } 349 350 if (ph.pptp_type != PPTP_CTRL_MESSAGE) { 351 syslog(LOG_WARNING, "MGR: initial packet has bad type"); 352 goto dos_exit; 353 } 354 355 if (ph.ctrl_type != START_CTRL_CONN_RQST) { 356 syslog(LOG_WARNING, "MGR: initial packet has bad ctrl type " 357 "0x%x", ph.ctrl_type); 358 dos_exit: 359 close(clientSocket); 360 continue; 361 } 362 363#ifndef HAVE_FORK 364 switch (ctrl_pid = vfork()) { 365#else 366 switch (ctrl_pid = fork()) { 367#endif 368 case -1: /* error */ 369 syslog(LOG_ERR, "MGR: fork() failed launching " PPTP_CTRL_BIN); 370 close(clientSocket); 371 break; 372 373 case 0: /* child */ 374 close(hostSocket); 375 if (pptp_debug) 376 syslog(LOG_DEBUG, "MGR: Launching " PPTP_CTRL_BIN " to handle client"); 377 connectCall(clientSocket, !pptp_delegate ? firstOpen : 0); 378 _exit(1); 379 /* NORETURN */ 380 default: /* parent */ 381 close(clientSocket); 382 unique_call_id += MAX_CALLS_PER_TCP_LINK; 383 if (!pptp_delegate) 384 slot_set_pid(firstOpen, ctrl_pid); 385 break; 386 } 387 } 388 } /* FD_ISSET(hostSocket, &connSet) */ 389 } /* while (1) */ 390} /* pptp_manager() */ 391 392/* 393 * Author: Kevin Thayer 394 * 395 * This creates a socket to listen on, sets the max # of pending connections and 396 * various other options. 397 * 398 * Returns the fd of the host socket. 399 * 400 * The function return values are: 401 * 0 for sucessful 402 * -1 for bad socket creation 403 * -2 for bad socket options 404 * -3 for bad bind 405 * -4 for bad listen 406 */ 407static int createHostSocket(int *hostSocket) 408{ 409 int opt = 1; 410 struct sockaddr_in address; 411#ifdef HAVE_GETSERVBYNAME 412 struct servent *serv; 413#endif 414 415 /* create the master socket and check it worked */ 416 if ((*hostSocket = socket(AF_INET, SOCK_STREAM, 0)) == 0) 417 return -1; 418 419 /* set master socket to allow daemon to be restarted with connections active */ 420 if (setsockopt(*hostSocket, SOL_SOCKET, SO_REUSEADDR, 421 (char *) &opt, sizeof(opt)) < 0) 422 return -2; 423 424 /* set up socket */ 425 memset(&address, 0, sizeof(address)); 426 address.sin_family = AF_INET; 427 if(bindaddr) 428 address.sin_addr.s_addr = inet_addr(bindaddr); 429 else 430 address.sin_addr.s_addr = INADDR_ANY; 431#ifdef HAVE_GETSERVBYNAME 432 if ((serv = getservbyname("pptp", "tcp")) != NULL) { 433 address.sin_port = serv->s_port; 434 } else 435#endif 436 address.sin_port = htons(PPTP_PORT); 437 438 /* bind the socket to the pptp port */ 439 if (bind(*hostSocket, (struct sockaddr *) &address, sizeof(address)) < 0) 440 return -3; 441 442 /* minimal backlog to avoid DoS */ 443 if (listen(*hostSocket, 3) < 0) 444 return -4; 445 446 return 0; 447} 448 449/* 450 * Author: Kevin Thayer 451 * 452 * this routine sets up the arguments for the call handler and calls it. 453 */ 454 455static void connectCall(int clientSocket, int clientNumber) 456{ 457 458#define NUM2ARRAY(array, num) snprintf(array, sizeof(array), "%d", num) 459 460 char *ctrl_argv[16]; /* arguments for launching 'pptpctrl' binary */ 461 462 int pptpctrl_argc = 0; /* count the number of arguments sent to pptpctrl */ 463 464 /* lame strings to hold passed args. */ 465 char ctrl_debug[2]; 466 char ctrl_noipparam[2]; 467 char pppdoptfile_argv[2]; 468 char speedgiven_argv[2]; 469 extern char **environ; 470 471 char callid_argv[16]; 472 473 /* 474 * Launch the CTRL manager binary; we send it some information such as 475 * speed and option file on the command line. 476 */ 477 478 ctrl_argv[pptpctrl_argc++] = PPTP_CTRL_BIN " "; 479 480 /* Pass socket as stdin */ 481 if (clientSocket != 0) { 482 dup2(clientSocket, 0); 483 close(clientSocket); 484 } 485 486 /* get argv set up */ 487 NUM2ARRAY(ctrl_debug, pptp_debug ? 1 : 0); 488 ctrl_debug[1] = '\0'; 489 ctrl_argv[pptpctrl_argc++] = ctrl_debug; 490 491 NUM2ARRAY(ctrl_noipparam, pptp_noipparam ? 1 : 0); 492 ctrl_noipparam[1] = '\0'; 493 ctrl_argv[pptpctrl_argc++] = ctrl_noipparam; 494 495 /* optionfile = TRUE or FALSE; so the CTRL manager knows whether to load a non-standard options file */ 496 NUM2ARRAY(pppdoptfile_argv, pppdoptstr ? 1 : 0); 497 pppdoptfile_argv[1] = '\0'; 498 ctrl_argv[pptpctrl_argc++] = pppdoptfile_argv; 499 if (pppdoptstr) { 500 /* send the option filename so the CTRL manager can launch pppd with this alternate file */ 501 ctrl_argv[pptpctrl_argc++] = pppdoptstr; 502 } 503 /* tell the ctrl manager whether we were given a speed */ 504 NUM2ARRAY(speedgiven_argv, speedstr ? 1 : 0); 505 speedgiven_argv[1] = '\0'; 506 ctrl_argv[pptpctrl_argc++] = speedgiven_argv; 507 if (speedstr) { 508 /* send the CTRL manager the speed of the connection so it can fire pppd at that speed */ 509 ctrl_argv[pptpctrl_argc++] = speedstr; 510 } 511 if (pptp_delegate) { 512 /* no local or remote address to specify */ 513 ctrl_argv[pptpctrl_argc++] = "0"; 514 ctrl_argv[pptpctrl_argc++] = "0"; 515 } else { 516 /* specify local & remote addresses for this call */ 517 ctrl_argv[pptpctrl_argc++] = "1"; 518 ctrl_argv[pptpctrl_argc++] = slot_get_local(clientNumber); 519 ctrl_argv[pptpctrl_argc++] = "1"; 520 ctrl_argv[pptpctrl_argc++] = slot_get_remote(clientNumber); 521 } 522 523 /* our call id to be included in GRE packets the client 524 * will send to us */ 525 NUM2ARRAY(callid_argv, unique_call_id); 526 ctrl_argv[pptpctrl_argc++] = callid_argv; 527 528 /* pass path to ppp binary */ 529 ctrl_argv[pptpctrl_argc++] = ppp_binary; 530 531 /* pass logwtmp flag */ 532 ctrl_argv[pptpctrl_argc++] = pptp_logwtmp ? "1" : "0"; 533 534 /* note: update pptpctrl.8 if the argument list format is changed */ 535 536 /* terminate argv array with a NULL */ 537 ctrl_argv[pptpctrl_argc] = NULL; 538 pptpctrl_argc++; 539 540 /* ok, args are setup: invoke the call handler */ 541 execve(PPTP_CTRL_BIN, ctrl_argv, environ); 542 syslog(LOG_ERR, "MGR: Failed to exec " PPTP_CTRL_BIN "!"); 543 _exit(1); 544} 545