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