xref: /openbsd-src/usr.sbin/authpf/authpf.c (revision 2119819d27c39b88d37ed54bf0fe45068921d06d)
1*2119819dSjca /*	$OpenBSD: authpf.c,v 1.130 2024/11/04 21:59:15 jca Exp $	*/
2b81c0f41Sderaadt 
3bc4d3c8cSbeck /*
48aff7383Sbeck  * Copyright (C) 1998 - 2007 Bob Beck (beck@openbsd.org).
5bc4d3c8cSbeck  *
68aff7383Sbeck  * Permission to use, copy, modify, and distribute this software for any
78aff7383Sbeck  * purpose with or without fee is hereby granted, provided that the above
88aff7383Sbeck  * copyright notice and this permission notice appear in all copies.
9bc4d3c8cSbeck  *
108aff7383Sbeck  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
118aff7383Sbeck  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
128aff7383Sbeck  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
138aff7383Sbeck  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
148aff7383Sbeck  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
158aff7383Sbeck  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
168aff7383Sbeck  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17bc4d3c8cSbeck  */
18bc4d3c8cSbeck 
19bc4d3c8cSbeck #include <sys/types.h>
20bc4d3c8cSbeck #include <sys/ioctl.h>
21bc4d3c8cSbeck #include <sys/socket.h>
2248f9c6d2Sbeck #include <sys/stat.h>
2348f9c6d2Sbeck #include <sys/wait.h>
24bc4d3c8cSbeck 
2568928c43Sderaadt #include <netinet/in.h>
2668928c43Sderaadt #include <arpa/inet.h>
27bc4d3c8cSbeck #include <net/if.h>
28bc4d3c8cSbeck #include <net/pfvar.h>
29bc4d3c8cSbeck 
30bc4d3c8cSbeck #include <err.h>
31bc4d3c8cSbeck #include <errno.h>
32f4147939Sguenther #include <fcntl.h>
33b939c4c8Shenning #include <login_cap.h>
34f38893f8Sbeck #include <pwd.h>
357940323aSmcbride #include <grp.h>
36bc4d3c8cSbeck #include <signal.h>
37bc4d3c8cSbeck #include <stdio.h>
38bc4d3c8cSbeck #include <stdlib.h>
39bc4d3c8cSbeck #include <string.h>
40bc4d3c8cSbeck #include <syslog.h>
416f074127Scheloha #include <time.h>
42bc4d3c8cSbeck #include <unistd.h>
43bc4d3c8cSbeck 
44bc4d3c8cSbeck #include "pathnames.h"
45bc4d3c8cSbeck 
46834f5058Sderaadt static int	read_config(FILE *);
47bc4d3c8cSbeck static void	print_message(char *);
487940323aSmcbride static int	allowed_luser(struct passwd *);
49bc4d3c8cSbeck static int	check_luser(char *, char *);
50cc5e4fc4Sdhartmei static int	remove_stale_rulesets(void);
5159d5b6a4Smcbride static int	recursive_ruleset_purge(char *, char *);
52f535f952Sdhartmei static int	change_filter(int, const char *, const char *);
53f6004c0dSbeck static int	change_table(int, const char *);
54bc4d3c8cSbeck static void	authpf_kill_states(void);
55834f5058Sderaadt 
56f535f952Sdhartmei int	dev;			/* pf device */
57f535f952Sdhartmei char	anchorname[PF_ANCHOR_NAME_SIZE] = "authpf";
58b06f065dSderaadt char	rulesetname[PATH_MAX - PF_ANCHOR_NAME_SIZE - 2];
59fdf28050Scedric char	tablename[PF_TABLE_NAME_SIZE] = "authpf_users";
60bdd45003Smcbride int	user_ip = 1;	/* controls whether $user_ip is set */
61f535f952Sdhartmei 
62f535f952Sdhartmei FILE	*pidfp;
631be1eb5cSderaadt int	pidfd = -1;
64b06f065dSderaadt char	 luser[LOGIN_NAME_MAX];	/* username */
65f535f952Sdhartmei char	 ipsrc[256];		/* ip as a string */
66b06f065dSderaadt char	 pidfile[PATH_MAX];	/* we save pid in this file. */
67f535f952Sdhartmei 
686f074127Scheloha struct timespec	Tstart, Tend;	/* start and end times of session */
69f535f952Sdhartmei 
70834f5058Sderaadt volatile sig_atomic_t	want_death;
71834f5058Sderaadt static void		need_death(int signo);
72834f5058Sderaadt static __dead void	do_death(int);
73bdd45003Smcbride extern char *__progname;	/* program name */
74bc4d3c8cSbeck 
75bc4d3c8cSbeck /*
76d20d8154Shenning  * User shell for authenticating gateways. Sole purpose is to allow
77bc4d3c8cSbeck  * a user to ssh to a gateway, and have the gateway modify packet
78bc4d3c8cSbeck  * filters to allow access, then remove access when the user finishes
79bc4d3c8cSbeck  * up. Meant to be used only from ssh(1) connections.
80bc4d3c8cSbeck  */
81bc4d3c8cSbeck int
82bc4d3c8cSbeck main(int argc, char *argv[])
83bc4d3c8cSbeck {
841be1eb5cSderaadt 	int		 lockcnt = 0, n;
85834f5058Sderaadt 	FILE		*config;
865318e21bSdjm 	struct in6_addr	 ina;
87834f5058Sderaadt 	struct passwd	*pw;
88834f5058Sderaadt 	char		*cp;
89b0674facSbeck 	gid_t		 gid;
90834f5058Sderaadt 	uid_t		 uid;
91b939c4c8Shenning 	char		*shell;
92b939c4c8Shenning 	login_cap_t	*lc;
93bc4d3c8cSbeck 
94bdd45003Smcbride 	if (strcmp(__progname, "-authpf-noip") == 0)
95bdd45003Smcbride                 user_ip = 0;
96bdd45003Smcbride 
97f535f952Sdhartmei 	config = fopen(PATH_CONFFILE, "r");
98bd736532Sbeck 	if (config == NULL) {
99bd736532Sbeck 		syslog(LOG_ERR, "cannot open %s (%m)", PATH_CONFFILE);
100bd736532Sbeck 		exit(1);
101bd736532Sbeck 	}
102bc4d3c8cSbeck 
103eadb1e8dSbeck 	if ((cp = getenv("SSH_TTY")) == NULL) {
104e9255b99Sderaadt 		syslog(LOG_ERR, "non-interactive session connection for authpf");
105eadb1e8dSbeck 		exit(1);
106eadb1e8dSbeck 	}
107eadb1e8dSbeck 
108834f5058Sderaadt 	if ((cp = getenv("SSH_CLIENT")) == NULL) {
109e9255b99Sderaadt 		syslog(LOG_ERR, "cannot determine connection source");
110e5199132Sbeck 		exit(1);
111bc4d3c8cSbeck 	}
112834f5058Sderaadt 
113e6c83ac4Scamield 	if (strlcpy(ipsrc, cp, sizeof(ipsrc)) >= sizeof(ipsrc)) {
114e6c83ac4Scamield 		syslog(LOG_ERR, "SSH_CLIENT variable too long");
115e6c83ac4Scamield 		exit(1);
116e6c83ac4Scamield 	}
117834f5058Sderaadt 	cp = strchr(ipsrc, ' ');
118834f5058Sderaadt 	if (!cp) {
119e9255b99Sderaadt 		syslog(LOG_ERR, "corrupt SSH_CLIENT variable %s", ipsrc);
120834f5058Sderaadt 		exit(1);
121834f5058Sderaadt 	}
122834f5058Sderaadt 	*cp = '\0';
1235318e21bSdjm 	if (inet_pton(AF_INET, ipsrc, &ina) != 1 &&
1245318e21bSdjm 	    inet_pton(AF_INET6, ipsrc, &ina) != 1) {
125834f5058Sderaadt 		syslog(LOG_ERR,
126e9255b99Sderaadt 		    "cannot determine IP from SSH_CLIENT %s", ipsrc);
127e5199132Sbeck 		exit(1);
128bc4d3c8cSbeck 	}
129834f5058Sderaadt 	/* open the pf device */
130834f5058Sderaadt 	dev = open(PATH_DEVFILE, O_RDWR);
131834f5058Sderaadt 	if (dev == -1) {
132d20d8154Shenning 		syslog(LOG_ERR, "cannot open packet filter device (%m)");
133834f5058Sderaadt 		goto die;
134bc4d3c8cSbeck 	}
135bc4d3c8cSbeck 
136834f5058Sderaadt 	uid = getuid();
137834f5058Sderaadt 	pw = getpwuid(uid);
138834f5058Sderaadt 	if (pw == NULL) {
139e9255b99Sderaadt 		syslog(LOG_ERR, "cannot find user for uid %u", uid);
140834f5058Sderaadt 		goto die;
141bc4d3c8cSbeck 	}
142b939c4c8Shenning 
143b939c4c8Shenning 	if ((lc = login_getclass(pw->pw_class)) != NULL)
144b939c4c8Shenning 		shell = login_getcapstr(lc, "shell", pw->pw_shell,
145b939c4c8Shenning 		    pw->pw_shell);
146b939c4c8Shenning 	else
147b939c4c8Shenning 		shell = pw->pw_shell;
148b939c4c8Shenning 
149b939c4c8Shenning 	login_close(lc);
150b939c4c8Shenning 
151bdd45003Smcbride 	if (strcmp(shell, PATH_AUTHPF_SHELL) &&
152bdd45003Smcbride 	    strcmp(shell, PATH_AUTHPF_SHELL_NOIP)) {
153834f5058Sderaadt 		syslog(LOG_ERR, "wrong shell for user %s, uid %u",
154834f5058Sderaadt 		    pw->pw_name, pw->pw_uid);
155b939c4c8Shenning 		if (shell != pw->pw_shell)
156b939c4c8Shenning 			free(shell);
157834f5058Sderaadt 		goto die;
158834f5058Sderaadt 	}
159834f5058Sderaadt 
160b939c4c8Shenning 	if (shell != pw->pw_shell)
161b939c4c8Shenning 		free(shell);
162b939c4c8Shenning 
163e6c83ac4Scamield 	/*
164e6c83ac4Scamield 	 * Paranoia, but this data _does_ come from outside authpf, and
165e6c83ac4Scamield 	 * truncation would be bad.
166e6c83ac4Scamield 	 */
167e6c83ac4Scamield 	if (strlcpy(luser, pw->pw_name, sizeof(luser)) >= sizeof(luser)) {
168e6c83ac4Scamield 		syslog(LOG_ERR, "username too long: %s", pw->pw_name);
169e6c83ac4Scamield 		goto die;
170e6c83ac4Scamield 	}
171bc4d3c8cSbeck 
172aa21d737Sbeck 	if ((n = snprintf(rulesetname, sizeof(rulesetname), "%s(%ld)",
1737bd6faa8Shenning 	    luser, (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) {
174aa21d737Sbeck 		syslog(LOG_INFO, "%s(%ld) too large, ruleset name will be %ld",
175aa21d737Sbeck 		    luser, (long)getpid(), (long)getpid());
176aa21d737Sbeck 		if ((n = snprintf(rulesetname, sizeof(rulesetname), "%ld",
1777bd6faa8Shenning 		    (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) {
178aa21d737Sbeck 			syslog(LOG_ERR, "pid too large for ruleset name");
179aa21d737Sbeck 			goto die;
180aa21d737Sbeck 		}
181aa21d737Sbeck 	}
182aa21d737Sbeck 
183aa21d737Sbeck 
184bdd45003Smcbride 	/* Make our entry in /var/authpf as ipaddr or username */
185bdd45003Smcbride 	n = snprintf(pidfile, sizeof(pidfile), "%s/%s",
186bdd45003Smcbride 	    PATH_PIDFILE, user_ip ? ipsrc : luser);
187e6c83ac4Scamield 	if (n < 0 || (u_int)n >= sizeof(pidfile)) {
188e6c83ac4Scamield 		syslog(LOG_ERR, "path to pidfile too long");
189e6c83ac4Scamield 		goto die;
190e6c83ac4Scamield 	}
191e6c83ac4Scamield 
1921be1eb5cSderaadt 	signal(SIGTERM, need_death);
1931be1eb5cSderaadt 	signal(SIGINT, need_death);
1941be1eb5cSderaadt 	signal(SIGALRM, need_death);
1951be1eb5cSderaadt 	signal(SIGPIPE, need_death);
1961be1eb5cSderaadt 	signal(SIGHUP, need_death);
1971be1eb5cSderaadt 	signal(SIGQUIT, need_death);
1981be1eb5cSderaadt 	signal(SIGTSTP, need_death);
1991be1eb5cSderaadt 
200bc4d3c8cSbeck 	/*
201bc4d3c8cSbeck 	 * If someone else is already using this ip, then this person
202834f5058Sderaadt 	 * wants to switch users - so kill the old process and exit
203834f5058Sderaadt 	 * as well.
204bc4d3c8cSbeck 	 *
205bc4d3c8cSbeck 	 * Note, we could print a message and tell them to log out, but the
206bc4d3c8cSbeck 	 * usual case of this is that someone has left themselves logged in,
207bc4d3c8cSbeck 	 * with the authenticated connection iconized and someone else walks
208bc4d3c8cSbeck 	 * up to use and automatically logs in before using. If this just
209bc4d3c8cSbeck 	 * gets rid of the old one silently, the new user never knows they
210bc4d3c8cSbeck 	 * could have used someone else's old authentication. If we
211bc4d3c8cSbeck 	 * tell them to log out before switching users it is an invitation
212bc4d3c8cSbeck 	 * for abuse.
213bc4d3c8cSbeck 	 */
2143067ed22Sbeck 
215834f5058Sderaadt 	do {
216834f5058Sderaadt 		int	save_errno, otherpid = -1;
217b06f065dSderaadt 		char	otherluser[LOGIN_NAME_MAX];
218bc4d3c8cSbeck 
219834f5058Sderaadt 		if ((pidfd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1 ||
220834f5058Sderaadt 		    (pidfp = fdopen(pidfd, "r+")) == NULL) {
221beed7294Svincent 			if (pidfd != -1)
222bc4d3c8cSbeck 				close(pidfd);
223e9255b99Sderaadt 			syslog(LOG_ERR, "cannot open or create %s: %s", pidfile,
224834f5058Sderaadt 			    strerror(errno));
225834f5058Sderaadt 			goto die;
226834f5058Sderaadt 		}
227834f5058Sderaadt 
228834f5058Sderaadt 		if (flock(fileno(pidfp), LOCK_EX|LOCK_NB) == 0)
229834f5058Sderaadt 			break;
230834f5058Sderaadt 		save_errno = errno;
231834f5058Sderaadt 
2323067ed22Sbeck 		/* Mark our pid, and username to our file. */
2333067ed22Sbeck 
234834f5058Sderaadt 		rewind(pidfp);
235e6c83ac4Scamield 		/* 31 == MAXLOGNAME - 1 */
236e6c83ac4Scamield 		if (fscanf(pidfp, "%d\n%31s\n", &otherpid, otherluser) != 2)
237834f5058Sderaadt 			otherpid = -1;
238e9255b99Sderaadt 		syslog(LOG_DEBUG, "tried to lock %s, in use by pid %d: %s",
239834f5058Sderaadt 		    pidfile, otherpid, strerror(save_errno));
240834f5058Sderaadt 
241bc4d3c8cSbeck 		if (otherpid > 0) {
242bc4d3c8cSbeck 			syslog(LOG_INFO,
243bc4d3c8cSbeck 			    "killing prior auth (pid %d) of %s by user %s",
2443067ed22Sbeck 			    otherpid, ipsrc, otherluser);
245bc4d3c8cSbeck 			if (kill((pid_t) otherpid, SIGTERM) == -1) {
246bc4d3c8cSbeck 				syslog(LOG_INFO,
247e9255b99Sderaadt 				    "could not kill process %d: (%m)",
248bc4d3c8cSbeck 				    otherpid);
249bc4d3c8cSbeck 			}
250bc4d3c8cSbeck 		}
251bc4d3c8cSbeck 
252684fdcdeSderaadt 		/*
2531be1eb5cSderaadt 		 * We try to kill the previous process and acquire the lock
254bc4d3c8cSbeck 		 * for 10 seconds, trying once a second. if we can't after
2551be1eb5cSderaadt 		 * 10 attempts we log an error and give up.
256bc4d3c8cSbeck 		 */
2571be1eb5cSderaadt 		if (want_death || ++lockcnt > 10) {
2581be1eb5cSderaadt 			if (!want_death)
259e9255b99Sderaadt 				syslog(LOG_ERR, "cannot kill previous authpf (pid %d)",
260bc4d3c8cSbeck 				    otherpid);
2615fba32bbSbeck 			fclose(pidfp);
2625fba32bbSbeck 			pidfp = NULL;
2631be1eb5cSderaadt 			pidfd = -1;
264bc4d3c8cSbeck 			goto dogdeath;
265bc4d3c8cSbeck 		}
266bc4d3c8cSbeck 		sleep(1);
267bc4d3c8cSbeck 
268834f5058Sderaadt 		/* re-open, and try again. The previous authpf process
269834f5058Sderaadt 		 * we killed above should unlink the file and release
2706668a736Sguenther 		 * its lock, giving us a chance to get it now
271834f5058Sderaadt 		 */
272834f5058Sderaadt 		fclose(pidfp);
2735fba32bbSbeck 		pidfp = NULL;
2741be1eb5cSderaadt 		pidfd = -1;
275834f5058Sderaadt 	} while (1);
276bc4d3c8cSbeck 
277b0674facSbeck 	/* whack the group list */
278b0674facSbeck 	gid = getegid();
279b0674facSbeck 	if (setgroups(1, &gid) == -1) {
280b0674facSbeck 		syslog(LOG_INFO, "setgroups: %s", strerror(errno));
281b0674facSbeck 		do_death(0);
282b0674facSbeck 	}
283b0674facSbeck 
284834f5058Sderaadt 	/* revoke privs */
2853e7173e3Sdjm 	uid = getuid();
2863e7173e3Sdjm 	if (setresuid(uid, uid, uid) == -1) {
2873e7173e3Sdjm 		syslog(LOG_INFO, "setresuid: %s", strerror(errno));
2883e7173e3Sdjm 		do_death(0);
2893e7173e3Sdjm 	}
290f535f952Sdhartmei 	openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON);
291834f5058Sderaadt 
2927940323aSmcbride 	if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(pw)) {
293aa21d737Sbeck 		syslog(LOG_INFO, "user %s prohibited", luser);
294cc5e4fc4Sdhartmei 		do_death(0);
295aa21d737Sbeck 	}
296aa21d737Sbeck 
297bd736532Sbeck 	if (read_config(config)) {
298bd736532Sbeck 		syslog(LOG_ERR, "invalid config file %s", PATH_CONFFILE);
299aa21d737Sbeck 		do_death(0);
300aa21d737Sbeck 	}
301aa21d737Sbeck 
302aa21d737Sbeck 	if (remove_stale_rulesets()) {
303aa21d737Sbeck 		syslog(LOG_INFO, "error removing stale rulesets");
304aa21d737Sbeck 		do_death(0);
305aa21d737Sbeck 	}
306cc5e4fc4Sdhartmei 
307834f5058Sderaadt 	/* We appear to be making headway, so actually mark our pid */
308834f5058Sderaadt 	rewind(pidfp);
3093067ed22Sbeck 	fprintf(pidfp, "%ld\n%s\n", (long)getpid(), luser);
310834f5058Sderaadt 	fflush(pidfp);
31169f3a5e2Sderaadt 	(void) ftruncate(fileno(pidfp), ftello(pidfp));
312834f5058Sderaadt 
313f535f952Sdhartmei 	if (change_filter(1, luser, ipsrc) == -1) {
314834f5058Sderaadt 		printf("Unable to modify filters\r\n");
31579cc0068Scedric 		do_death(0);
316bc4d3c8cSbeck 	}
317bdd45003Smcbride 	if (user_ip && change_table(1, ipsrc) == -1) {
318fdf28050Scedric 		printf("Unable to modify table\r\n");
319fdf28050Scedric 		change_filter(0, luser, ipsrc);
320fdf28050Scedric 		do_death(0);
321fdf28050Scedric 	}
322bc4d3c8cSbeck 
323bc4d3c8cSbeck 	while (1) {
32433c3bf74Stodd 		struct stat sb;
32533c3bf74Stodd 		char *path_message;
326613d707cSjoel 		printf("\r\nHello %s. ", luser);
327bc4d3c8cSbeck 		printf("You are authenticated from host \"%s\"\r\n", ipsrc);
328f5fcd460Sderaadt 		setproctitle("%s@%s", luser, ipsrc);
32933c3bf74Stodd 		if (asprintf(&path_message, "%s/%s/authpf.message",
33033c3bf74Stodd 		    PATH_USER_DIR, luser) == -1)
33133c3bf74Stodd 			do_death(1);
33233c3bf74Stodd 		if (stat(path_message, &sb) == -1 || ! S_ISREG(sb.st_mode)) {
33333c3bf74Stodd 			free(path_message);
33433c3bf74Stodd 			if ((path_message = strdup(PATH_MESSAGE)) == NULL)
33533c3bf74Stodd 				do_death(1);
33633c3bf74Stodd 		}
33733c3bf74Stodd 		print_message(path_message);
338bc4d3c8cSbeck 		while (1) {
339bc4d3c8cSbeck 			sleep(10);
3401f42e912Sderaadt 			if (want_death)
341834f5058Sderaadt 				do_death(1);
342bc4d3c8cSbeck 		}
343bc4d3c8cSbeck 	}
344834f5058Sderaadt 
345bc4d3c8cSbeck dogdeath:
346bc4d3c8cSbeck 	printf("\r\n\r\nSorry, this service is currently unavailable due to ");
347bc4d3c8cSbeck 	printf("technical difficulties\r\n\r\n");
348bc4d3c8cSbeck 	print_message(PATH_PROBLEM);
349984b27c3Sderaadt 	printf("\r\nYour authentication process (pid %ld) was unable to run\n",
350984b27c3Sderaadt 	    (long)getpid());
351bc4d3c8cSbeck 	sleep(180); /* them lusers read reaaaaal slow */
352834f5058Sderaadt die:
353834f5058Sderaadt 	do_death(0);
354bc4d3c8cSbeck }
355bc4d3c8cSbeck 
356684fdcdeSderaadt /*
3570b2ab063Sbeck  * reads config file in PATH_CONFFILE to set optional behaviours up
358bc4d3c8cSbeck  */
359834f5058Sderaadt static int
360834f5058Sderaadt read_config(FILE *f)
361bc4d3c8cSbeck {
362bc4d3c8cSbeck 	char	buf[1024];
363bc4d3c8cSbeck 	int	i = 0;
364e5199132Sbeck 
365bc4d3c8cSbeck 	do {
36659b66841Shenning 		char	**ap;
36759b66841Shenning 		char	 *pair[4], *cp, *tp;
368bc4d3c8cSbeck 		int	  len;
369bc4d3c8cSbeck 
370bc4d3c8cSbeck 		if (fgets(buf, sizeof(buf), f) == NULL) {
371bc4d3c8cSbeck 			fclose(f);
372834f5058Sderaadt 			return (0);
373bc4d3c8cSbeck 		}
374bc4d3c8cSbeck 		i++;
375bc4d3c8cSbeck 		len = strlen(buf);
3764b7b714fSchl 		if (len == 0)
3774b7b714fSchl 			continue;
378bc4d3c8cSbeck 		if (buf[len - 1] != '\n' && !feof(f)) {
379bc4d3c8cSbeck 			syslog(LOG_ERR, "line %d too long in %s", i,
3801f42e912Sderaadt 			    PATH_CONFFILE);
381834f5058Sderaadt 			return (1);
382bc4d3c8cSbeck 		}
383bc4d3c8cSbeck 		buf[len - 1] = '\0';
384bc4d3c8cSbeck 
385bc4d3c8cSbeck 		for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
38659b66841Shenning 			; /* nothing */
387bc4d3c8cSbeck 
388bc4d3c8cSbeck 		if (!*cp || *cp == '#' || *cp == '\n')
389bc4d3c8cSbeck 			continue;
390bc4d3c8cSbeck 
391bc4d3c8cSbeck 		for (ap = pair; ap < &pair[3] &&
392bc4d3c8cSbeck 		    (*ap = strsep(&cp, "=")) != NULL; ) {
393bc4d3c8cSbeck 			if (**ap != '\0')
394bc4d3c8cSbeck 				ap++;
395bc4d3c8cSbeck 		}
396bc4d3c8cSbeck 		if (ap != &pair[2])
397bc4d3c8cSbeck 			goto parse_error;
398bc4d3c8cSbeck 
399bc4d3c8cSbeck 		tp = pair[1] + strlen(pair[1]);
400bc4d3c8cSbeck 		while ((*tp == ' ' || *tp == '\t') && tp >= pair[1])
401bc4d3c8cSbeck 			*tp-- = '\0';
402bc4d3c8cSbeck 
403f535f952Sdhartmei 		if (strcasecmp(pair[0], "anchor") == 0) {
404f535f952Sdhartmei 			if (!pair[1][0] || strlcpy(anchorname, pair[1],
405f535f952Sdhartmei 			    sizeof(anchorname)) >= sizeof(anchorname))
406bc4d3c8cSbeck 				goto parse_error;
407bc4d3c8cSbeck 		}
408fdf28050Scedric 		if (strcasecmp(pair[0], "table") == 0) {
409fdf28050Scedric 			if (!pair[1][0] || strlcpy(tablename, pair[1],
410fdf28050Scedric 			    sizeof(tablename)) >= sizeof(tablename))
411fdf28050Scedric 				goto parse_error;
412fdf28050Scedric 		}
413bc4d3c8cSbeck 	} while (!feof(f) && !ferror(f));
414bc4d3c8cSbeck 	fclose(f);
415834f5058Sderaadt 	return (0);
41659b66841Shenning 
417bc4d3c8cSbeck parse_error:
418bc4d3c8cSbeck 	fclose(f);
4191f42e912Sderaadt 	syslog(LOG_ERR, "parse error, line %d of %s", i, PATH_CONFFILE);
420834f5058Sderaadt 	return (1);
421bc4d3c8cSbeck }
422bc4d3c8cSbeck 
423bc4d3c8cSbeck 
424bc4d3c8cSbeck /*
425bc4d3c8cSbeck  * splatter a file to stdout - max line length of 1024,
426bc4d3c8cSbeck  * used for spitting message files at users to tell them
427bc4d3c8cSbeck  * they've been bad or we're unavailable.
428bc4d3c8cSbeck  */
429bc4d3c8cSbeck static void
430bc4d3c8cSbeck print_message(char *filename)
431bc4d3c8cSbeck {
432bc4d3c8cSbeck 	char	 buf[1024];
433bc4d3c8cSbeck 	FILE	*f;
434bc4d3c8cSbeck 
435bc4d3c8cSbeck 	if ((f = fopen(filename, "r")) == NULL)
436bc4d3c8cSbeck 		return; /* fail silently, we don't care if it isn't there */
437bc4d3c8cSbeck 
438bc4d3c8cSbeck 	do {
439bc4d3c8cSbeck 		if (fgets(buf, sizeof(buf), f) == NULL) {
440bc4d3c8cSbeck 			fflush(stdout);
441bc4d3c8cSbeck 			fclose(f);
442bc4d3c8cSbeck 			return;
443bc4d3c8cSbeck 		}
444bc4d3c8cSbeck 	} while (fputs(buf, stdout) != EOF && !feof(f));
44567470ee3Sfrantzen 	fflush(stdout);
446bc4d3c8cSbeck 	fclose(f);
447bc4d3c8cSbeck }
448bc4d3c8cSbeck 
449bc4d3c8cSbeck /*
450bc4d3c8cSbeck  * allowed_luser checks to see if user "luser" is allowed to
451bc4d3c8cSbeck  * use this gateway by virtue of being listed in an allowed
4522e4c54a0Shenning  * users file, namely /etc/authpf/authpf.allow .
4537940323aSmcbride  * Users may be listed by <username>, %<group>, or @<login_class>.
454bc4d3c8cSbeck  *
4552e4c54a0Shenning  * If /etc/authpf/authpf.allow does not exist, then we assume that
456bc4d3c8cSbeck  * all users who are allowed in by sshd(8) are permitted to
4572e4c54a0Shenning  * use this gateway. If /etc/authpf/authpf.allow does exist, then a
458bc4d3c8cSbeck  * user must be listed if the connection is to continue, else
459bc4d3c8cSbeck  * the session terminates in the same manner as being banned.
460bc4d3c8cSbeck  */
461bc4d3c8cSbeck static int
4627940323aSmcbride allowed_luser(struct passwd *pw)
463bc4d3c8cSbeck {
464bc4d3c8cSbeck 	char	*buf, *lbuf;
465d81ff81eSbeck 	int	 matched;
466bc4d3c8cSbeck 	size_t	 len;
467bc4d3c8cSbeck 	FILE	*f;
468bc4d3c8cSbeck 
4691f42e912Sderaadt 	if ((f = fopen(PATH_ALLOWFILE, "r")) == NULL) {
470bc4d3c8cSbeck 		if (errno == ENOENT) {
471bc4d3c8cSbeck 			/*
4724a685e41Sbeck 			 * allowfile doesn't exist, thus this gateway
473bc4d3c8cSbeck 			 * isn't restricted to certain users...
474bc4d3c8cSbeck 			 */
475bc4d3c8cSbeck 			return (1);
476bc4d3c8cSbeck 		}
477bc4d3c8cSbeck 
478bc4d3c8cSbeck 		/*
479bc4d3c8cSbeck 		 * luser may in fact be allowed, but we can't open
480bc4d3c8cSbeck 		 * the file even though it's there. probably a config
481bc4d3c8cSbeck 		 * problem.
482bc4d3c8cSbeck 		 */
483e9255b99Sderaadt 		syslog(LOG_ERR, "cannot open allowed users file %s (%s)",
4841f42e912Sderaadt 		    PATH_ALLOWFILE, strerror(errno));
485bc4d3c8cSbeck 		return (0);
486bc4d3c8cSbeck 	} else {
487bc4d3c8cSbeck 		/*
4882e4c54a0Shenning 		 * /etc/authpf/authpf.allow exists, thus we do a linear
489bc4d3c8cSbeck 		 * search to see if they are allowed.
490bc4d3c8cSbeck 		 * also, if username "*" exists, then this is a
491bc4d3c8cSbeck 		 * "public" gateway, such as it is, so let
492bc4d3c8cSbeck 		 * everyone use it.
493bc4d3c8cSbeck 		 */
494b06f065dSderaadt 		int gl_init = 0, ngroups = NGROUPS_MAX + 1;
495b06f065dSderaadt 		gid_t groups[NGROUPS_MAX + 1];
496979e1713Smiod 
497e67ff504Stodd 		lbuf = NULL;
498979e1713Smiod 		matched = 0;
4997940323aSmcbride 
500bc4d3c8cSbeck 		while ((buf = fgetln(f, &len))) {
5017940323aSmcbride 
502bc4d3c8cSbeck 			if (buf[len - 1] == '\n')
503bc4d3c8cSbeck 				buf[len - 1] = '\0';
504bc4d3c8cSbeck 			else {
505e0e27c81Sderaadt 				if ((lbuf = malloc(len + 1)) == NULL)
506bc4d3c8cSbeck 					err(1, NULL);
507bc4d3c8cSbeck 				memcpy(lbuf, buf, len);
508bc4d3c8cSbeck 				lbuf[len] = '\0';
509bc4d3c8cSbeck 				buf = lbuf;
510bc4d3c8cSbeck 			}
511bc4d3c8cSbeck 
5127940323aSmcbride 			if (buf[0] == '@') {
5137940323aSmcbride 				/* check login class */
5147940323aSmcbride 				if (strcmp(pw->pw_class, buf + 1) == 0)
5157940323aSmcbride 					matched++;
5167940323aSmcbride 			} else if (buf[0] == '%') {
5177940323aSmcbride 				/* check group membership */
5187940323aSmcbride 				int cnt;
5197940323aSmcbride 				struct group *group;
5207940323aSmcbride 
5217940323aSmcbride 				if ((group = getgrnam(buf + 1)) == NULL) {
5227940323aSmcbride 					syslog(LOG_ERR,
5237940323aSmcbride 					    "invalid group '%s' in %s (%s)",
5247940323aSmcbride 					    buf + 1, PATH_ALLOWFILE,
5257940323aSmcbride 				 	    strerror(errno));
5263b6169f3Sclaudio 					fclose(f);
5277940323aSmcbride 					return (0);
5287940323aSmcbride 				}
5297940323aSmcbride 
5307940323aSmcbride 				if (!gl_init) {
531*2119819dSjca 					int maxgroups, ret;
532*2119819dSjca 
533*2119819dSjca 					maxgroups = ngroups;
534*2119819dSjca 					ret = getgrouplist(pw->pw_name,
5357940323aSmcbride 					    pw->pw_gid, groups, &ngroups);
536*2119819dSjca 					if (ret == -1) {
537*2119819dSjca 						/*
538*2119819dSjca 						 * Silently truncate group list
539*2119819dSjca 						 */
540*2119819dSjca 						ngroups = maxgroups;
541*2119819dSjca 					}
5427940323aSmcbride 					gl_init++;
5437940323aSmcbride 				}
5447940323aSmcbride 
5457940323aSmcbride 				for ( cnt = 0; cnt < ngroups; cnt++) {
5467940323aSmcbride 					if (group->gr_gid == groups[cnt]) {
5477940323aSmcbride 						matched++;
5487940323aSmcbride 						break;
5497940323aSmcbride 					}
5507940323aSmcbride 				}
5517940323aSmcbride 			} else {
5527940323aSmcbride 				/* check username and wildcard */
5537940323aSmcbride 				matched = strcmp(pw->pw_name, buf) == 0 ||
5547940323aSmcbride 				    strcmp("*", buf) == 0;
5557940323aSmcbride 			}
556bc4d3c8cSbeck 
557bc4d3c8cSbeck 			free(lbuf);
558bc4d3c8cSbeck 			lbuf = NULL;
559e5db98e4Smpech 
5603b6169f3Sclaudio 			if (matched) {
5613b6169f3Sclaudio 				fclose(f);
5627940323aSmcbride 				return (1); /* matched an allowed user/group */
563bc4d3c8cSbeck 			}
5643b6169f3Sclaudio 		}
565e9255b99Sderaadt 		syslog(LOG_INFO, "denied access to %s: not listed in %s",
5667940323aSmcbride 		    pw->pw_name, PATH_ALLOWFILE);
567bc4d3c8cSbeck 
568bc4d3c8cSbeck 		/* reuse buf */
569e9255b99Sderaadt 		buf = "\n\nSorry, you are not allowed to use this facility!\n";
570bc4d3c8cSbeck 		fputs(buf, stdout);
571bc4d3c8cSbeck 	}
572bc4d3c8cSbeck 	fflush(stdout);
5733b6169f3Sclaudio 	fclose(f);
574d81ff81eSbeck 	return (0);
575bc4d3c8cSbeck }
576bc4d3c8cSbeck 
577bc4d3c8cSbeck /*
578bc4d3c8cSbeck  * check_luser checks to see if user "luser" has been banned
579d81ff81eSbeck  * from using us by virtue of having an file of the same name
580bc4d3c8cSbeck  * in the "luserdir" directory.
581bc4d3c8cSbeck  *
582bc4d3c8cSbeck  * If the user has been banned, we copy the contents of the file
583bc4d3c8cSbeck  * to the user's screen. (useful for telling the user what to
584bc4d3c8cSbeck  * do to get un-banned, or just to tell them they aren't
585bc4d3c8cSbeck  * going to be un-banned.)
586bc4d3c8cSbeck  */
587bc4d3c8cSbeck static int
588bc4d3c8cSbeck check_luser(char *luserdir, char *luser)
589bc4d3c8cSbeck {
590bc4d3c8cSbeck 	FILE	*f;
591e6c83ac4Scamield 	int	 n;
592b06f065dSderaadt 	char	 tmp[PATH_MAX];
593bc4d3c8cSbeck 
594e6c83ac4Scamield 	n = snprintf(tmp, sizeof(tmp), "%s/%s", luserdir, luser);
595e6c83ac4Scamield 	if (n < 0 || (u_int)n >= sizeof(tmp)) {
596e9255b99Sderaadt 		syslog(LOG_ERR, "provided banned directory line too long (%s)",
597bc4d3c8cSbeck 		    luserdir);
598bc4d3c8cSbeck 		return (0);
599bc4d3c8cSbeck 	}
600bc4d3c8cSbeck 	if ((f = fopen(tmp, "r")) == NULL) {
601bc4d3c8cSbeck 		if (errno == ENOENT) {
602bc4d3c8cSbeck 			/*
603bc4d3c8cSbeck 			 * file or dir doesn't exist, so therefore
604bc4d3c8cSbeck 			 * this luser isn't banned..  all is well
605bc4d3c8cSbeck 			 */
606bc4d3c8cSbeck 			return (1);
607bc4d3c8cSbeck 		} else {
608bc4d3c8cSbeck 			/*
609bc4d3c8cSbeck 			 * luser may in fact be banned, but we can't open the
610bc4d3c8cSbeck 			 * file even though it's there. probably a config
611bc4d3c8cSbeck 			 * problem.
612bc4d3c8cSbeck 			 */
613e9255b99Sderaadt 			syslog(LOG_ERR, "cannot open banned file %s (%s)",
614bc4d3c8cSbeck 			    tmp, strerror(errno));
615bc4d3c8cSbeck 			return (0);
616bc4d3c8cSbeck 		}
617bc4d3c8cSbeck 	} else {
618bc4d3c8cSbeck 		/*
619bc4d3c8cSbeck 		 * luser is banned - spit the file at them to
620bc4d3c8cSbeck 		 * tell what they can do and where they can go.
621bc4d3c8cSbeck 		 */
622e9255b99Sderaadt 		syslog(LOG_INFO, "denied access to %s: %s exists",
623bc4d3c8cSbeck 		    luser, tmp);
624bc4d3c8cSbeck 
625bc4d3c8cSbeck 		/* reuse tmp */
626bc4d3c8cSbeck 		strlcpy(tmp, "\n\n-**- Sorry, you have been banned! -**-\n\n",
627bc4d3c8cSbeck 		    sizeof(tmp));
6288fa1b61fSderaadt 		while (fputs(tmp, stdout) != EOF && !feof(f)) {
629bc4d3c8cSbeck 			if (fgets(tmp, sizeof(tmp), f) == NULL) {
630bc4d3c8cSbeck 				fflush(stdout);
63159e75d46Sderaadt 				fclose(f);
632bc4d3c8cSbeck 				return (0);
633bc4d3c8cSbeck 			}
634bc4d3c8cSbeck 		}
63559e75d46Sderaadt 		fclose(f);
636bc4d3c8cSbeck 	}
637bc4d3c8cSbeck 	fflush(stdout);
638bc4d3c8cSbeck 	return (0);
639bc4d3c8cSbeck }
640bc4d3c8cSbeck 
641cc5e4fc4Sdhartmei /*
642cc5e4fc4Sdhartmei  * Search for rulesets left by other authpf processes (either because they
643cc5e4fc4Sdhartmei  * died ungracefully or were terminated) and remove them.
644cc5e4fc4Sdhartmei  */
645cc5e4fc4Sdhartmei static int
6461c00ca32Sderaadt remove_stale_rulesets(void)
647cc5e4fc4Sdhartmei {
648cc5e4fc4Sdhartmei 	struct pfioc_ruleset	 prs;
64959d5b6a4Smcbride 	u_int32_t		 nr;
650cc5e4fc4Sdhartmei 
651cc5e4fc4Sdhartmei 	memset(&prs, 0, sizeof(prs));
652d9ad7941Sdhartmei 	strlcpy(prs.path, anchorname, sizeof(prs.path));
653df69c215Sderaadt 	if (ioctl(dev, DIOCGETRULESETS, &prs) == -1) {
654cc5e4fc4Sdhartmei 		if (errno == EINVAL)
655cc5e4fc4Sdhartmei 			return (0);
656cc5e4fc4Sdhartmei 		else
657cc5e4fc4Sdhartmei 			return (1);
658cc5e4fc4Sdhartmei 	}
659cc5e4fc4Sdhartmei 
66059d5b6a4Smcbride 	nr = prs.nr;
66159d5b6a4Smcbride 	while (nr) {
662a5f84a1dSdhartmei 		char	*s, *t;
663cc5e4fc4Sdhartmei 		pid_t	 pid;
664cc5e4fc4Sdhartmei 
66559d5b6a4Smcbride 		prs.nr = nr - 1;
666df69c215Sderaadt 		if (ioctl(dev, DIOCGETRULESET, &prs) == -1)
667cc5e4fc4Sdhartmei 			return (1);
668cc5e4fc4Sdhartmei 		errno = 0;
669a5f84a1dSdhartmei 		if ((t = strchr(prs.name, '(')) == NULL)
670a5f84a1dSdhartmei 			t = prs.name;
671a5f84a1dSdhartmei 		else
672a5f84a1dSdhartmei 			t++;
673a5f84a1dSdhartmei 		pid = strtoul(t, &s, 10);
674a5f84a1dSdhartmei 		if (!prs.name[0] || errno ||
675a5f84a1dSdhartmei 		    (*s && (t == prs.name || *s != ')')))
676cc5e4fc4Sdhartmei 			return (1);
67759d5b6a4Smcbride 		if ((kill(pid, 0) && errno != EPERM) || pid == getpid()) {
67859d5b6a4Smcbride 			if (recursive_ruleset_purge(anchorname, prs.name))
679cc5e4fc4Sdhartmei 				return (1);
68059d5b6a4Smcbride 		}
68159d5b6a4Smcbride 		nr--;
682cc5e4fc4Sdhartmei 	}
683cc5e4fc4Sdhartmei 	return (0);
684cc5e4fc4Sdhartmei }
685bc4d3c8cSbeck 
68659d5b6a4Smcbride static int
68759d5b6a4Smcbride recursive_ruleset_purge(char *an, char *rs)
68859d5b6a4Smcbride {
68959d5b6a4Smcbride 	struct pfioc_trans_e     *t_e = NULL;
69059d5b6a4Smcbride 	struct pfioc_trans	 *t = NULL;
69159d5b6a4Smcbride 	struct pfioc_ruleset	 *prs = NULL;
69259d5b6a4Smcbride 
69359d5b6a4Smcbride 	/* purge rules */
69459d5b6a4Smcbride 	errno = 0;
69559d5b6a4Smcbride 	if ((t = calloc(1, sizeof(struct pfioc_trans))) == NULL)
69659d5b6a4Smcbride 		goto no_mem;
697a0a7d581Sclaudio 	if ((t_e = calloc(2, sizeof(struct pfioc_trans_e))) == NULL)
69859d5b6a4Smcbride 		goto no_mem;
699a0a7d581Sclaudio 	t->size = 2;
70059d5b6a4Smcbride 	t->esize = sizeof(struct pfioc_trans_e);
70159d5b6a4Smcbride 	t->array = t_e;
702a0a7d581Sclaudio 	t_e[0].type = PF_TRANS_RULESET;
703a0a7d581Sclaudio 	snprintf(t_e[0].anchor, sizeof(t_e[0].anchor), "%s/%s", an, rs);
704a0a7d581Sclaudio 	t_e[1].type = PF_TRANS_TABLE;
705a0a7d581Sclaudio 
706df69c215Sderaadt 	if ((ioctl(dev, DIOCXBEGIN, t) == -1||
707df69c215Sderaadt 	    ioctl(dev, DIOCXCOMMIT, t) == -1) &&
70859d5b6a4Smcbride 	    errno != EINVAL)
70959d5b6a4Smcbride 		goto cleanup;
71059d5b6a4Smcbride 
71159d5b6a4Smcbride 	/* purge any children */
71259d5b6a4Smcbride 	if ((prs = calloc(1, sizeof(struct pfioc_ruleset))) == NULL)
71359d5b6a4Smcbride 		goto no_mem;
71459d5b6a4Smcbride 	snprintf(prs->path, sizeof(prs->path), "%s/%s", an, rs);
715df69c215Sderaadt 	if (ioctl(dev, DIOCGETRULESETS, prs) == -1) {
71659d5b6a4Smcbride 		if (errno != EINVAL)
71759d5b6a4Smcbride 			goto cleanup;
71859d5b6a4Smcbride 		errno = 0;
71959d5b6a4Smcbride 	} else {
72059d5b6a4Smcbride 		int nr = prs->nr;
72159d5b6a4Smcbride 
72259d5b6a4Smcbride 		while (nr) {
72359d5b6a4Smcbride 			prs->nr = 0;
724df69c215Sderaadt 			if (ioctl(dev, DIOCGETRULESET, prs) == -1)
72559d5b6a4Smcbride 				goto cleanup;
72659d5b6a4Smcbride 
72759d5b6a4Smcbride 			if (recursive_ruleset_purge(prs->path, prs->name))
72859d5b6a4Smcbride 				goto cleanup;
72959d5b6a4Smcbride 			nr--;
73059d5b6a4Smcbride 		}
73159d5b6a4Smcbride 	}
73259d5b6a4Smcbride 
73359d5b6a4Smcbride no_mem:
73459d5b6a4Smcbride 	if (errno == ENOMEM)
73559d5b6a4Smcbride 		syslog(LOG_ERR, "calloc failed");
73659d5b6a4Smcbride 
73759d5b6a4Smcbride cleanup:
73859d5b6a4Smcbride 	free(t);
73959d5b6a4Smcbride 	free(t_e);
74059d5b6a4Smcbride 	free(prs);
74159d5b6a4Smcbride 	return (errno);
74259d5b6a4Smcbride }
74359d5b6a4Smcbride 
744bc4d3c8cSbeck /*
745bc4d3c8cSbeck  * Add/remove filter entries for user "luser" from ip "ipsrc"
746bc4d3c8cSbeck  */
747bc4d3c8cSbeck static int
748f535f952Sdhartmei change_filter(int add, const char *luser, const char *ipsrc)
749bc4d3c8cSbeck {
75007e8a33bSderaadt 	char	*fdpath = NULL, *userstr = NULL, *ipstr = NULL;
75107e8a33bSderaadt 	char	*rsn = NULL, *fn = NULL;
75207e8a33bSderaadt 	pid_t	pid;
753b0674facSbeck 	gid_t   gid;
75407e8a33bSderaadt 	int	s;
755f535f952Sdhartmei 
75659d5b6a4Smcbride 	if (add) {
75759d5b6a4Smcbride 		struct stat sb;
758e5a64f8bSbeck 		struct group *grent;
759bdd45003Smcbride 		char	*pargv[13] = {
760bdd45003Smcbride 			"pfctl", "-p", "/dev/pf", "-q", "-a", "anchor/ruleset",
761bdd45003Smcbride 			"-D", "user_id=X", "-D", "user_ip=X", "-f", "file", NULL
762bdd45003Smcbride 		};
76359d5b6a4Smcbride 
7640987cbc7Smiod 		if((grent = getgrgid(getgid())) == NULL) {
7650987cbc7Smiod 			syslog(LOG_ERR, "Group not found user %s, gid %d",
7660987cbc7Smiod 			    luser, getgid());
767c364e0ecSmestre 			goto error;
7680987cbc7Smiod 		}
7690987cbc7Smiod 
770337b3cc9Shenning 		if (luser == NULL || !luser[0] || ipsrc == NULL || !ipsrc[0]) {
771f535f952Sdhartmei 			syslog(LOG_ERR, "invalid luser/ipsrc");
772d81ff81eSbeck 			goto error;
773f535f952Sdhartmei 		}
774f535f952Sdhartmei 
775eba02a15Sdhartmei 		if (asprintf(&rsn, "%s/%s", anchorname, rulesetname) == -1)
77648f9c6d2Sbeck 			goto no_mem;
77748f9c6d2Sbeck 		if (asprintf(&fdpath, "/dev/fd/%d", dev) == -1)
77848f9c6d2Sbeck 			goto no_mem;
77948f9c6d2Sbeck 		if (asprintf(&ipstr, "user_ip=%s", ipsrc) == -1)
78048f9c6d2Sbeck 			goto no_mem;
78148f9c6d2Sbeck 		if (asprintf(&userstr, "user_id=%s", luser) == -1)
78248f9c6d2Sbeck 			goto no_mem;
78359d5b6a4Smcbride 		if (asprintf(&fn, "%s/%s/authpf.rules",
78459d5b6a4Smcbride 		    PATH_USER_DIR, luser) == -1)
78548f9c6d2Sbeck 			goto no_mem;
78648f9c6d2Sbeck 		if (stat(fn, &sb) == -1) {
78748f9c6d2Sbeck 			free(fn);
788e5a64f8bSbeck 			if(asprintf(&fn, "%s/%s/authpf.rules", PATH_GROUP_DIR,
789e5a64f8bSbeck 				grent->gr_name) == -1)
790e5a64f8bSbeck 				goto no_mem;
791e5a64f8bSbeck 			if(stat(fn, &sb) == -1) {
792e5a64f8bSbeck 				free(fn);
79348f9c6d2Sbeck 				if ((fn = strdup(PATH_PFRULES)) == NULL)
79448f9c6d2Sbeck 					goto no_mem;
79548f9c6d2Sbeck 			}
796e5a64f8bSbeck 		}
79748f9c6d2Sbeck 		pargv[2] = fdpath;
79848f9c6d2Sbeck 		pargv[5] = rsn;
79948f9c6d2Sbeck 		pargv[7] = userstr;
800bdd45003Smcbride 		if (user_ip) {
80148f9c6d2Sbeck 			pargv[9] = ipstr;
80248f9c6d2Sbeck 			pargv[11] = fn;
803bdd45003Smcbride 		} else {
804bdd45003Smcbride 			pargv[8] = "-f";
805bdd45003Smcbride 			pargv[9] = fn;
806bdd45003Smcbride 			pargv[10] = NULL;
807bdd45003Smcbride 		}
80807e8a33bSderaadt 
80948f9c6d2Sbeck 		switch (pid = fork()) {
81048f9c6d2Sbeck 		case -1:
811f6004c0dSbeck 			syslog(LOG_ERR, "fork failed");
812f6004c0dSbeck 			goto error;
81348f9c6d2Sbeck 		case 0:
814b0674facSbeck 			/* revoke group privs before exec */
815b0674facSbeck 			gid = getgid();
816f265b59bSderaadt 			if (setresgid(gid, gid, gid) == -1) {
817ab1312c3Sbeck 				err(1, "setregid");
818b0674facSbeck 			}
81948f9c6d2Sbeck 			execvp(PATH_PFCTL, pargv);
820be09db7cShenning 			warn("exec of %s failed", PATH_PFCTL);
821be09db7cShenning 			_exit(1);
82248f9c6d2Sbeck 		}
82307e8a33bSderaadt 
82448f9c6d2Sbeck 		/* parent */
82548f9c6d2Sbeck 		waitpid(pid, &s, 0);
82648f9c6d2Sbeck 		if (s != 0) {
82748f9c6d2Sbeck 			syslog(LOG_ERR, "pfctl exited abnormally");
828d81ff81eSbeck 			goto error;
829f535f952Sdhartmei 		}
830f535f952Sdhartmei 
8316f074127Scheloha 		clock_gettime(CLOCK_MONOTONIC, &Tstart);
832e9255b99Sderaadt 		syslog(LOG_INFO, "allowing %s, user %s", ipsrc, luser);
833bc4d3c8cSbeck 	} else {
83459d5b6a4Smcbride 		remove_stale_rulesets();
83559d5b6a4Smcbride 
8366f074127Scheloha 		clock_gettime(CLOCK_MONOTONIC, &Tend);
83742cfecbbSguenther 		syslog(LOG_INFO, "removed %s, user %s - duration %d seconds",
83842cfecbbSguenther 		    ipsrc, luser, (int)(Tend.tv_sec - Tstart.tv_sec));
839bc4d3c8cSbeck 	}
840d81ff81eSbeck 	return (0);
84148f9c6d2Sbeck no_mem:
84248f9c6d2Sbeck 	syslog(LOG_ERR, "malloc failed");
843d81ff81eSbeck error:
84448f9c6d2Sbeck 	free(fdpath);
84548f9c6d2Sbeck 	free(rsn);
84648f9c6d2Sbeck 	free(userstr);
84748f9c6d2Sbeck 	free(ipstr);
84848f9c6d2Sbeck 	free(fn);
849d81ff81eSbeck 	return (-1);
850bc4d3c8cSbeck }
851bc4d3c8cSbeck 
852bc4d3c8cSbeck /*
853fdf28050Scedric  * Add/remove this IP from the "authpf_users" table.
854fdf28050Scedric  */
855fdf28050Scedric static int
856f6004c0dSbeck change_table(int add, const char *ipsrc)
857fdf28050Scedric {
858fdf28050Scedric 	struct pfioc_table	io;
859fdf28050Scedric 	struct pfr_addr		addr;
860fdf28050Scedric 
861fdf28050Scedric 	bzero(&io, sizeof(io));
862c8c96bffSbeck 	strlcpy(io.pfrio_table.pfrt_name, tablename,
863c8c96bffSbeck 	    sizeof(io.pfrio_table.pfrt_name));
864fdf28050Scedric 	io.pfrio_buffer = &addr;
865fdf28050Scedric 	io.pfrio_esize = sizeof(addr);
866fdf28050Scedric 	io.pfrio_size = 1;
867fdf28050Scedric 
868fdf28050Scedric 	bzero(&addr, sizeof(addr));
869fdf28050Scedric 	if (ipsrc == NULL || !ipsrc[0])
870fdf28050Scedric 		return (-1);
871fdf28050Scedric 	if (inet_pton(AF_INET, ipsrc, &addr.pfra_ip4addr) == 1) {
872fdf28050Scedric 		addr.pfra_af = AF_INET;
873fdf28050Scedric 		addr.pfra_net = 32;
874fdf28050Scedric 	} else if (inet_pton(AF_INET6, ipsrc, &addr.pfra_ip6addr) == 1) {
875fdf28050Scedric 		addr.pfra_af = AF_INET6;
876fdf28050Scedric 		addr.pfra_net = 128;
877fdf28050Scedric 	} else {
878fdf28050Scedric 		syslog(LOG_ERR, "invalid ipsrc");
879fdf28050Scedric 		return (-1);
880fdf28050Scedric 	}
881fdf28050Scedric 
882df69c215Sderaadt 	if (ioctl(dev, add ? DIOCRADDADDRS : DIOCRDELADDRS, &io) == -1 &&
883fdf28050Scedric 	    errno != ESRCH) {
884fdf28050Scedric 		syslog(LOG_ERR, "cannot %s %s from table %s: %s",
885fdf28050Scedric 		    add ? "add" : "remove", ipsrc, tablename,
886fdf28050Scedric 		    strerror(errno));
887fdf28050Scedric 		return (-1);
888fdf28050Scedric 	}
889fdf28050Scedric 	return (0);
890fdf28050Scedric }
891fdf28050Scedric 
892fdf28050Scedric /*
89367470ee3Sfrantzen  * This is to kill off states that would otherwise be left behind stateful
894bc4d3c8cSbeck  * rules. This means we don't need to allow in more traffic than we really
895bc4d3c8cSbeck  * want to, since we don't have to worry about any luser sessions lasting
896bc4d3c8cSbeck  * longer than their ssh session. This function is based on
897bc4d3c8cSbeck  * pfctl_kill_states from pfctl.
898bc4d3c8cSbeck  */
899bc4d3c8cSbeck static void
9001c00ca32Sderaadt authpf_kill_states(void)
901bc4d3c8cSbeck {
902bc4d3c8cSbeck 	struct pfioc_state_kill	psk;
9035318e21bSdjm 	struct pf_addr target;
904bc4d3c8cSbeck 
905bc4d3c8cSbeck 	memset(&psk, 0, sizeof(psk));
9065318e21bSdjm 	memset(&target, 0, sizeof(target));
907bc4d3c8cSbeck 
9085318e21bSdjm 	if (inet_pton(AF_INET, ipsrc, &target.v4) == 1)
9095318e21bSdjm 		psk.psk_af = AF_INET;
9105318e21bSdjm 	else if (inet_pton(AF_INET6, ipsrc, &target.v6) == 1)
9115318e21bSdjm 		psk.psk_af = AF_INET6;
9125318e21bSdjm 	else {
9135318e21bSdjm 		syslog(LOG_ERR, "inet_pton(%s) failed", ipsrc);
9145318e21bSdjm 		return;
9155318e21bSdjm 	}
916bc4d3c8cSbeck 
917130aea02Sfrantzen 	/* Kill all states from ipsrc */
9185318e21bSdjm 	memcpy(&psk.psk_src.addr.v.a.addr, &target,
9195318e21bSdjm 	    sizeof(psk.psk_src.addr.v.a.addr));
920bcf142a4Sdhartmei 	memset(&psk.psk_src.addr.v.a.mask, 0xff,
921bcf142a4Sdhartmei 	    sizeof(psk.psk_src.addr.v.a.mask));
922df69c215Sderaadt 	if (ioctl(dev, DIOCKILLSTATES, &psk) == -1)
923bc4d3c8cSbeck 		syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
924bc4d3c8cSbeck 
925130aea02Sfrantzen 	/* Kill all states to ipsrc */
926130aea02Sfrantzen 	memset(&psk.psk_src, 0, sizeof(psk.psk_src));
9275318e21bSdjm 	memcpy(&psk.psk_dst.addr.v.a.addr, &target,
9285318e21bSdjm 	    sizeof(psk.psk_dst.addr.v.a.addr));
929bcf142a4Sdhartmei 	memset(&psk.psk_dst.addr.v.a.mask, 0xff,
930bcf142a4Sdhartmei 	    sizeof(psk.psk_dst.addr.v.a.mask));
931df69c215Sderaadt 	if (ioctl(dev, DIOCKILLSTATES, &psk) == -1)
932bc4d3c8cSbeck 		syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
933bc4d3c8cSbeck }
934bc4d3c8cSbeck 
935bc4d3c8cSbeck /* signal handler that makes us go away properly */
936bc4d3c8cSbeck static void
937834f5058Sderaadt need_death(int signo)
938bc4d3c8cSbeck {
9391f42e912Sderaadt 	want_death = 1;
940bc4d3c8cSbeck }
941bc4d3c8cSbeck 
942bc4d3c8cSbeck /*
943bc4d3c8cSbeck  * function that removes our stuff when we go away.
944bc4d3c8cSbeck  */
945bc4d3c8cSbeck static __dead void
946834f5058Sderaadt do_death(int active)
947bc4d3c8cSbeck {
948e5199132Sbeck 	int	ret = 0;
949bc4d3c8cSbeck 
950834f5058Sderaadt 	if (active) {
951f535f952Sdhartmei 		change_filter(0, luser, ipsrc);
952bdd45003Smcbride 		if (user_ip) {
953f6004c0dSbeck 			change_table(0, ipsrc);
954bc4d3c8cSbeck 			authpf_kill_states();
955bc4d3c8cSbeck 		}
956bdd45003Smcbride 	}
9571be1eb5cSderaadt 	if (pidfile[0] && pidfd != -1)
9583067ed22Sbeck 		if (unlink(pidfile) == -1)
959e9255b99Sderaadt 			syslog(LOG_ERR, "cannot unlink %s (%m)", pidfile);
960bc4d3c8cSbeck 	exit(ret);
961bc4d3c8cSbeck }
962