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