Ejemplo n.º 1
0
static void my_daemon(int argc, char **argv)
{
#ifndef HAVE_FORK
	/* need to use vfork - eg, uClinux */
	char **new_argv;
	int pid;
	extern char **environ;
	int fdr;

	new_argv = malloc((argc + 2) * sizeof(char **));
	fdr = open("/dev/null", O_RDONLY);
	syslog(LOG_INFO, "MGR: Option parse OK, re-execing as daemon");
	fflush(stderr);
	if ((pid = vfork()) == 0) {
		if (fdr != 0) { dup2(fdr, 0); close(fdr); }
		SETSIDPGRP();
		chdir("/");
		umask(0);
		memcpy(new_argv + 1, argv, (argc + 1) * sizeof(char **));
		new_argv[0] = PPTPD_BIN;
		new_argv[1] = "-f";
		execve(PPTPD_BIN, new_argv, environ);
		_exit(1);
	} else if (pid > 0) {
		exit(0);
	} else {
		syslog_perror("vfork");
		exit(1);
	}
#else
	int pid;

	closelog();
	if ((pid = fork()) < 0) {
		syslog_perror("fork");
		exit(1);
	} else if (pid)
		exit(0);
	freopen("/dev/null", "r", stdin);
	SETSIDPGRP();
	chdir("/");
	umask(0);
	/* pid will have changed */
	openlog("pptpd", LOG_PID, PPTP_FACILITY);
#endif
}
Ejemplo n.º 2
0
/*
 * startCall
 *
 * Launches PPPD for the call.
 *
 * args:        pppaddrs - local/remote IPs or "" for either/both if none
 * retn:        pty file descriptor
 *
 */
static int startCall(char **pppaddrs, struct in_addr *inetaddrs)
{
	/* PTY/TTY pair for talking to PPPd */
	int pty_fd, tty_fd;
	/* register pids of children */
#if BSDUSER_PPP || SLIRP
	int sockfd[2];

#ifndef AF_LOCAL
#ifdef AF_UNIX
#define AF_LOCAL AF_UNIX /* Old BSD */
#else
#define AF_LOCAL AF_FILE /* POSIX */
#endif
#endif

	/* userspace ppp doesn't need to waste a real pty/tty pair */
	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd)) {
		syslog(LOG_ERR, "CTRL: socketpair() error");
		syslog_perror("socketpair");
		exit(1);
	}
	tty_fd = sockfd[0];
	pty_fd = sockfd[1];
#else
	/* Finds an open pty/tty pair */
	if (openpty(&pty_fd, &tty_fd, NULL, NULL, NULL) != 0) {
		syslog(LOG_ERR, "CTRL: openpty() error");
		syslog_perror("openpty");
		exit(1);
	} else {
		struct termios tios;

		/* Turn off echo in the slave - to prevent loopback.
		   pppd will do this, but might not do it before we
		   try to send data. */
		if (tcgetattr(tty_fd, &tios) < 0) {
			syslog(LOG_ERR, "CTRL: tcgetattr() error");
			syslog_perror("tcgetattr");
			exit(1);
		}
		tios.c_lflag &= ~(ECHO | ECHONL);
		if (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0) {
			syslog(LOG_ERR, "CTRL: tcsetattr() error");
			syslog_perror("tcsetattr");
			exit(1);
		}
	}
#endif
	if (pptpctrl_debug) {
		syslog(LOG_DEBUG, "CTRL: pty_fd = %d", pty_fd);
		syslog(LOG_DEBUG, "CTRL: tty_fd = %d", tty_fd);
	}
	/* Launch the PPPD  */
#ifndef HAVE_FORK
        switch(pppfork=vfork()){
#else
        switch(pppfork=fork()){
#endif
	case -1:	/* fork() error */
		syslog(LOG_ERR, "CTRL: Error forking to exec pppd");
		_exit(1);

	case 0:		/* child */
		if (dup2(tty_fd, 0) == -1) {
		  syslog(LOG_ERR, "CTRL: child tty_fd dup2 to stdin, %s",
			 strerror(errno));
		  exit(1);
		}
		if (dup2(tty_fd, 1) == -1) {
		  syslog(LOG_ERR, "CTRL: child tty_fd dup2 to stdout, %s",
			 strerror(errno));
		  exit(1);
		}
#if 0
		/* This must never be used if !HAVE_SYSLOG since that logs to stderr.
		 * Trying just never using it to see if it causes anyone else problems.
		 * It may let people see the pppd errors, which would be good.
		 */
		dup2(tty_fd, 2);
#endif
		if (tty_fd > 1)
			close(tty_fd);
		if (pty_fd > 1)
			close(pty_fd);
/* In case we move clientSocket back off stdin */
#ifndef clientSocket
		if (clientSocket > 1)
			close(clientSocket);
#elif clientSocket > 1
		close(clientSocket);
#endif
		launch_pppd(pppaddrs, inetaddrs);
		syslog(LOG_ERR, "CTRL: PPPD launch failed! (launch_pppd did not fork)");
		_exit(1);
	}
	
	close(tty_fd);
	return pty_fd;
}

/*
 * launch_pppd
 *
 * Launches the PPP daemon. The PPP daemon is responsible for assigning the
 * PPTP client its IP address.. These values are assigned via the command
 * line.
 *
 * Add return of connected ppp interface
 *
 * retn: 0 on success, -1 on failure.
 *
 */
static void launch_pppd(char **pppaddrs, struct in_addr *inetaddrs)
{
	char *pppd_argv[14];
	int an = 0;
	sigset_t sigs;

	pppd_argv[an++] = ppp_binary;

	if (pptpctrl_debug) {
		syslog(LOG_DEBUG, 
		       "CTRL (PPPD Launcher): program binary = %s", 
		       pppd_argv[an - 1]);
	}

#if BSDUSER_PPP

	/* The way that Brian Somers' user-land ppp works is to use the
	 * system name as a reference for most of the useful options. Hence
	 * most things can't be defined on the command line. On OpenBSD at
	 * least the file used for the systems is /etc/ppp/ppp.conf, where
	 * the pptp stanza should look something like:

	 pptp:
	 set speed sync
	 enable pap
	 enable chap
	 set dns a.a.a.a b.b.b.b
	 set ndbs x.x.x.x y.y.y.y
	 accept dns
	 add 10.0.0/24

	 * To be honest, at the time of writing, I haven't had the thing
	 * working enough to understand :) I will update this comment and
	 * make a sample config available when I get there.
	 */

	/* options for BSDUSER_PPP
	 *
	 * ignores IP addresses, config file option, speed
	 * fix usage info in pptpd.c and configure script if this changes
	 *
	 * IP addresses can be specified in /etc/ppp/ppp.secret per user
	 */
	pppd_argv[an++] = "-direct";
	pppd_argv[an++] = "pptp";	/* XXX this is the system name */
	/* should be dynamic - PMG */

#elif SLIRP

	/* options for SLIRP
	 *
	 * ignores IP addresses from config - SLIRP handles this
	 */
	pppd_argv[an++] = "-P";
	pppd_argv[an++] = "+chap";
	pppd_argv[an++] = "-b";

	/* If a speed has been specified, use it
	 * if not, use "smart" default (defaults.h)
	 */
	if (*speed) {
		pppd_argv[an++] = speed;
	} else {
		pppd_argv[an++] = PPP_SPEED_DEFAULT;
	}

	if (*pppdxfig) {
		pppd_argv[an++] = "-f";
		pppd_argv[an++] = pppdxfig;
	}

	if (pptpctrl_debug) {
		syslog(LOG_DEBUG, "CTRL (PPPD Launcher): Connection speed = %s", pppd_argv[an - 1]);
	}
#else

	/* options for 'normal' pppd */

	pppd_argv[an++] = "local";

	/* If a pppd option file is specified, use it
	 * if not, pppd will default to /etc/ppp/options
	 */
	if (*pppdxfig) {
		pppd_argv[an++] = "file";
		pppd_argv[an++] = pppdxfig;
	}
	
	/* If a speed has been specified, use it
	 * if not, use "smart" default (defaults.h)
	 */
	if (*speed) {
		pppd_argv[an++] = speed;
	} else {
		pppd_argv[an++] = PPP_SPEED_DEFAULT;
	}

	if (pptpctrl_debug) {
		if (*pppaddrs[0])
			syslog(LOG_DEBUG, "CTRL (PPPD Launcher): local address = %s", pppaddrs[0]);
		if (*pppaddrs[1])
			syslog(LOG_DEBUG, "CTRL (PPPD Launcher): remote address = %s", pppaddrs[1]);
	}
	
	if (*pppaddrs[0] || *pppaddrs[1]) {
		char pppInterfaceIPs[33];
		sprintf(pppInterfaceIPs, "%s:%s", pppaddrs[0], pppaddrs[1]);
		pppd_argv[an++] = pppInterfaceIPs;
	}
#endif

        if (!noipparam) {
                 pppd_argv[an++] = "ipparam";
                 pppd_argv[an++] = inet_ntoa(inetaddrs[1]);
        }

        if (pptp_logwtmp) {
                 pppd_argv[an++] = "plugin";
                 pppd_argv[an++] = "/usr/lib/pptpd/pptpd-logwtmp.so";
                 pppd_argv[an++] = "pptpd-original-ip";
                 pppd_argv[an++] = inet_ntoa(inetaddrs[1]);
        }

	/* argv arrays must always be NULL terminated */
	pppd_argv[an++] = NULL;
	/* make sure SIGCHLD is unblocked, pppd does not expect it */
	sigfillset(&sigs);
	sigprocmask(SIG_UNBLOCK, &sigs, NULL);
	/* run pppd now */
	execvp(pppd_argv[0], pppd_argv);
	/* execvp() failed */
	syslog(LOG_ERR, 
	       "CTRL (PPPD Launcher): Failed to launch PPP daemon. %s",
	       strerror(errno));
}
Ejemplo n.º 3
0
int main(int argc, char **argv)
{
	char pppLocal[16];		/* local IP to pass to pppd */
	char pppRemote[16];		/* remote IP address to pass to pppd */
	struct sockaddr_in addr;	/* client address */
	socklen_t addrlen;
	int arg = 1;
	int flags;
	struct in_addr inetaddrs[2];
	char *pppaddrs[2] = { pppLocal, pppRemote };

        gargc = argc;
        gargv = argv;

	/* fail if argument count invalid */
	if (argc < 7) {
		fprintf(stderr, "pptpctrl: insufficient arguments, see man pptpctrl\n");
		exit(2);
	}

	/* open a connection to the syslog daemon */
	openlog("pptpd", LOG_PID, PPTP_FACILITY);

	/* autoreap if supported */
	signal(SIGCHLD, SIG_IGN);

	/* note: update pptpctrl.8 if the argument list format is changed */
	GETARG_INT(pptpctrl_debug);
	GETARG_INT(noipparam);
	GETARG_VALUE(pppdxfig);
	GETARG_VALUE(speed);
	GETARG_VALUE(pppLocal);
	GETARG_VALUE(pppRemote);
	if (arg < argc) GETARG_INT(unique_call_id);
	if (arg < argc) GETARG_STRING(ppp_binary);
	if (arg < argc) GETARG_INT(pptp_logwtmp);
	
	if (pptpctrl_debug) {
		if (*pppLocal)
			syslog(LOG_DEBUG, "CTRL: local address = %s", pppLocal);
		if (*pppRemote)
			syslog(LOG_DEBUG, "CTRL: remote address = %s", pppRemote);
		if (*speed)
			syslog(LOG_DEBUG, "CTRL: pppd speed = %s", speed);
		if (*pppdxfig)
			syslog(LOG_DEBUG, "CTRL: pppd options file = %s", pppdxfig);
	}

	addrlen = sizeof(addr);
	if (getsockname(clientSocket, (struct sockaddr *) &addr, &addrlen) != 0) {
		syslog(LOG_ERR, "CTRL: getsockname() failed");
		syslog_perror("getsockname");
		close(clientSocket);
		bail(0);	/* NORETURN */
	}
	inetaddrs[0] = addr.sin_addr;

	addrlen = sizeof(addr);
	if (getpeername(clientSocket, (struct sockaddr *) &addr, &addrlen) != 0) {
		syslog(LOG_ERR, "CTRL: getpeername() failed");
		syslog_perror("getpeername");
		close(clientSocket);
		bail(0);	/* NORETURN */
	}
	inetaddrs[1] = addr.sin_addr;

	/* Set non-blocking */
	if ((flags = fcntl(clientSocket, F_GETFL, arg /* ignored */)) == -1 ||
	    fcntl(clientSocket, F_SETFL, flags|OUR_NB_MODE) == -1) {
		syslog(LOG_ERR, "CTRL: Failed to set client socket non-blocking");
		syslog_perror("fcntl");
		close(clientSocket);
		bail(0);	/* NORETURN */
	}

	
	/* Fiddle with argv */
        my_setproctitle(gargc, gargv, "pptpd [%s]%20c",
            inet_ntoa(addr.sin_addr), ' ');

	/* be ready for a grisly death */
	sigpipe_create();
	sigpipe_assign(SIGTERM);
	NOTE_VALUE(PAC, call_id_pair, htons(-1));
	NOTE_VALUE(PNS, call_id_pair, htons(-1));

	syslog(LOG_INFO, "CTRL: Client %s control connection started", inet_ntoa(addr.sin_addr));
	pptp_handle_ctrl_connection(pppaddrs, inetaddrs);
	syslog(LOG_DEBUG, "CTRL: Reaping child PPP[%i]", pppfork);
	if (pppfork > 0)
		waitpid(pppfork, NULL, 0);
	syslog(LOG_INFO, "CTRL: Client %s control connection finished", inet_ntoa(addr.sin_addr));

	bail(0);		/* NORETURN */
	return 1;		/* make gcc happy */
}
Ejemplo n.º 4
0
int pptp_manager(int argc, char **argv)
{
        int firstOpen = -1;
        int ctrl_pid;
        socklen_t addrsize;

        int hostSocket;
        fd_set connSet;

        int rc, sig_fd;

        rc = sigpipe_create();
        if (rc < 0) {
                syslog(LOG_ERR, "MGR: unable to setup sigchld pipe!");
                syslog_perror("sigpipe_create");
                exit(-1);
        }
        
        sigpipe_assign(SIGCHLD);
        sigpipe_assign(SIGTERM);
        sig_fd = sigpipe_fd();

        /* openlog() not required, done in pptpd.c */

        syslog(LOG_INFO, "MGR: Manager process started");

        if (!pptp_delegate) {
                syslog(LOG_INFO, "MGR: Maximum of %d connections available", 
                       pptp_connections);
        }

        /* Connect the host socket and activate it for listening */
        if (createHostSocket(&hostSocket) < 0) {
                syslog(LOG_ERR, "MGR: Couldn't create host socket");
                syslog_perror("createHostSocket");
                exit(-1);
        }

        while (1) {
                int max_fd;
                FD_ZERO(&connSet);
                if (pptp_delegate) {
                        FD_SET(hostSocket, &connSet);
                } else {
                        firstOpen = slot_find_empty();
                        if (firstOpen == -1) {
                                syslog(LOG_ERR, "MGR: No free connection slots or IPs - no more clients can connect!");
                        } else {
                                FD_SET(hostSocket, &connSet);
                        }
                }
                max_fd = hostSocket;

                FD_SET(sig_fd, &connSet);
                if (max_fd < sig_fd) max_fd = sig_fd;

                while (1) {
                        if (select(max_fd + 1, &connSet, NULL, NULL, NULL) != -1) break;
                        if (errno == EINTR) continue;
                        syslog(LOG_ERR, "MGR: Error with manager select()!");
                        syslog_perror("select");
                        exit(-1);
                }

                if (FD_ISSET(sig_fd, &connSet)) {       /* SIGCHLD */
                        int signum = sigpipe_read();
                        if (signum == SIGCHLD)
                                sigchld_responder(signum);
                        else if (signum == SIGTERM)
                        {
                                if (!keep_connections) sigterm_responder();
                                    return signum;
                        }
                }

                if (FD_ISSET(hostSocket, &connSet)) {   /* A call came! */
                        int clientSocket;
                        struct sockaddr_in client_addr;

                        /* Accept call and launch PPTPCTRL */
                        addrsize = sizeof(client_addr);
                        clientSocket = accept(hostSocket, (struct sockaddr *) &client_addr, &addrsize);

#ifdef HAVE_LIBWRAP
                        if (clientSocket != -1) {
                                struct request_info r;
                                request_init(&r, RQ_DAEMON, "pptpd", RQ_FILE, clientSocket, NULL);
                                fromhost(&r);
                                if (!hosts_access(&r)) {
                                        /* send a permission denied message? this is a tcp wrapper
                                         * type deny so probably best to just drop it immediately like
                                         * this, as tcp wrappers usually do.
                                         */
                                        close(clientSocket);
                                        /* this would never be file descriptor 0, so use it as a error
                                         * value
                                         */
                                        clientSocket = 0;
                                }
                        }
#endif
                        if (clientSocket == -1) {
                                /* accept failed, but life goes on... */
                                syslog(LOG_ERR, "MGR: accept() failed");
                                syslog_perror("accept");
                        } else if (clientSocket != 0) {
                                fd_set rfds;
                                struct timeval tv;
                                struct pptp_header ph;

                                /* TODO: this select below prevents
                                   other connections from being
                                   processed during the wait for the
                                   first data packet from the
                                   client. */

                                /*
                                 * DOS protection: get a peek at the first packet
                                 * and do some checks on it before we continue.
                                 * A 10 second timeout on the first packet seems reasonable
                                 * to me,  if anything looks sus,  throw it away.
                                 */

                                FD_ZERO(&rfds);
                                FD_SET(clientSocket, &rfds);
                                tv.tv_sec = pptp_stimeout;
                                tv.tv_usec = 0;
                                if (select(clientSocket + 1, &rfds, NULL, NULL, &tv) <= 0) {
                                        syslog(LOG_ERR, "MGR: dropped slow initial connection");
                                        close(clientSocket);
                                        continue;
                                }

                                if (recv(clientSocket, &ph, sizeof(ph), MSG_PEEK) !=
                                                sizeof(ph)) {
                                        syslog(LOG_ERR, "MGR: dropped small initial connection");
                                        close(clientSocket);
                                        continue;
                                }

                                ph.length = ntohs(ph.length);
                                ph.pptp_type = ntohs(ph.pptp_type);
                                ph.magic = ntohl(ph.magic);
                                ph.ctrl_type = ntohs(ph.ctrl_type);

                                if (ph.length <= 0 || ph.length > PPTP_MAX_CTRL_PCKT_SIZE) {
                                        syslog(LOG_WARNING, "MGR: initial packet length %d outside "
                                                        "(0 - %d)",  ph.length, PPTP_MAX_CTRL_PCKT_SIZE);
                                        goto dos_exit;
                                }

                                if (ph.magic != PPTP_MAGIC_COOKIE) {
                                        syslog(LOG_WARNING, "MGR: initial packet bad magic");
                                        goto dos_exit;
                                }

                                if (ph.pptp_type != PPTP_CTRL_MESSAGE) {
                                        syslog(LOG_WARNING, "MGR: initial packet has bad type");
                                        goto dos_exit;
                                }

                                if (ph.ctrl_type != START_CTRL_CONN_RQST) {
                                        syslog(LOG_WARNING, "MGR: initial packet has bad ctrl type "
                                                        "0x%x", ph.ctrl_type);
                dos_exit:
                                        close(clientSocket);
                                        continue;
                                }

#ifndef HAVE_FORK
                                switch (ctrl_pid = vfork()) {
#else
                                switch (ctrl_pid = fork()) {
#endif
                                case -1:        /* error */
                                        syslog(LOG_ERR, "MGR: fork() failed launching " PPTP_CTRL_BIN);
                                        close(clientSocket);
                                        break;

                                case 0: /* child */
                                        close(hostSocket);
                                        if (pptp_debug)
                                                syslog(LOG_DEBUG, "MGR: Launching " PPTP_CTRL_BIN " to handle client");
                                        connectCall(clientSocket, !pptp_delegate ? firstOpen : 0);
                                        _exit(1);
                                        /* NORETURN */
                                default:        /* parent */
                                        close(clientSocket);
                                        unique_call_id += MAX_CALLS_PER_TCP_LINK;
                                        if (!pptp_delegate)
                                                slot_set_pid(firstOpen, ctrl_pid);
                                        break;
                                }
                        }
                }               /* FD_ISSET(hostSocket, &connSet) */
        }                       /* while (1) */
}                               /* pptp_manager() */

/*
 * Author: Kevin Thayer
 * 
 * This creates a socket to listen on, sets the max # of pending connections and 
 * various other options.
 * 
 * Returns the fd of the host socket.
 * 
 * The function return values are:
 * 0 for sucessful
 * -1 for bad socket creation
 * -2 for bad socket options
 * -3 for bad bind
 * -4 for bad listen
 */
static int createHostSocket(int *hostSocket)
{
        int opt = 1;
        struct sockaddr_in address;
#ifdef HAVE_GETSERVBYNAME
        struct servent *serv;
#endif

        /* create the master socket and check it worked */
        if ((*hostSocket = vrf_socket(vrf, AF_INET, SOCK_STREAM, 0)) <= 0)
                return -1;

        /* set master socket to allow daemon to be restarted with connections active  */
        if (setsockopt(*hostSocket, SOL_SOCKET, SO_REUSEADDR,
                       (char *) &opt, sizeof(opt)) < 0)
                return -2;

        /* set up socket */
        memset(&address, 0, sizeof(address));
        address.sin_family = AF_INET;
        if(bindaddr)
                address.sin_addr.s_addr = inet_addr(bindaddr);
        else
                address.sin_addr.s_addr = INADDR_ANY;
#ifdef HAVE_GETSERVBYNAME
        if ((serv = getservbyname("pptp", "tcp")) != NULL) {
                address.sin_port = serv->s_port;
        } else
#endif
                address.sin_port = htons(PPTP_PORT);

        /* bind the socket to the pptp port */
        if (bind(*hostSocket, (struct sockaddr *) &address, sizeof(address)) < 0)
                return -3;

        /* minimal backlog to avoid DoS */
        if (listen(*hostSocket, 3) < 0)
                return -4;

        return 0;
}