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