authpf.c revision 130617
1130617Smlaier/*	$OpenBSD: authpf.c,v 1.75 2004/01/29 01:55:10 deraadt Exp $	*/
2126353Smlaier
3126353Smlaier/*
4126353Smlaier * Copyright (C) 1998 - 2002 Bob Beck (beck@openbsd.org).
5126353Smlaier *
6126353Smlaier * Redistribution and use in source and binary forms, with or without
7126353Smlaier * modification, are permitted provided that the following conditions
8126353Smlaier * are met:
9126353Smlaier * 1. Redistributions of source code must retain the above copyright
10126353Smlaier *    notice, this list of conditions and the following disclaimer.
11126353Smlaier * 2. Redistributions in binary form must reproduce the above copyright
12126353Smlaier *    notice, this list of conditions and the following disclaimer in the
13126353Smlaier *    documentation and/or other materials provided with the distribution.
14126353Smlaier *
15126353Smlaier * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16126353Smlaier * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17126353Smlaier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18126353Smlaier * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19126353Smlaier * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20126353Smlaier * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21126353Smlaier * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22126353Smlaier * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23126353Smlaier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24126353Smlaier * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25126353Smlaier * SUCH DAMAGE.
26126353Smlaier */
27126353Smlaier
28127082Sobrien#include <sys/cdefs.h>
29127082Sobrien__FBSDID("$FreeBSD: head/contrib/pf/authpf/authpf.c 130617 2004-06-16 23:39:33Z mlaier $");
30127082Sobrien
31127024Smlaier#include <sys/param.h>
32126353Smlaier#include <sys/file.h>
33126353Smlaier#include <sys/ioctl.h>
34126353Smlaier#include <sys/socket.h>
35126353Smlaier#include <sys/time.h>
36126353Smlaier
37126353Smlaier#include <net/if.h>
38126353Smlaier#include <net/pfvar.h>
39126353Smlaier#include <arpa/inet.h>
40126353Smlaier
41126353Smlaier#include <err.h>
42126353Smlaier#include <errno.h>
43126353Smlaier#include <pwd.h>
44126353Smlaier#include <signal.h>
45126353Smlaier#include <stdio.h>
46126353Smlaier#include <stdlib.h>
47126353Smlaier#include <string.h>
48126353Smlaier#include <syslog.h>
49126353Smlaier#include <unistd.h>
50126353Smlaier
51126353Smlaier#include <pfctl_parser.h>
52130617Smlaier#include <pfctl.h>
53126353Smlaier
54126353Smlaier#include "pathnames.h"
55126353Smlaier
56126353Smlaierextern int	symset(const char *, const char *, int);
57126353Smlaier
58126353Smlaierstatic int	read_config(FILE *);
59126353Smlaierstatic void	print_message(char *);
60126353Smlaierstatic int	allowed_luser(char *);
61126353Smlaierstatic int	check_luser(char *, char *);
62126353Smlaierstatic int	remove_stale_rulesets(void);
63126353Smlaierstatic int	change_filter(int, const char *, const char *);
64126353Smlaierstatic void	authpf_kill_states(void);
65126353Smlaier
66126353Smlaierint	dev;			/* pf device */
67126353Smlaierchar	anchorname[PF_ANCHOR_NAME_SIZE] = "authpf";
68126353Smlaierchar	rulesetname[PF_RULESET_NAME_SIZE];
69126353Smlaier
70126353SmlaierFILE	*pidfp;
71126353Smlaierchar	*infile;		/* file name printed by yyerror() in parse.y */
72126353Smlaierchar	 luser[MAXLOGNAME];	/* username */
73126353Smlaierchar	 ipsrc[256];		/* ip as a string */
74126353Smlaierchar	 pidfile[MAXPATHLEN];	/* we save pid in this file. */
75126353Smlaier
76126353Smlaierstruct timeval	Tstart, Tend;	/* start and end times of session */
77126353Smlaier
78126353Smlaiervolatile sig_atomic_t	want_death;
79126353Smlaierstatic void		need_death(int signo);
80127024Smlaier#ifdef __FreeBSD__
81127024Smlaierstatic __dead2 void	do_death(int);
82127024Smlaier#else
83126353Smlaierstatic __dead void	do_death(int);
84127024Smlaier#endif
85126353Smlaier
86126353Smlaier/*
87126353Smlaier * User shell for authenticating gateways. Sole purpose is to allow
88126353Smlaier * a user to ssh to a gateway, and have the gateway modify packet
89126353Smlaier * filters to allow access, then remove access when the user finishes
90126353Smlaier * up. Meant to be used only from ssh(1) connections.
91126353Smlaier */
92126353Smlaierint
93126353Smlaiermain(int argc, char *argv[])
94126353Smlaier{
95126353Smlaier	int		 lockcnt = 0, n, pidfd;
96126353Smlaier	FILE		*config;
97126353Smlaier	struct in_addr	 ina;
98126353Smlaier	struct passwd	*pw;
99126353Smlaier	char		*cp;
100126353Smlaier	uid_t		 uid;
101126353Smlaier
102126353Smlaier	config = fopen(PATH_CONFFILE, "r");
103126353Smlaier
104126353Smlaier	if ((cp = getenv("SSH_TTY")) == NULL) {
105126353Smlaier		syslog(LOG_ERR, "non-interactive session connection for authpf");
106126353Smlaier		exit(1);
107126353Smlaier	}
108126353Smlaier
109126353Smlaier	if ((cp = getenv("SSH_CLIENT")) == NULL) {
110126353Smlaier		syslog(LOG_ERR, "cannot determine connection source");
111126353Smlaier		exit(1);
112126353Smlaier	}
113126353Smlaier
114126353Smlaier	if (strlcpy(ipsrc, cp, sizeof(ipsrc)) >= sizeof(ipsrc)) {
115126353Smlaier		syslog(LOG_ERR, "SSH_CLIENT variable too long");
116126353Smlaier		exit(1);
117126353Smlaier	}
118126353Smlaier	cp = strchr(ipsrc, ' ');
119126353Smlaier	if (!cp) {
120126353Smlaier		syslog(LOG_ERR, "corrupt SSH_CLIENT variable %s", ipsrc);
121126353Smlaier		exit(1);
122126353Smlaier	}
123126353Smlaier	*cp = '\0';
124126353Smlaier	if (inet_pton(AF_INET, ipsrc, &ina) != 1) {
125126353Smlaier		syslog(LOG_ERR,
126126353Smlaier		    "cannot determine IP from SSH_CLIENT %s", ipsrc);
127126353Smlaier		exit(1);
128126353Smlaier	}
129126353Smlaier	/* open the pf device */
130126353Smlaier	dev = open(PATH_DEVFILE, O_RDWR);
131126353Smlaier	if (dev == -1) {
132126353Smlaier		syslog(LOG_ERR, "cannot open packet filter device (%m)");
133126353Smlaier		goto die;
134126353Smlaier	}
135126353Smlaier
136126353Smlaier	uid = getuid();
137126353Smlaier	pw = getpwuid(uid);
138126353Smlaier	if (pw == NULL) {
139126353Smlaier		syslog(LOG_ERR, "cannot find user for uid %u", uid);
140126353Smlaier		goto die;
141126353Smlaier	}
142126353Smlaier	if (strcmp(pw->pw_shell, PATH_AUTHPF_SHELL)) {
143126353Smlaier		syslog(LOG_ERR, "wrong shell for user %s, uid %u",
144126353Smlaier		    pw->pw_name, pw->pw_uid);
145126353Smlaier		goto die;
146126353Smlaier	}
147126353Smlaier
148126353Smlaier	/*
149126353Smlaier	 * Paranoia, but this data _does_ come from outside authpf, and
150126353Smlaier	 * truncation would be bad.
151126353Smlaier	 */
152126353Smlaier	if (strlcpy(luser, pw->pw_name, sizeof(luser)) >= sizeof(luser)) {
153126353Smlaier		syslog(LOG_ERR, "username too long: %s", pw->pw_name);
154126353Smlaier		goto die;
155126353Smlaier	}
156126353Smlaier
157130617Smlaier	if ((n = snprintf(rulesetname, sizeof(rulesetname), "%s(%ld)",
158130617Smlaier	    luser, (long)getpid())) < 0 || n >= sizeof(rulesetname)) {
159130617Smlaier		syslog(LOG_INFO, "%s(%ld) too large, ruleset name will be %ld",
160130617Smlaier		    luser, (long)getpid(), (long)getpid());
161130617Smlaier		if ((n = snprintf(rulesetname, sizeof(rulesetname), "%ld",
162130617Smlaier		    (long)getpid())) < 0 || n >= sizeof(rulesetname)) {
163130617Smlaier			syslog(LOG_ERR, "pid too large for ruleset name");
164130617Smlaier			goto die;
165130617Smlaier		}
166130617Smlaier	}
167130617Smlaier
168130617Smlaier
169126353Smlaier	/* Make our entry in /var/authpf as /var/authpf/ipaddr */
170126353Smlaier	n = snprintf(pidfile, sizeof(pidfile), "%s/%s", PATH_PIDFILE, ipsrc);
171126353Smlaier	if (n < 0 || (u_int)n >= sizeof(pidfile)) {
172126353Smlaier		syslog(LOG_ERR, "path to pidfile too long");
173126353Smlaier		goto die;
174126353Smlaier	}
175126353Smlaier
176126353Smlaier	/*
177126353Smlaier	 * If someone else is already using this ip, then this person
178126353Smlaier	 * wants to switch users - so kill the old process and exit
179126353Smlaier	 * as well.
180126353Smlaier	 *
181126353Smlaier	 * Note, we could print a message and tell them to log out, but the
182126353Smlaier	 * usual case of this is that someone has left themselves logged in,
183126353Smlaier	 * with the authenticated connection iconized and someone else walks
184126353Smlaier	 * up to use and automatically logs in before using. If this just
185126353Smlaier	 * gets rid of the old one silently, the new user never knows they
186126353Smlaier	 * could have used someone else's old authentication. If we
187126353Smlaier	 * tell them to log out before switching users it is an invitation
188126353Smlaier	 * for abuse.
189126353Smlaier	 */
190126353Smlaier
191126353Smlaier	do {
192126353Smlaier		int	save_errno, otherpid = -1;
193126353Smlaier		char	otherluser[MAXLOGNAME];
194126353Smlaier
195126353Smlaier		if ((pidfd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1 ||
196126353Smlaier		    (pidfp = fdopen(pidfd, "r+")) == NULL) {
197126353Smlaier			if (pidfd != -1)
198126353Smlaier				close(pidfd);
199126353Smlaier			syslog(LOG_ERR, "cannot open or create %s: %s", pidfile,
200126353Smlaier			    strerror(errno));
201126353Smlaier			goto die;
202126353Smlaier		}
203126353Smlaier
204126353Smlaier		if (flock(fileno(pidfp), LOCK_EX|LOCK_NB) == 0)
205126353Smlaier			break;
206126353Smlaier		save_errno = errno;
207126353Smlaier
208126353Smlaier		/* Mark our pid, and username to our file. */
209126353Smlaier
210126353Smlaier		rewind(pidfp);
211126353Smlaier		/* 31 == MAXLOGNAME - 1 */
212126353Smlaier		if (fscanf(pidfp, "%d\n%31s\n", &otherpid, otherluser) != 2)
213126353Smlaier			otherpid = -1;
214126353Smlaier		syslog(LOG_DEBUG, "tried to lock %s, in use by pid %d: %s",
215126353Smlaier		    pidfile, otherpid, strerror(save_errno));
216126353Smlaier
217126353Smlaier		if (otherpid > 0) {
218126353Smlaier			syslog(LOG_INFO,
219126353Smlaier			    "killing prior auth (pid %d) of %s by user %s",
220126353Smlaier			    otherpid, ipsrc, otherluser);
221126353Smlaier			if (kill((pid_t) otherpid, SIGTERM) == -1) {
222126353Smlaier				syslog(LOG_INFO,
223126353Smlaier				    "could not kill process %d: (%m)",
224126353Smlaier				    otherpid);
225126353Smlaier			}
226126353Smlaier		}
227126353Smlaier
228126353Smlaier		/*
229126353Smlaier		 * we try to kill the previous process and acquire the lock
230126353Smlaier		 * for 10 seconds, trying once a second. if we can't after
231126353Smlaier		 * 10 attempts we log an error and give up
232126353Smlaier		 */
233126353Smlaier		if (++lockcnt > 10) {
234126353Smlaier			syslog(LOG_ERR, "cannot kill previous authpf (pid %d)",
235126353Smlaier			    otherpid);
236126353Smlaier			goto dogdeath;
237126353Smlaier		}
238126353Smlaier		sleep(1);
239126353Smlaier
240126353Smlaier		/* re-open, and try again. The previous authpf process
241126353Smlaier		 * we killed above should unlink the file and release
242126353Smlaier		 * it's lock, giving us a chance to get it now
243126353Smlaier		 */
244126353Smlaier		fclose(pidfp);
245126353Smlaier	} while (1);
246126353Smlaier
247126353Smlaier	/* revoke privs */
248126353Smlaier	seteuid(getuid());
249126353Smlaier	setuid(getuid());
250126353Smlaier
251130617Smlaier	openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON);
252130617Smlaier
253130617Smlaier	if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(luser)) {
254130617Smlaier		syslog(LOG_INFO, "user %s prohibited", luser);
255126353Smlaier		do_death(0);
256130617Smlaier	}
257126353Smlaier
258130617Smlaier	if (config == NULL || read_config(config)) {
259130617Smlaier		syslog(LOG_INFO, "bad or nonexistent %s", PATH_CONFFILE);
260126353Smlaier		do_death(0);
261130617Smlaier	}
262126353Smlaier
263130617Smlaier	if (remove_stale_rulesets()) {
264130617Smlaier		syslog(LOG_INFO, "error removing stale rulesets");
265126353Smlaier		do_death(0);
266130617Smlaier	}
267126353Smlaier
268126353Smlaier	/* We appear to be making headway, so actually mark our pid */
269126353Smlaier	rewind(pidfp);
270126353Smlaier	fprintf(pidfp, "%ld\n%s\n", (long)getpid(), luser);
271126353Smlaier	fflush(pidfp);
272126353Smlaier	(void) ftruncate(fileno(pidfp), ftell(pidfp));
273126353Smlaier
274126353Smlaier	if (change_filter(1, luser, ipsrc) == -1) {
275126353Smlaier		printf("Unable to modify filters\r\n");
276130617Smlaier		do_death(0);
277126353Smlaier	}
278126353Smlaier
279126353Smlaier	signal(SIGTERM, need_death);
280126353Smlaier	signal(SIGINT, need_death);
281126353Smlaier	signal(SIGALRM, need_death);
282126353Smlaier	signal(SIGPIPE, need_death);
283126353Smlaier	signal(SIGHUP, need_death);
284126353Smlaier	signal(SIGSTOP, need_death);
285126353Smlaier	signal(SIGTSTP, need_death);
286126353Smlaier	while (1) {
287126353Smlaier		printf("\r\nHello %s, ", luser);
288126353Smlaier		printf("You are authenticated from host \"%s\"\r\n", ipsrc);
289126353Smlaier		setproctitle("%s@%s", luser, ipsrc);
290126353Smlaier		print_message(PATH_MESSAGE);
291126353Smlaier		while (1) {
292126353Smlaier			sleep(10);
293126353Smlaier			if (want_death)
294126353Smlaier				do_death(1);
295126353Smlaier		}
296126353Smlaier	}
297126353Smlaier
298126353Smlaier	/* NOTREACHED */
299126353Smlaierdogdeath:
300126353Smlaier	printf("\r\n\r\nSorry, this service is currently unavailable due to ");
301126353Smlaier	printf("technical difficulties\r\n\r\n");
302126353Smlaier	print_message(PATH_PROBLEM);
303126353Smlaier	printf("\r\nYour authentication process (pid %ld) was unable to run\n",
304126353Smlaier	    (long)getpid());
305126353Smlaier	sleep(180); /* them lusers read reaaaaal slow */
306126353Smlaierdie:
307126353Smlaier	do_death(0);
308127024Smlaier
309127024Smlaier	/* NOTREACHED */
310126353Smlaier}
311126353Smlaier
312126353Smlaier/*
313126353Smlaier * reads config file in PATH_CONFFILE to set optional behaviours up
314126353Smlaier */
315126353Smlaierstatic int
316126353Smlaierread_config(FILE *f)
317126353Smlaier{
318126353Smlaier	char	buf[1024];
319126353Smlaier	int	i = 0;
320126353Smlaier
321126353Smlaier	do {
322126353Smlaier		char	**ap;
323126353Smlaier		char	 *pair[4], *cp, *tp;
324126353Smlaier		int	  len;
325126353Smlaier
326126353Smlaier		if (fgets(buf, sizeof(buf), f) == NULL) {
327126353Smlaier			fclose(f);
328126353Smlaier			return (0);
329126353Smlaier		}
330126353Smlaier		i++;
331126353Smlaier		len = strlen(buf);
332126353Smlaier		if (buf[len - 1] != '\n' && !feof(f)) {
333126353Smlaier			syslog(LOG_ERR, "line %d too long in %s", i,
334126353Smlaier			    PATH_CONFFILE);
335126353Smlaier			return (1);
336126353Smlaier		}
337126353Smlaier		buf[len - 1] = '\0';
338126353Smlaier
339126353Smlaier		for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
340126353Smlaier			; /* nothing */
341126353Smlaier
342126353Smlaier		if (!*cp || *cp == '#' || *cp == '\n')
343126353Smlaier			continue;
344126353Smlaier
345126353Smlaier		for (ap = pair; ap < &pair[3] &&
346126353Smlaier		    (*ap = strsep(&cp, "=")) != NULL; ) {
347126353Smlaier			if (**ap != '\0')
348126353Smlaier				ap++;
349126353Smlaier		}
350126353Smlaier		if (ap != &pair[2])
351126353Smlaier			goto parse_error;
352126353Smlaier
353126353Smlaier		tp = pair[1] + strlen(pair[1]);
354126353Smlaier		while ((*tp == ' ' || *tp == '\t') && tp >= pair[1])
355126353Smlaier			*tp-- = '\0';
356126353Smlaier
357126353Smlaier		if (strcasecmp(pair[0], "anchor") == 0) {
358126353Smlaier			if (!pair[1][0] || strlcpy(anchorname, pair[1],
359126353Smlaier			    sizeof(anchorname)) >= sizeof(anchorname))
360126353Smlaier				goto parse_error;
361126353Smlaier		}
362126353Smlaier	} while (!feof(f) && !ferror(f));
363126353Smlaier	fclose(f);
364126353Smlaier	return (0);
365126353Smlaier
366126353Smlaierparse_error:
367126353Smlaier	fclose(f);
368126353Smlaier	syslog(LOG_ERR, "parse error, line %d of %s", i, PATH_CONFFILE);
369126353Smlaier	return (1);
370126353Smlaier}
371126353Smlaier
372126353Smlaier
373126353Smlaier/*
374126353Smlaier * splatter a file to stdout - max line length of 1024,
375126353Smlaier * used for spitting message files at users to tell them
376126353Smlaier * they've been bad or we're unavailable.
377126353Smlaier */
378126353Smlaierstatic void
379126353Smlaierprint_message(char *filename)
380126353Smlaier{
381126353Smlaier	char	 buf[1024];
382126353Smlaier	FILE	*f;
383126353Smlaier
384126353Smlaier	if ((f = fopen(filename, "r")) == NULL)
385126353Smlaier		return; /* fail silently, we don't care if it isn't there */
386126353Smlaier
387126353Smlaier	do {
388126353Smlaier		if (fgets(buf, sizeof(buf), f) == NULL) {
389126353Smlaier			fflush(stdout);
390126353Smlaier			fclose(f);
391126353Smlaier			return;
392126353Smlaier		}
393126353Smlaier	} while (fputs(buf, stdout) != EOF && !feof(f));
394126353Smlaier	fflush(stdout);
395126353Smlaier	fclose(f);
396126353Smlaier}
397126353Smlaier
398126353Smlaier/*
399126353Smlaier * allowed_luser checks to see if user "luser" is allowed to
400126353Smlaier * use this gateway by virtue of being listed in an allowed
401126353Smlaier * users file, namely /etc/authpf/authpf.allow .
402126353Smlaier *
403126353Smlaier * If /etc/authpf/authpf.allow does not exist, then we assume that
404126353Smlaier * all users who are allowed in by sshd(8) are permitted to
405126353Smlaier * use this gateway. If /etc/authpf/authpf.allow does exist, then a
406126353Smlaier * user must be listed if the connection is to continue, else
407126353Smlaier * the session terminates in the same manner as being banned.
408126353Smlaier */
409126353Smlaierstatic int
410126353Smlaierallowed_luser(char *luser)
411126353Smlaier{
412126353Smlaier	char	*buf, *lbuf;
413126353Smlaier	int	 matched;
414126353Smlaier	size_t	 len;
415126353Smlaier	FILE	*f;
416126353Smlaier
417126353Smlaier	if ((f = fopen(PATH_ALLOWFILE, "r")) == NULL) {
418126353Smlaier		if (errno == ENOENT) {
419126353Smlaier			/*
420126353Smlaier			 * allowfile doesn't exist, thus this gateway
421126353Smlaier			 * isn't restricted to certain users...
422126353Smlaier			 */
423126353Smlaier			return (1);
424126353Smlaier		}
425126353Smlaier
426126353Smlaier		/*
427126353Smlaier		 * luser may in fact be allowed, but we can't open
428126353Smlaier		 * the file even though it's there. probably a config
429126353Smlaier		 * problem.
430126353Smlaier		 */
431126353Smlaier		syslog(LOG_ERR, "cannot open allowed users file %s (%s)",
432126353Smlaier		    PATH_ALLOWFILE, strerror(errno));
433126353Smlaier		return (0);
434126353Smlaier	} else {
435126353Smlaier		/*
436126353Smlaier		 * /etc/authpf/authpf.allow exists, thus we do a linear
437126353Smlaier		 * search to see if they are allowed.
438126353Smlaier		 * also, if username "*" exists, then this is a
439126353Smlaier		 * "public" gateway, such as it is, so let
440126353Smlaier		 * everyone use it.
441126353Smlaier		 */
442126353Smlaier		lbuf = NULL;
443126353Smlaier		while ((buf = fgetln(f, &len))) {
444126353Smlaier			if (buf[len - 1] == '\n')
445126353Smlaier				buf[len - 1] = '\0';
446126353Smlaier			else {
447126353Smlaier				if ((lbuf = (char *)malloc(len + 1)) == NULL)
448126353Smlaier					err(1, NULL);
449126353Smlaier				memcpy(lbuf, buf, len);
450126353Smlaier				lbuf[len] = '\0';
451126353Smlaier				buf = lbuf;
452126353Smlaier			}
453126353Smlaier
454126353Smlaier			matched = strcmp(luser, buf) == 0 || strcmp("*", buf) == 0;
455126353Smlaier
456126353Smlaier			if (lbuf != NULL) {
457126353Smlaier				free(lbuf);
458126353Smlaier				lbuf = NULL;
459126353Smlaier			}
460126353Smlaier
461126353Smlaier			if (matched)
462126353Smlaier				return (1); /* matched an allowed username */
463126353Smlaier		}
464126353Smlaier		syslog(LOG_INFO, "denied access to %s: not listed in %s",
465126353Smlaier		    luser, PATH_ALLOWFILE);
466126353Smlaier
467126353Smlaier		/* reuse buf */
468126353Smlaier		buf = "\n\nSorry, you are not allowed to use this facility!\n";
469126353Smlaier		fputs(buf, stdout);
470126353Smlaier	}
471126353Smlaier	fflush(stdout);
472126353Smlaier	return (0);
473126353Smlaier}
474126353Smlaier
475126353Smlaier/*
476126353Smlaier * check_luser checks to see if user "luser" has been banned
477126353Smlaier * from using us by virtue of having an file of the same name
478126353Smlaier * in the "luserdir" directory.
479126353Smlaier *
480126353Smlaier * If the user has been banned, we copy the contents of the file
481126353Smlaier * to the user's screen. (useful for telling the user what to
482126353Smlaier * do to get un-banned, or just to tell them they aren't
483126353Smlaier * going to be un-banned.)
484126353Smlaier */
485126353Smlaierstatic int
486126353Smlaiercheck_luser(char *luserdir, char *luser)
487126353Smlaier{
488126353Smlaier	FILE	*f;
489126353Smlaier	int	 n;
490126353Smlaier	char	 tmp[MAXPATHLEN];
491126353Smlaier
492126353Smlaier	n = snprintf(tmp, sizeof(tmp), "%s/%s", luserdir, luser);
493126353Smlaier	if (n < 0 || (u_int)n >= sizeof(tmp)) {
494126353Smlaier		syslog(LOG_ERR, "provided banned directory line too long (%s)",
495126353Smlaier		    luserdir);
496126353Smlaier		return (0);
497126353Smlaier	}
498126353Smlaier	if ((f = fopen(tmp, "r")) == NULL) {
499126353Smlaier		if (errno == ENOENT) {
500126353Smlaier			/*
501126353Smlaier			 * file or dir doesn't exist, so therefore
502126353Smlaier			 * this luser isn't banned..  all is well
503126353Smlaier			 */
504126353Smlaier			return (1);
505126353Smlaier		} else {
506126353Smlaier			/*
507126353Smlaier			 * luser may in fact be banned, but we can't open the
508126353Smlaier			 * file even though it's there. probably a config
509126353Smlaier			 * problem.
510126353Smlaier			 */
511126353Smlaier			syslog(LOG_ERR, "cannot open banned file %s (%s)",
512126353Smlaier			    tmp, strerror(errno));
513126353Smlaier			return (0);
514126353Smlaier		}
515126353Smlaier	} else {
516126353Smlaier		/*
517126353Smlaier		 * luser is banned - spit the file at them to
518126353Smlaier		 * tell what they can do and where they can go.
519126353Smlaier		 */
520126353Smlaier		syslog(LOG_INFO, "denied access to %s: %s exists",
521126353Smlaier		    luser, tmp);
522126353Smlaier
523126353Smlaier		/* reuse tmp */
524126353Smlaier		strlcpy(tmp, "\n\n-**- Sorry, you have been banned! -**-\n\n",
525126353Smlaier		    sizeof(tmp));
526126353Smlaier		while (fputs(tmp, stdout) != EOF && !feof(f)) {
527126353Smlaier			if (fgets(tmp, sizeof(tmp), f) == NULL) {
528126353Smlaier				fflush(stdout);
529126353Smlaier				return (0);
530126353Smlaier			}
531126353Smlaier		}
532126353Smlaier	}
533126353Smlaier	fflush(stdout);
534126353Smlaier	return (0);
535126353Smlaier}
536126353Smlaier
537126353Smlaier/*
538126353Smlaier * Search for rulesets left by other authpf processes (either because they
539126353Smlaier * died ungracefully or were terminated) and remove them.
540126353Smlaier */
541126353Smlaierstatic int
542126353Smlaierremove_stale_rulesets(void)
543126353Smlaier{
544126353Smlaier	struct pfioc_ruleset	 prs;
545126353Smlaier	const int		 action[PF_RULESET_MAX] = { PF_SCRUB,
546126353Smlaier				    PF_PASS, PF_NAT, PF_BINAT, PF_RDR };
547126353Smlaier	u_int32_t		 nr, mnr;
548126353Smlaier
549126353Smlaier	memset(&prs, 0, sizeof(prs));
550126353Smlaier	strlcpy(prs.anchor, anchorname, sizeof(prs.anchor));
551126353Smlaier	if (ioctl(dev, DIOCGETRULESETS, &prs)) {
552126353Smlaier		if (errno == EINVAL)
553126353Smlaier			return (0);
554126353Smlaier		else
555126353Smlaier			return (1);
556126353Smlaier	}
557126353Smlaier
558126353Smlaier	mnr = prs.nr;
559126353Smlaier	nr = 0;
560126353Smlaier	while (nr < mnr) {
561130617Smlaier		char	*s, *t;
562126353Smlaier		pid_t	 pid;
563126353Smlaier
564126353Smlaier		prs.nr = nr;
565126353Smlaier		if (ioctl(dev, DIOCGETRULESET, &prs))
566126353Smlaier			return (1);
567126353Smlaier		errno = 0;
568130617Smlaier		if ((t = strchr(prs.name, '(')) == NULL)
569130617Smlaier			t = prs.name;
570130617Smlaier		else
571130617Smlaier			t++;
572130617Smlaier		pid = strtoul(t, &s, 10);
573130617Smlaier		if (!prs.name[0] || errno ||
574130617Smlaier		    (*s && (t == prs.name || *s != ')')))
575126353Smlaier			return (1);
576126353Smlaier		if (kill(pid, 0) && errno != EPERM) {
577126353Smlaier			int i;
578126353Smlaier
579126353Smlaier			for (i = 0; i < PF_RULESET_MAX; ++i) {
580126353Smlaier				struct pfioc_rule pr;
581126353Smlaier
582126353Smlaier				memset(&pr, 0, sizeof(pr));
583126353Smlaier				memcpy(pr.anchor, prs.anchor, sizeof(pr.anchor));
584126353Smlaier				memcpy(pr.ruleset, prs.name, sizeof(pr.ruleset));
585126353Smlaier				pr.rule.action = action[i];
586126353Smlaier				if ((ioctl(dev, DIOCBEGINRULES, &pr) ||
587126353Smlaier				    ioctl(dev, DIOCCOMMITRULES, &pr)) &&
588126353Smlaier				    errno != EINVAL)
589126353Smlaier					return (1);
590126353Smlaier			}
591126353Smlaier			mnr--;
592126353Smlaier		} else
593126353Smlaier			nr++;
594126353Smlaier	}
595126353Smlaier	return (0);
596126353Smlaier}
597126353Smlaier
598126353Smlaier/*
599126353Smlaier * Add/remove filter entries for user "luser" from ip "ipsrc"
600126353Smlaier */
601126353Smlaierstatic int
602126353Smlaierchange_filter(int add, const char *luser, const char *ipsrc)
603126353Smlaier{
604126353Smlaier	char			 fn[MAXPATHLEN];
605126353Smlaier	FILE			*f = NULL;
606126353Smlaier	struct pfctl		 pf;
607130617Smlaier	struct pfr_buffer	 t;
608126353Smlaier	int			 i;
609126353Smlaier
610130617Smlaier	if (luser == NULL || !luser[0] || ipsrc == NULL || !ipsrc[0]) {
611126353Smlaier		syslog(LOG_ERR, "invalid luser/ipsrc");
612126353Smlaier		goto error;
613126353Smlaier	}
614126353Smlaier
615126353Smlaier	if (add) {
616126353Smlaier		if ((i = snprintf(fn, sizeof(fn), "%s/%s/authpf.rules",
617126353Smlaier		    PATH_USER_DIR, luser)) < 0 || i >= sizeof(fn)) {
618126353Smlaier			syslog(LOG_ERR, "user rule path too long");
619126353Smlaier			goto error;
620126353Smlaier		}
621126353Smlaier		if ((f = fopen(fn, "r")) == NULL && errno != ENOENT) {
622126353Smlaier			syslog(LOG_ERR, "cannot open %s (%m)", fn);
623126353Smlaier			goto error;
624126353Smlaier		}
625126353Smlaier		if (f == NULL) {
626126353Smlaier			if (strlcpy(fn, PATH_PFRULES, sizeof(fn)) >=
627126353Smlaier			    sizeof(fn)) {
628126353Smlaier				syslog(LOG_ERR, "rule path too long");
629126353Smlaier				goto error;
630126353Smlaier			}
631126353Smlaier			if ((f = fopen(fn, "r")) == NULL) {
632126353Smlaier				syslog(LOG_ERR, "cannot open %s (%m)", fn);
633126353Smlaier				goto error;
634126353Smlaier			}
635126353Smlaier		}
636126353Smlaier	}
637126353Smlaier
638126353Smlaier	if (pfctl_load_fingerprints(dev, 0)) {
639126353Smlaier		syslog(LOG_ERR, "unable to load kernel's OS fingerprints");
640126353Smlaier		goto error;
641126353Smlaier	}
642130617Smlaier	bzero(&t, sizeof(t));
643130617Smlaier	t.pfrb_type = PFRB_TRANS;
644126353Smlaier	memset(&pf, 0, sizeof(pf));
645126353Smlaier	for (i = 0; i < PF_RULESET_MAX; ++i) {
646130617Smlaier		if (pfctl_add_trans(&t, i, anchorname, rulesetname)) {
647130617Smlaier			syslog(LOG_ERR, "pfctl_add_trans %m");
648126353Smlaier			goto error;
649126353Smlaier		}
650126353Smlaier	}
651130617Smlaier	if (pfctl_trans(dev, &t, DIOCXBEGIN, 0)) {
652130617Smlaier		syslog(LOG_ERR, "DIOCXBEGIN (%s) %m", add?"add":"remove");
653130617Smlaier		goto error;
654130617Smlaier	}
655126353Smlaier
656126353Smlaier	if (add) {
657126353Smlaier		if (symset("user_ip", ipsrc, 0) ||
658126353Smlaier		    symset("user_id", luser, 0)) {
659126353Smlaier			syslog(LOG_ERR, "symset");
660126353Smlaier			goto error;
661126353Smlaier		}
662126353Smlaier
663126353Smlaier		pf.dev = dev;
664130617Smlaier		pf.trans = &t;
665130617Smlaier		pf.anchor = anchorname;
666130617Smlaier		pf.ruleset = rulesetname;
667130617Smlaier
668126353Smlaier		infile = fn;
669126353Smlaier		if (parse_rules(f, &pf) < 0) {
670126353Smlaier			syslog(LOG_ERR, "syntax error in rule file: "
671126353Smlaier			    "authpf rules not loaded");
672126353Smlaier			goto error;
673126353Smlaier		}
674126353Smlaier
675126353Smlaier		infile = NULL;
676126353Smlaier		fclose(f);
677126353Smlaier		f = NULL;
678126353Smlaier	}
679126353Smlaier
680130617Smlaier	if (pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) {
681130617Smlaier		syslog(LOG_ERR, "DIOCXCOMMIT (%s) %m", add?"add":"remove");
682130617Smlaier		goto error;
683130617Smlaier	}
684126353Smlaier
685126353Smlaier	if (add) {
686126353Smlaier		gettimeofday(&Tstart, NULL);
687126353Smlaier		syslog(LOG_INFO, "allowing %s, user %s", ipsrc, luser);
688126353Smlaier	} else {
689126353Smlaier		gettimeofday(&Tend, NULL);
690126353Smlaier		syslog(LOG_INFO, "removed %s, user %s - duration %ld seconds",
691126353Smlaier		    ipsrc, luser, Tend.tv_sec - Tstart.tv_sec);
692126353Smlaier	}
693126353Smlaier	return (0);
694126353Smlaier
695126353Smlaiererror:
696126353Smlaier	if (f != NULL)
697126353Smlaier		fclose(f);
698130617Smlaier	if (pfctl_trans(dev, &t, DIOCXROLLBACK, 0))
699130617Smlaier		syslog(LOG_ERR, "DIOCXROLLBACK (%s) %m", add?"add":"remove");
700126353Smlaier
701126353Smlaier	infile = NULL;
702126353Smlaier	return (-1);
703126353Smlaier}
704126353Smlaier
705126353Smlaier/*
706126353Smlaier * This is to kill off states that would otherwise be left behind stateful
707126353Smlaier * rules. This means we don't need to allow in more traffic than we really
708126353Smlaier * want to, since we don't have to worry about any luser sessions lasting
709126353Smlaier * longer than their ssh session. This function is based on
710126353Smlaier * pfctl_kill_states from pfctl.
711126353Smlaier */
712126353Smlaierstatic void
713126353Smlaierauthpf_kill_states(void)
714126353Smlaier{
715126353Smlaier	struct pfioc_state_kill	psk;
716126353Smlaier	struct in_addr		target;
717126353Smlaier
718126353Smlaier	memset(&psk, 0, sizeof(psk));
719126353Smlaier	psk.psk_af = AF_INET;
720126353Smlaier
721126353Smlaier	inet_pton(AF_INET, ipsrc, &target);
722126353Smlaier
723126353Smlaier	/* Kill all states from ipsrc */
724126353Smlaier	psk.psk_src.addr.v.a.addr.v4 = target;
725126353Smlaier	memset(&psk.psk_src.addr.v.a.mask, 0xff,
726126353Smlaier	    sizeof(psk.psk_src.addr.v.a.mask));
727126353Smlaier	if (ioctl(dev, DIOCKILLSTATES, &psk))
728126353Smlaier		syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
729126353Smlaier
730126353Smlaier	/* Kill all states to ipsrc */
731126353Smlaier	psk.psk_af = AF_INET;
732126353Smlaier	memset(&psk.psk_src, 0, sizeof(psk.psk_src));
733126353Smlaier	psk.psk_dst.addr.v.a.addr.v4 = target;
734126353Smlaier	memset(&psk.psk_dst.addr.v.a.mask, 0xff,
735126353Smlaier	    sizeof(psk.psk_dst.addr.v.a.mask));
736126353Smlaier	if (ioctl(dev, DIOCKILLSTATES, &psk))
737126353Smlaier		syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
738126353Smlaier}
739126353Smlaier
740126353Smlaier/* signal handler that makes us go away properly */
741126353Smlaierstatic void
742126353Smlaierneed_death(int signo)
743126353Smlaier{
744126353Smlaier	want_death = 1;
745126353Smlaier}
746126353Smlaier
747126353Smlaier/*
748126353Smlaier * function that removes our stuff when we go away.
749126353Smlaier */
750127024Smlaier#ifdef __FreeBSD__
751127024Smlaierstatic __dead2 void
752127024Smlaier#else
753126353Smlaierstatic __dead void
754127024Smlaier#endif
755126353Smlaierdo_death(int active)
756126353Smlaier{
757126353Smlaier	int	ret = 0;
758126353Smlaier
759126353Smlaier	if (active) {
760126353Smlaier		change_filter(0, luser, ipsrc);
761126353Smlaier		authpf_kill_states();
762126353Smlaier		remove_stale_rulesets();
763126353Smlaier	}
764126353Smlaier	if (pidfp)
765126353Smlaier		ftruncate(fileno(pidfp), 0);
766126353Smlaier	if (pidfile[0])
767126353Smlaier		if (unlink(pidfile) == -1)
768126353Smlaier			syslog(LOG_ERR, "cannot unlink %s (%m)", pidfile);
769126353Smlaier	exit(ret);
770126353Smlaier}
771126353Smlaier
772126353Smlaier/*
773126353Smlaier * callbacks for parse_rules(void)
774126353Smlaier */
775126353Smlaier
776126353Smlaierint
777126353Smlaierpfctl_add_rule(struct pfctl *pf, struct pf_rule *r)
778126353Smlaier{
779130617Smlaier	u_int8_t		rs_num;
780130617Smlaier	struct pfioc_rule	pr;
781126353Smlaier
782126353Smlaier	switch (r->action) {
783126353Smlaier	case PF_PASS:
784126353Smlaier	case PF_DROP:
785130617Smlaier		rs_num = PF_RULESET_FILTER;
786126353Smlaier		break;
787126353Smlaier	case PF_SCRUB:
788130617Smlaier		rs_num = PF_RULESET_SCRUB;
789126353Smlaier		break;
790126353Smlaier	case PF_NAT:
791126353Smlaier	case PF_NONAT:
792130617Smlaier		rs_num = PF_RULESET_NAT;
793126353Smlaier		break;
794126353Smlaier	case PF_RDR:
795126353Smlaier	case PF_NORDR:
796130617Smlaier		rs_num = PF_RULESET_RDR;
797126353Smlaier		break;
798126353Smlaier	case PF_BINAT:
799126353Smlaier	case PF_NOBINAT:
800130617Smlaier		rs_num = PF_RULESET_BINAT;
801126353Smlaier		break;
802126353Smlaier	default:
803126353Smlaier		syslog(LOG_ERR, "invalid rule action %d", r->action);
804126353Smlaier		return (1);
805126353Smlaier	}
806130617Smlaier
807130617Smlaier	bzero(&pr, sizeof(pr));
808130617Smlaier	strlcpy(pr.anchor, pf->anchor, sizeof(pr.anchor));
809130617Smlaier	strlcpy(pr.ruleset, pf->ruleset, sizeof(pr.ruleset));
810126353Smlaier	if (pfctl_add_pool(pf, &r->rpool, r->af))
811126353Smlaier		return (1);
812130617Smlaier	pr.ticket = pfctl_get_ticket(pf->trans, rs_num, pf->anchor,
813130617Smlaier	    pf->ruleset);
814130617Smlaier	pr.pool_ticket = pf->paddr.ticket;
815130617Smlaier	memcpy(&pr.rule, r, sizeof(pr.rule));
816130617Smlaier	if (ioctl(pf->dev, DIOCADDRULE, &pr)) {
817126353Smlaier		syslog(LOG_ERR, "DIOCADDRULE %m");
818126353Smlaier		return (1);
819126353Smlaier	}
820126353Smlaier	pfctl_clear_pool(&r->rpool);
821126353Smlaier	return (0);
822126353Smlaier}
823126353Smlaier
824126353Smlaierint
825126353Smlaierpfctl_add_pool(struct pfctl *pf, struct pf_pool *p, sa_family_t af)
826126353Smlaier{
827126353Smlaier	struct pf_pooladdr	*pa;
828126353Smlaier
829126353Smlaier	if (ioctl(pf->dev, DIOCBEGINADDRS, &pf->paddr)) {
830126353Smlaier		syslog(LOG_ERR, "DIOCBEGINADDRS %m");
831126353Smlaier		return (1);
832126353Smlaier	}
833126353Smlaier	pf->paddr.af = af;
834126353Smlaier	TAILQ_FOREACH(pa, &p->list, entries) {
835126353Smlaier		memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr));
836126353Smlaier		if (ioctl(pf->dev, DIOCADDADDR, &pf->paddr)) {
837126353Smlaier			syslog(LOG_ERR, "DIOCADDADDR %m");
838126353Smlaier			return (1);
839126353Smlaier		}
840126353Smlaier	}
841126353Smlaier	return (0);
842126353Smlaier}
843126353Smlaier
844126353Smlaiervoid
845126353Smlaierpfctl_clear_pool(struct pf_pool *pool)
846126353Smlaier{
847126353Smlaier	struct pf_pooladdr	*pa;
848126353Smlaier
849126353Smlaier	while ((pa = TAILQ_FIRST(&pool->list)) != NULL) {
850126353Smlaier		TAILQ_REMOVE(&pool->list, pa, entries);
851126353Smlaier		free(pa);
852126353Smlaier	}
853126353Smlaier}
854126353Smlaier
855126353Smlaierint
856126353Smlaierpfctl_add_altq(struct pfctl *pf, struct pf_altq *a)
857126353Smlaier{
858126353Smlaier	fprintf(stderr, "altq rules not supported in authpf\n");
859126353Smlaier	return (1);
860126353Smlaier}
861126353Smlaier
862126353Smlaierint
863126353Smlaierpfctl_set_optimization(struct pfctl *pf, const char *opt)
864126353Smlaier{
865126353Smlaier	fprintf(stderr, "set optimization not supported in authpf\n");
866126353Smlaier	return (1);
867126353Smlaier}
868126353Smlaier
869126353Smlaierint
870126353Smlaierpfctl_set_logif(struct pfctl *pf, char *ifname)
871126353Smlaier{
872126353Smlaier	fprintf(stderr, "set loginterface not supported in authpf\n");
873126353Smlaier	return (1);
874126353Smlaier}
875126353Smlaier
876126353Smlaierint
877130617Smlaierpfctl_set_hostid(struct pfctl *pf, u_int32_t hostid)
878130617Smlaier{
879130617Smlaier	fprintf(stderr, "set hostid not supported in authpf\n");
880130617Smlaier	return (1);
881130617Smlaier}
882130617Smlaier
883130617Smlaierint
884126353Smlaierpfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
885126353Smlaier{
886126353Smlaier	fprintf(stderr, "set timeout not supported in authpf\n");
887126353Smlaier	return (1);
888126353Smlaier}
889126353Smlaier
890126353Smlaierint
891126353Smlaierpfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)
892126353Smlaier{
893126353Smlaier	fprintf(stderr, "set limit not supported in authpf\n");
894126353Smlaier	return (1);
895126353Smlaier}
896126353Smlaier
897126353Smlaierint
898130617Smlaierpfctl_set_debug(struct pfctl *pf, char *d)
899130617Smlaier{
900130617Smlaier	fprintf(stderr, "set debug not supported in authpf\n");
901130617Smlaier	return (1);
902130617Smlaier}
903130617Smlaier
904130617Smlaierint
905126353Smlaierpfctl_define_table(char *name, int flags, int addrs, const char *anchor,
906126353Smlaier    const char *ruleset, struct pfr_buffer *ab, u_int32_t ticket)
907126353Smlaier{
908126353Smlaier	fprintf(stderr, "table definitions not yet supported in authpf\n");
909126353Smlaier	return (1);
910126353Smlaier}
911126353Smlaier
912126353Smlaierint
913126353Smlaierpfctl_rules(int dev, char *filename, int opts, char *anchorname,
914130617Smlaier    char *rulesetname, struct pfr_buffer *t)
915126353Smlaier{
916126353Smlaier	/* never called, no anchors inside anchors, but we need the stub */
917126353Smlaier	fprintf(stderr, "load anchor not supported from authpf\n");
918126353Smlaier	return (1);
919126353Smlaier}
920126353Smlaier
921130617Smlaiervoid
922130617Smlaierpfctl_print_title(char *title)
923130617Smlaier{
924130617Smlaier}
925