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