xref: /dflybsd-src/usr.sbin/authpf/authpf.c (revision 2c3b1d1bc3a233e4bf80218452e719882a1bb011)
1d316f7c9SJohn Marino /*	$OpenBSD: authpf.c,v 1.112 2009/01/10 19:08:53 miod Exp $	*/
295cc27f0SJoerg Sonnenberger 
395cc27f0SJoerg Sonnenberger /*
470224baaSJan Lentfer  * Copyright (C) 1998 - 2007 Bob Beck (beck@openbsd.org).
595cc27f0SJoerg Sonnenberger  *
6d316f7c9SJohn Marino  * Permission to use, copy, modify, and distribute this software for any
7d316f7c9SJohn Marino  * purpose with or without fee is hereby granted, provided that the above
8d316f7c9SJohn Marino  * copyright notice and this permission notice appear in all copies.
995cc27f0SJoerg Sonnenberger  *
10d316f7c9SJohn Marino  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d316f7c9SJohn Marino  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d316f7c9SJohn Marino  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d316f7c9SJohn Marino  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d316f7c9SJohn Marino  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d316f7c9SJohn Marino  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d316f7c9SJohn Marino  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d316f7c9SJohn Marino  *
18d316f7c9SJohn Marino  * $FreeBSD: head/contrib/pf/authpf/authpf.c 223637 2011-06-28 11:57:25Z bz $
1995cc27f0SJoerg Sonnenberger  */
2095cc27f0SJoerg Sonnenberger 
2195cc27f0SJoerg Sonnenberger #include <sys/param.h>
2295cc27f0SJoerg Sonnenberger #include <sys/types.h>
2395cc27f0SJoerg Sonnenberger #include <sys/ioctl.h>
2495cc27f0SJoerg Sonnenberger #include <sys/socket.h>
2570224baaSJan Lentfer #include <sys/stat.h>
2695cc27f0SJoerg Sonnenberger #include <sys/time.h>
2770224baaSJan Lentfer #include <sys/wait.h>
2895cc27f0SJoerg Sonnenberger 
2995cc27f0SJoerg Sonnenberger #include <net/if.h>
3095cc27f0SJoerg Sonnenberger #include <net/pf/pfvar.h>
3195cc27f0SJoerg Sonnenberger #include <arpa/inet.h>
3295cc27f0SJoerg Sonnenberger 
3395cc27f0SJoerg Sonnenberger #include <err.h>
3495cc27f0SJoerg Sonnenberger #include <errno.h>
35*2c3b1d1bSSascha Wildner #include <fcntl.h>
3670224baaSJan Lentfer #include <login_cap.h>
3795cc27f0SJoerg Sonnenberger #include <pwd.h>
3895cc27f0SJoerg Sonnenberger #include <signal.h>
3995cc27f0SJoerg Sonnenberger #include <stdio.h>
4095cc27f0SJoerg Sonnenberger #include <stdlib.h>
4195cc27f0SJoerg Sonnenberger #include <string.h>
4295cc27f0SJoerg Sonnenberger #include <syslog.h>
4395cc27f0SJoerg Sonnenberger #include <unistd.h>
4495cc27f0SJoerg Sonnenberger 
4595cc27f0SJoerg Sonnenberger #include "pathnames.h"
4695cc27f0SJoerg Sonnenberger 
4795cc27f0SJoerg Sonnenberger #define __dead __dead2
4895cc27f0SJoerg Sonnenberger 
4995cc27f0SJoerg Sonnenberger static int	read_config(FILE *);
5095cc27f0SJoerg Sonnenberger static void	print_message(const char *);
5195cc27f0SJoerg Sonnenberger static int	allowed_luser(const char *);
5295cc27f0SJoerg Sonnenberger static int	check_luser(const char *, const char *);
5395cc27f0SJoerg Sonnenberger static int	remove_stale_rulesets(void);
5495cc27f0SJoerg Sonnenberger static int	change_filter(int, const char *, const char *);
5570224baaSJan Lentfer static int	change_table(int, const char *);
5695cc27f0SJoerg Sonnenberger static void	authpf_kill_states(void);
5795cc27f0SJoerg Sonnenberger 
5895cc27f0SJoerg Sonnenberger int	dev_fd;			/* pf device */
5995cc27f0SJoerg Sonnenberger char	anchorname[PF_ANCHOR_NAME_SIZE] = "authpf";
6070224baaSJan Lentfer char	rulesetname[MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 2];
6170224baaSJan Lentfer char	tablename[PF_TABLE_NAME_SIZE] = "authpf_users";
6295cc27f0SJoerg Sonnenberger 
6395cc27f0SJoerg Sonnenberger FILE	*pidfp;
6495cc27f0SJoerg Sonnenberger char	 luser[MAXLOGNAME];	/* username */
6595cc27f0SJoerg Sonnenberger char	 ipsrc[256];		/* ip as a string */
6695cc27f0SJoerg Sonnenberger char	 pidfile[MAXPATHLEN];	/* we save pid in this file. */
6795cc27f0SJoerg Sonnenberger 
6895cc27f0SJoerg Sonnenberger struct timeval	Tstart, Tend;	/* start and end times of session */
6995cc27f0SJoerg Sonnenberger 
7095cc27f0SJoerg Sonnenberger volatile sig_atomic_t	want_death;
7195cc27f0SJoerg Sonnenberger static void		need_death(int signo);
7295cc27f0SJoerg Sonnenberger static __dead void	do_death(int);
7395cc27f0SJoerg Sonnenberger 
7495cc27f0SJoerg Sonnenberger /*
7595cc27f0SJoerg Sonnenberger  * User shell for authenticating gateways. Sole purpose is to allow
7695cc27f0SJoerg Sonnenberger  * a user to ssh to a gateway, and have the gateway modify packet
7795cc27f0SJoerg Sonnenberger  * filters to allow access, then remove access when the user finishes
7895cc27f0SJoerg Sonnenberger  * up. Meant to be used only from ssh(1) connections.
7995cc27f0SJoerg Sonnenberger  */
8095cc27f0SJoerg Sonnenberger int
main(int argc __unused,char ** argv __unused)8195cc27f0SJoerg Sonnenberger main(int argc __unused, char **argv __unused)
8295cc27f0SJoerg Sonnenberger {
8395cc27f0SJoerg Sonnenberger 	int		 lockcnt = 0, n, pidfd;
8495cc27f0SJoerg Sonnenberger 	FILE		*config;
8570224baaSJan Lentfer 	struct in6_addr	 ina;
8695cc27f0SJoerg Sonnenberger 	struct passwd	*pw;
8795cc27f0SJoerg Sonnenberger 	char		*cp;
8870224baaSJan Lentfer 	gid_t		 gid;
8995cc27f0SJoerg Sonnenberger 	uid_t		 uid;
90d316f7c9SJohn Marino 	const char	*shell;
9170224baaSJan Lentfer 	login_cap_t	*lc;
9295cc27f0SJoerg Sonnenberger 
9395cc27f0SJoerg Sonnenberger 	config = fopen(PATH_CONFFILE, "r");
9470224baaSJan Lentfer 	if (config == NULL) {
9570224baaSJan Lentfer 		syslog(LOG_ERR, "cannot open %s (%m)", PATH_CONFFILE);
9670224baaSJan Lentfer 		exit(1);
9770224baaSJan Lentfer 	}
9895cc27f0SJoerg Sonnenberger 
9995cc27f0SJoerg Sonnenberger 	if ((cp = getenv("SSH_TTY")) == NULL) {
10095cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "non-interactive session connection for authpf");
10195cc27f0SJoerg Sonnenberger 		exit(1);
10295cc27f0SJoerg Sonnenberger 	}
10395cc27f0SJoerg Sonnenberger 
10495cc27f0SJoerg Sonnenberger 	if ((cp = getenv("SSH_CLIENT")) == NULL) {
10595cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "cannot determine connection source");
10695cc27f0SJoerg Sonnenberger 		exit(1);
10795cc27f0SJoerg Sonnenberger 	}
10895cc27f0SJoerg Sonnenberger 
10995cc27f0SJoerg Sonnenberger 	if (strlcpy(ipsrc, cp, sizeof(ipsrc)) >= sizeof(ipsrc)) {
11095cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "SSH_CLIENT variable too long");
11195cc27f0SJoerg Sonnenberger 		exit(1);
11295cc27f0SJoerg Sonnenberger 	}
11395cc27f0SJoerg Sonnenberger 	cp = strchr(ipsrc, ' ');
11495cc27f0SJoerg Sonnenberger 	if (!cp) {
11595cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "corrupt SSH_CLIENT variable %s", ipsrc);
11695cc27f0SJoerg Sonnenberger 		exit(1);
11795cc27f0SJoerg Sonnenberger 	}
11895cc27f0SJoerg Sonnenberger 	*cp = '\0';
11970224baaSJan Lentfer 	if (inet_pton(AF_INET, ipsrc, &ina) != 1 &&
12070224baaSJan Lentfer 	    inet_pton(AF_INET6, ipsrc, &ina) != 1) {
12195cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR,
12295cc27f0SJoerg Sonnenberger 		    "cannot determine IP from SSH_CLIENT %s", ipsrc);
12395cc27f0SJoerg Sonnenberger 		exit(1);
12495cc27f0SJoerg Sonnenberger 	}
12595cc27f0SJoerg Sonnenberger 	/* open the pf device */
12695cc27f0SJoerg Sonnenberger 	dev_fd = open(PATH_DEVFILE, O_RDWR);
12795cc27f0SJoerg Sonnenberger 	if (dev_fd == -1) {
12895cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "cannot open packet filter device (%m)");
12995cc27f0SJoerg Sonnenberger 		goto die;
13095cc27f0SJoerg Sonnenberger 	}
13195cc27f0SJoerg Sonnenberger 
13295cc27f0SJoerg Sonnenberger 	uid = getuid();
13395cc27f0SJoerg Sonnenberger 	pw = getpwuid(uid);
13495cc27f0SJoerg Sonnenberger 	if (pw == NULL) {
13595cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "cannot find user for uid %u", uid);
13695cc27f0SJoerg Sonnenberger 		goto die;
13795cc27f0SJoerg Sonnenberger 	}
13870224baaSJan Lentfer 
13970224baaSJan Lentfer 	if ((lc = login_getclass(pw->pw_class)) != NULL)
14070224baaSJan Lentfer 		shell = login_getcapstr(lc, "shell", pw->pw_shell,
14170224baaSJan Lentfer 		    pw->pw_shell);
14270224baaSJan Lentfer 	else
14370224baaSJan Lentfer 		shell = pw->pw_shell;
14470224baaSJan Lentfer 
145d316f7c9SJohn Marino 
14670224baaSJan Lentfer 
14770224baaSJan Lentfer 	if (strcmp(shell, PATH_AUTHPF_SHELL)) {
14895cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "wrong shell for user %s, uid %u",
14995cc27f0SJoerg Sonnenberger 		    pw->pw_name, pw->pw_uid);
150d316f7c9SJohn Marino 		login_close(lc);
15195cc27f0SJoerg Sonnenberger 		goto die;
15295cc27f0SJoerg Sonnenberger 	}
15395cc27f0SJoerg Sonnenberger 
154d316f7c9SJohn Marino 	login_close(lc);
15570224baaSJan Lentfer 
15695cc27f0SJoerg Sonnenberger 	/*
15795cc27f0SJoerg Sonnenberger 	 * Paranoia, but this data _does_ come from outside authpf, and
15895cc27f0SJoerg Sonnenberger 	 * truncation would be bad.
15995cc27f0SJoerg Sonnenberger 	 */
16095cc27f0SJoerg Sonnenberger 	if (strlcpy(luser, pw->pw_name, sizeof(luser)) >= sizeof(luser)) {
16195cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "username too long: %s", pw->pw_name);
16295cc27f0SJoerg Sonnenberger 		goto die;
16395cc27f0SJoerg Sonnenberger 	}
16495cc27f0SJoerg Sonnenberger 
16595cc27f0SJoerg Sonnenberger 	if ((n = snprintf(rulesetname, sizeof(rulesetname), "%s(%ld)",
166d316f7c9SJohn Marino 	    luser, (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) {
16795cc27f0SJoerg Sonnenberger 		syslog(LOG_INFO, "%s(%ld) too large, ruleset name will be %ld",
16895cc27f0SJoerg Sonnenberger 		    luser, (long)getpid(), (long)getpid());
16995cc27f0SJoerg Sonnenberger 		if ((n = snprintf(rulesetname, sizeof(rulesetname), "%ld",
170d316f7c9SJohn Marino 		    (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) {
17195cc27f0SJoerg Sonnenberger 			syslog(LOG_ERR, "pid too large for ruleset name");
17295cc27f0SJoerg Sonnenberger 			goto die;
17395cc27f0SJoerg Sonnenberger 		}
17495cc27f0SJoerg Sonnenberger 	}
17595cc27f0SJoerg Sonnenberger 
17695cc27f0SJoerg Sonnenberger 
17795cc27f0SJoerg Sonnenberger 	/* Make our entry in /var/authpf as /var/authpf/ipaddr */
17895cc27f0SJoerg Sonnenberger 	n = snprintf(pidfile, sizeof(pidfile), "%s/%s", PATH_PIDFILE, ipsrc);
17995cc27f0SJoerg Sonnenberger 	if (n < 0 || (u_int)n >= sizeof(pidfile)) {
18095cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "path to pidfile too long");
18195cc27f0SJoerg Sonnenberger 		goto die;
18295cc27f0SJoerg Sonnenberger 	}
18395cc27f0SJoerg Sonnenberger 
18495cc27f0SJoerg Sonnenberger 	/*
18595cc27f0SJoerg Sonnenberger 	 * If someone else is already using this ip, then this person
18695cc27f0SJoerg Sonnenberger 	 * wants to switch users - so kill the old process and exit
18795cc27f0SJoerg Sonnenberger 	 * as well.
18895cc27f0SJoerg Sonnenberger 	 *
18995cc27f0SJoerg Sonnenberger 	 * Note, we could print a message and tell them to log out, but the
19095cc27f0SJoerg Sonnenberger 	 * usual case of this is that someone has left themselves logged in,
19195cc27f0SJoerg Sonnenberger 	 * with the authenticated connection iconized and someone else walks
19295cc27f0SJoerg Sonnenberger 	 * up to use and automatically logs in before using. If this just
19395cc27f0SJoerg Sonnenberger 	 * gets rid of the old one silently, the new user never knows they
19495cc27f0SJoerg Sonnenberger 	 * could have used someone else's old authentication. If we
19595cc27f0SJoerg Sonnenberger 	 * tell them to log out before switching users it is an invitation
19695cc27f0SJoerg Sonnenberger 	 * for abuse.
19795cc27f0SJoerg Sonnenberger 	 */
19895cc27f0SJoerg Sonnenberger 
19995cc27f0SJoerg Sonnenberger 	do {
20095cc27f0SJoerg Sonnenberger 		int	save_errno, otherpid = -1;
20195cc27f0SJoerg Sonnenberger 		char	otherluser[MAXLOGNAME];
20295cc27f0SJoerg Sonnenberger 
20395cc27f0SJoerg Sonnenberger 		if ((pidfd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1 ||
20495cc27f0SJoerg Sonnenberger 		    (pidfp = fdopen(pidfd, "r+")) == NULL) {
20595cc27f0SJoerg Sonnenberger 			if (pidfd != -1)
20695cc27f0SJoerg Sonnenberger 				close(pidfd);
20795cc27f0SJoerg Sonnenberger 			syslog(LOG_ERR, "cannot open or create %s: %s", pidfile,
20895cc27f0SJoerg Sonnenberger 			    strerror(errno));
20995cc27f0SJoerg Sonnenberger 			goto die;
21095cc27f0SJoerg Sonnenberger 		}
21195cc27f0SJoerg Sonnenberger 
21295cc27f0SJoerg Sonnenberger 		if (flock(fileno(pidfp), LOCK_EX|LOCK_NB) == 0)
21395cc27f0SJoerg Sonnenberger 			break;
21495cc27f0SJoerg Sonnenberger 		save_errno = errno;
21595cc27f0SJoerg Sonnenberger 
21695cc27f0SJoerg Sonnenberger 		/* Mark our pid, and username to our file. */
21795cc27f0SJoerg Sonnenberger 
21895cc27f0SJoerg Sonnenberger 		rewind(pidfp);
21995cc27f0SJoerg Sonnenberger 		/* 31 == MAXLOGNAME - 1 */
22095cc27f0SJoerg Sonnenberger 		if (fscanf(pidfp, "%d\n%31s\n", &otherpid, otherluser) != 2)
22195cc27f0SJoerg Sonnenberger 			otherpid = -1;
22295cc27f0SJoerg Sonnenberger 		syslog(LOG_DEBUG, "tried to lock %s, in use by pid %d: %s",
22395cc27f0SJoerg Sonnenberger 		    pidfile, otherpid, strerror(save_errno));
22495cc27f0SJoerg Sonnenberger 
22595cc27f0SJoerg Sonnenberger 		if (otherpid > 0) {
22695cc27f0SJoerg Sonnenberger 			syslog(LOG_INFO,
22795cc27f0SJoerg Sonnenberger 			    "killing prior auth (pid %d) of %s by user %s",
22895cc27f0SJoerg Sonnenberger 			    otherpid, ipsrc, otherluser);
22995cc27f0SJoerg Sonnenberger 			if (kill((pid_t) otherpid, SIGTERM) == -1) {
23095cc27f0SJoerg Sonnenberger 				syslog(LOG_INFO,
23195cc27f0SJoerg Sonnenberger 				    "could not kill process %d: (%m)",
23295cc27f0SJoerg Sonnenberger 				    otherpid);
23395cc27f0SJoerg Sonnenberger 			}
23495cc27f0SJoerg Sonnenberger 		}
23595cc27f0SJoerg Sonnenberger 
23695cc27f0SJoerg Sonnenberger 		/*
237d316f7c9SJohn Marino 		 * We try to kill the previous process and acquire the lock
23895cc27f0SJoerg Sonnenberger 		 * for 10 seconds, trying once a second. if we can't after
239d316f7c9SJohn Marino 		 * 10 attempts we log an error and give up.
24095cc27f0SJoerg Sonnenberger 		 */
24195cc27f0SJoerg Sonnenberger 		if (++lockcnt > 10) {
24295cc27f0SJoerg Sonnenberger 			syslog(LOG_ERR, "cannot kill previous authpf (pid %d)",
24395cc27f0SJoerg Sonnenberger 			    otherpid);
24470224baaSJan Lentfer 			fclose(pidfp);
24570224baaSJan Lentfer 			pidfp = NULL;
24695cc27f0SJoerg Sonnenberger 			goto dogdeath;
24795cc27f0SJoerg Sonnenberger 		}
24895cc27f0SJoerg Sonnenberger 		sleep(1);
24995cc27f0SJoerg Sonnenberger 
25095cc27f0SJoerg Sonnenberger 		/* re-open, and try again. The previous authpf process
25195cc27f0SJoerg Sonnenberger 		 * we killed above should unlink the file and release
25295cc27f0SJoerg Sonnenberger 		 * it's lock, giving us a chance to get it now
25395cc27f0SJoerg Sonnenberger 		 */
25495cc27f0SJoerg Sonnenberger 		fclose(pidfp);
25570224baaSJan Lentfer 		pidfp = NULL;
25695cc27f0SJoerg Sonnenberger 	} while (1);
25795cc27f0SJoerg Sonnenberger 
25870224baaSJan Lentfer 	/* whack the group list */
25970224baaSJan Lentfer 	gid = getegid();
26070224baaSJan Lentfer 	if (setgroups(1, &gid) == -1) {
26170224baaSJan Lentfer 		syslog(LOG_INFO, "setgroups: %s", strerror(errno));
26270224baaSJan Lentfer 		do_death(0);
26370224baaSJan Lentfer 	}
26495cc27f0SJoerg Sonnenberger 
26570224baaSJan Lentfer 	/* revoke privs */
26670224baaSJan Lentfer 	uid = getuid();
26770224baaSJan Lentfer 	if (setresuid(uid, uid, uid) == -1) {
26870224baaSJan Lentfer 		syslog(LOG_INFO, "setresuid: %s", strerror(errno));
26970224baaSJan Lentfer 		do_death(0);
27070224baaSJan Lentfer 	}
27195cc27f0SJoerg Sonnenberger 	openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON);
27295cc27f0SJoerg Sonnenberger 
27395cc27f0SJoerg Sonnenberger 	if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(luser)) {
27495cc27f0SJoerg Sonnenberger 		syslog(LOG_INFO, "user %s prohibited", luser);
27595cc27f0SJoerg Sonnenberger 		do_death(0);
27695cc27f0SJoerg Sonnenberger 	}
27795cc27f0SJoerg Sonnenberger 
27870224baaSJan Lentfer 	if (read_config(config)) {
27970224baaSJan Lentfer 		syslog(LOG_ERR, "invalid config file %s", PATH_CONFFILE);
28095cc27f0SJoerg Sonnenberger 		do_death(0);
28195cc27f0SJoerg Sonnenberger 	}
28295cc27f0SJoerg Sonnenberger 
28395cc27f0SJoerg Sonnenberger 	if (remove_stale_rulesets()) {
28495cc27f0SJoerg Sonnenberger 		syslog(LOG_INFO, "error removing stale rulesets");
28595cc27f0SJoerg Sonnenberger 		do_death(0);
28695cc27f0SJoerg Sonnenberger 	}
28795cc27f0SJoerg Sonnenberger 
28895cc27f0SJoerg Sonnenberger 	/* We appear to be making headway, so actually mark our pid */
28995cc27f0SJoerg Sonnenberger 	rewind(pidfp);
29095cc27f0SJoerg Sonnenberger 	fprintf(pidfp, "%ld\n%s\n", (long)getpid(), luser);
29195cc27f0SJoerg Sonnenberger 	fflush(pidfp);
29271126e33SSascha Wildner 	ftruncate(fileno(pidfp), ftell(pidfp));
29395cc27f0SJoerg Sonnenberger 
29495cc27f0SJoerg Sonnenberger 	if (change_filter(1, luser, ipsrc) == -1) {
29595cc27f0SJoerg Sonnenberger 		printf("Unable to modify filters\r\n");
29695cc27f0SJoerg Sonnenberger 		do_death(0);
29795cc27f0SJoerg Sonnenberger 	}
29870224baaSJan Lentfer 	if (change_table(1, ipsrc) == -1) {
29970224baaSJan Lentfer 		printf("Unable to modify table\r\n");
30070224baaSJan Lentfer 		change_filter(0, luser, ipsrc);
30170224baaSJan Lentfer 		do_death(0);
30270224baaSJan Lentfer 	}
30395cc27f0SJoerg Sonnenberger 
30495cc27f0SJoerg Sonnenberger 	signal(SIGTERM, need_death);
30595cc27f0SJoerg Sonnenberger 	signal(SIGINT, need_death);
30695cc27f0SJoerg Sonnenberger 	signal(SIGALRM, need_death);
30795cc27f0SJoerg Sonnenberger 	signal(SIGPIPE, need_death);
30895cc27f0SJoerg Sonnenberger 	signal(SIGHUP, need_death);
30970224baaSJan Lentfer 	signal(SIGQUIT, need_death);
31095cc27f0SJoerg Sonnenberger 	signal(SIGTSTP, need_death);
31195cc27f0SJoerg Sonnenberger 	while (1) {
31270224baaSJan Lentfer 		printf("\r\nHello %s. ", luser);
31395cc27f0SJoerg Sonnenberger 		printf("You are authenticated from host \"%s\"\r\n", ipsrc);
31495cc27f0SJoerg Sonnenberger 		setproctitle("%s@%s", luser, ipsrc);
31595cc27f0SJoerg Sonnenberger 		print_message(PATH_MESSAGE);
31695cc27f0SJoerg Sonnenberger 		while (1) {
31795cc27f0SJoerg Sonnenberger 			sleep(10);
31895cc27f0SJoerg Sonnenberger 			if (want_death)
31995cc27f0SJoerg Sonnenberger 				do_death(1);
32095cc27f0SJoerg Sonnenberger 		}
32195cc27f0SJoerg Sonnenberger 	}
32295cc27f0SJoerg Sonnenberger 
32395cc27f0SJoerg Sonnenberger 	/* NOTREACHED */
32495cc27f0SJoerg Sonnenberger dogdeath:
32595cc27f0SJoerg Sonnenberger 	printf("\r\n\r\nSorry, this service is currently unavailable due to ");
32695cc27f0SJoerg Sonnenberger 	printf("technical difficulties\r\n\r\n");
32795cc27f0SJoerg Sonnenberger 	print_message(PATH_PROBLEM);
32895cc27f0SJoerg Sonnenberger 	printf("\r\nYour authentication process (pid %ld) was unable to run\n",
32995cc27f0SJoerg Sonnenberger 	    (long)getpid());
33095cc27f0SJoerg Sonnenberger 	sleep(180); /* them lusers read reaaaaal slow */
33195cc27f0SJoerg Sonnenberger die:
33295cc27f0SJoerg Sonnenberger 	do_death(0);
33395cc27f0SJoerg Sonnenberger }
33495cc27f0SJoerg Sonnenberger 
33595cc27f0SJoerg Sonnenberger /*
33695cc27f0SJoerg Sonnenberger  * reads config file in PATH_CONFFILE to set optional behaviours up
33795cc27f0SJoerg Sonnenberger  */
33895cc27f0SJoerg Sonnenberger static int
read_config(FILE * f)33995cc27f0SJoerg Sonnenberger read_config(FILE *f)
34095cc27f0SJoerg Sonnenberger {
34195cc27f0SJoerg Sonnenberger 	char	buf[1024];
34295cc27f0SJoerg Sonnenberger 	int	i = 0;
34395cc27f0SJoerg Sonnenberger 
34495cc27f0SJoerg Sonnenberger 	do {
34595cc27f0SJoerg Sonnenberger 		char	**ap;
34695cc27f0SJoerg Sonnenberger 		char	 *pair[4], *cp, *tp;
34795cc27f0SJoerg Sonnenberger 		int	  len;
34895cc27f0SJoerg Sonnenberger 
34995cc27f0SJoerg Sonnenberger 		if (fgets(buf, sizeof(buf), f) == NULL) {
35095cc27f0SJoerg Sonnenberger 			fclose(f);
35195cc27f0SJoerg Sonnenberger 			return (0);
35295cc27f0SJoerg Sonnenberger 		}
35395cc27f0SJoerg Sonnenberger 		i++;
35495cc27f0SJoerg Sonnenberger 		len = strlen(buf);
355d316f7c9SJohn Marino 		if (len == 0)
356d316f7c9SJohn Marino 			continue;
35795cc27f0SJoerg Sonnenberger 		if (buf[len - 1] != '\n' && !feof(f)) {
35895cc27f0SJoerg Sonnenberger 			syslog(LOG_ERR, "line %d too long in %s", i,
35995cc27f0SJoerg Sonnenberger 			    PATH_CONFFILE);
36095cc27f0SJoerg Sonnenberger 			return (1);
36195cc27f0SJoerg Sonnenberger 		}
36295cc27f0SJoerg Sonnenberger 		buf[len - 1] = '\0';
36395cc27f0SJoerg Sonnenberger 
36495cc27f0SJoerg Sonnenberger 		for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
36595cc27f0SJoerg Sonnenberger 			; /* nothing */
36695cc27f0SJoerg Sonnenberger 
36795cc27f0SJoerg Sonnenberger 		if (!*cp || *cp == '#' || *cp == '\n')
36895cc27f0SJoerg Sonnenberger 			continue;
36995cc27f0SJoerg Sonnenberger 
37095cc27f0SJoerg Sonnenberger 		for (ap = pair; ap < &pair[3] &&
37195cc27f0SJoerg Sonnenberger 		    (*ap = strsep(&cp, "=")) != NULL; ) {
37295cc27f0SJoerg Sonnenberger 			if (**ap != '\0')
37395cc27f0SJoerg Sonnenberger 				ap++;
37495cc27f0SJoerg Sonnenberger 		}
37595cc27f0SJoerg Sonnenberger 		if (ap != &pair[2])
37695cc27f0SJoerg Sonnenberger 			goto parse_error;
37795cc27f0SJoerg Sonnenberger 
37895cc27f0SJoerg Sonnenberger 		tp = pair[1] + strlen(pair[1]);
37995cc27f0SJoerg Sonnenberger 		while ((*tp == ' ' || *tp == '\t') && tp >= pair[1])
38095cc27f0SJoerg Sonnenberger 			*tp-- = '\0';
38195cc27f0SJoerg Sonnenberger 
38295cc27f0SJoerg Sonnenberger 		if (strcasecmp(pair[0], "anchor") == 0) {
38395cc27f0SJoerg Sonnenberger 			if (!pair[1][0] || strlcpy(anchorname, pair[1],
38495cc27f0SJoerg Sonnenberger 			    sizeof(anchorname)) >= sizeof(anchorname))
38595cc27f0SJoerg Sonnenberger 				goto parse_error;
38695cc27f0SJoerg Sonnenberger 		}
38770224baaSJan Lentfer 		if (strcasecmp(pair[0], "table") == 0) {
38870224baaSJan Lentfer 			if (!pair[1][0] || strlcpy(tablename, pair[1],
38970224baaSJan Lentfer 			    sizeof(tablename)) >= sizeof(tablename))
39070224baaSJan Lentfer 				goto parse_error;
39170224baaSJan Lentfer 		}
39295cc27f0SJoerg Sonnenberger 	} while (!feof(f) && !ferror(f));
39395cc27f0SJoerg Sonnenberger 	fclose(f);
39495cc27f0SJoerg Sonnenberger 	return (0);
39595cc27f0SJoerg Sonnenberger 
39695cc27f0SJoerg Sonnenberger parse_error:
39795cc27f0SJoerg Sonnenberger 	fclose(f);
39895cc27f0SJoerg Sonnenberger 	syslog(LOG_ERR, "parse error, line %d of %s", i, PATH_CONFFILE);
39995cc27f0SJoerg Sonnenberger 	return (1);
40095cc27f0SJoerg Sonnenberger }
40195cc27f0SJoerg Sonnenberger 
40295cc27f0SJoerg Sonnenberger 
40395cc27f0SJoerg Sonnenberger /*
40495cc27f0SJoerg Sonnenberger  * splatter a file to stdout - max line length of 1024,
40595cc27f0SJoerg Sonnenberger  * used for spitting message files at users to tell them
40695cc27f0SJoerg Sonnenberger  * they've been bad or we're unavailable.
40795cc27f0SJoerg Sonnenberger  */
40895cc27f0SJoerg Sonnenberger static void
print_message(const char * filename)40995cc27f0SJoerg Sonnenberger print_message(const char *filename)
41095cc27f0SJoerg Sonnenberger {
41195cc27f0SJoerg Sonnenberger 	char	 buf[1024];
41295cc27f0SJoerg Sonnenberger 	FILE	*f;
41395cc27f0SJoerg Sonnenberger 
41495cc27f0SJoerg Sonnenberger 	if ((f = fopen(filename, "r")) == NULL)
41595cc27f0SJoerg Sonnenberger 		return; /* fail silently, we don't care if it isn't there */
41695cc27f0SJoerg Sonnenberger 
41795cc27f0SJoerg Sonnenberger 	do {
41895cc27f0SJoerg Sonnenberger 		if (fgets(buf, sizeof(buf), f) == NULL) {
41995cc27f0SJoerg Sonnenberger 			fflush(stdout);
42095cc27f0SJoerg Sonnenberger 			fclose(f);
42195cc27f0SJoerg Sonnenberger 			return;
42295cc27f0SJoerg Sonnenberger 		}
42395cc27f0SJoerg Sonnenberger 	} while (fputs(buf, stdout) != EOF && !feof(f));
42495cc27f0SJoerg Sonnenberger 	fflush(stdout);
42595cc27f0SJoerg Sonnenberger 	fclose(f);
42695cc27f0SJoerg Sonnenberger }
42795cc27f0SJoerg Sonnenberger 
42895cc27f0SJoerg Sonnenberger /*
42995cc27f0SJoerg Sonnenberger  * allowed_luser checks to see if user "luser" is allowed to
43095cc27f0SJoerg Sonnenberger  * use this gateway by virtue of being listed in an allowed
43195cc27f0SJoerg Sonnenberger  * users file, namely /etc/authpf/authpf.allow .
43295cc27f0SJoerg Sonnenberger  *
43395cc27f0SJoerg Sonnenberger  * If /etc/authpf/authpf.allow does not exist, then we assume that
43495cc27f0SJoerg Sonnenberger  * all users who are allowed in by sshd(8) are permitted to
43595cc27f0SJoerg Sonnenberger  * use this gateway. If /etc/authpf/authpf.allow does exist, then a
43695cc27f0SJoerg Sonnenberger  * user must be listed if the connection is to continue, else
43795cc27f0SJoerg Sonnenberger  * the session terminates in the same manner as being banned.
43895cc27f0SJoerg Sonnenberger  */
43995cc27f0SJoerg Sonnenberger static int
allowed_luser(const char * user)44095cc27f0SJoerg Sonnenberger allowed_luser(const char *user)
44195cc27f0SJoerg Sonnenberger {
44295cc27f0SJoerg Sonnenberger 	char	*buf, *lbuf;
44395cc27f0SJoerg Sonnenberger 	int	 matched;
44495cc27f0SJoerg Sonnenberger 	size_t	 len;
44595cc27f0SJoerg Sonnenberger 	FILE	*f;
44695cc27f0SJoerg Sonnenberger 
44795cc27f0SJoerg Sonnenberger 	if ((f = fopen(PATH_ALLOWFILE, "r")) == NULL) {
44895cc27f0SJoerg Sonnenberger 		if (errno == ENOENT) {
44995cc27f0SJoerg Sonnenberger 			/*
45095cc27f0SJoerg Sonnenberger 			 * allowfile doesn't exist, thus this gateway
45195cc27f0SJoerg Sonnenberger 			 * isn't restricted to certain users...
45295cc27f0SJoerg Sonnenberger 			 */
45395cc27f0SJoerg Sonnenberger 			return (1);
45495cc27f0SJoerg Sonnenberger 		}
45595cc27f0SJoerg Sonnenberger 
45695cc27f0SJoerg Sonnenberger 		/*
45795cc27f0SJoerg Sonnenberger 		 * user may in fact be allowed, but we can't open
45895cc27f0SJoerg Sonnenberger 		 * the file even though it's there. probably a config
45995cc27f0SJoerg Sonnenberger 		 * problem.
46095cc27f0SJoerg Sonnenberger 		 */
46195cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "cannot open allowed users file %s (%s)",
46295cc27f0SJoerg Sonnenberger 		    PATH_ALLOWFILE, strerror(errno));
46395cc27f0SJoerg Sonnenberger 		return (0);
46495cc27f0SJoerg Sonnenberger 	} else {
46595cc27f0SJoerg Sonnenberger 		/*
46695cc27f0SJoerg Sonnenberger 		 * /etc/authpf/authpf.allow exists, thus we do a linear
46795cc27f0SJoerg Sonnenberger 		 * search to see if they are allowed.
46895cc27f0SJoerg Sonnenberger 		 * also, if username "*" exists, then this is a
46995cc27f0SJoerg Sonnenberger 		 * "public" gateway, such as it is, so let
47095cc27f0SJoerg Sonnenberger 		 * everyone use it.
47195cc27f0SJoerg Sonnenberger 		 */
47295cc27f0SJoerg Sonnenberger 		lbuf = NULL;
47395cc27f0SJoerg Sonnenberger 		while ((buf = fgetln(f, &len))) {
47495cc27f0SJoerg Sonnenberger 			if (buf[len - 1] == '\n')
47595cc27f0SJoerg Sonnenberger 				buf[len - 1] = '\0';
47695cc27f0SJoerg Sonnenberger 			else {
47795cc27f0SJoerg Sonnenberger 				if ((lbuf = (char *)malloc(len + 1)) == NULL)
47895cc27f0SJoerg Sonnenberger 					err(1, NULL);
47995cc27f0SJoerg Sonnenberger 				memcpy(lbuf, buf, len);
48095cc27f0SJoerg Sonnenberger 				lbuf[len] = '\0';
48195cc27f0SJoerg Sonnenberger 				buf = lbuf;
48295cc27f0SJoerg Sonnenberger 			}
48395cc27f0SJoerg Sonnenberger 
48495cc27f0SJoerg Sonnenberger 			matched = strcmp(user, buf) == 0 || strcmp("*", buf) == 0;
48595cc27f0SJoerg Sonnenberger 
48695cc27f0SJoerg Sonnenberger 			if (lbuf != NULL) {
48795cc27f0SJoerg Sonnenberger 				free(lbuf);
48895cc27f0SJoerg Sonnenberger 				lbuf = NULL;
48995cc27f0SJoerg Sonnenberger 			}
49095cc27f0SJoerg Sonnenberger 
49195cc27f0SJoerg Sonnenberger 			if (matched)
49295cc27f0SJoerg Sonnenberger 				return (1); /* matched an allowed username */
49395cc27f0SJoerg Sonnenberger 		}
49495cc27f0SJoerg Sonnenberger 		syslog(LOG_INFO, "denied access to %s: not listed in %s",
49595cc27f0SJoerg Sonnenberger 		    user, PATH_ALLOWFILE);
49695cc27f0SJoerg Sonnenberger 
49795cc27f0SJoerg Sonnenberger 		fputs("\n\nSorry, you are not allowed to use this facility!\n",
49895cc27f0SJoerg Sonnenberger 		      stdout);
49995cc27f0SJoerg Sonnenberger 	}
50095cc27f0SJoerg Sonnenberger 	fflush(stdout);
50195cc27f0SJoerg Sonnenberger 	return (0);
50295cc27f0SJoerg Sonnenberger }
50395cc27f0SJoerg Sonnenberger 
50495cc27f0SJoerg Sonnenberger /*
50595cc27f0SJoerg Sonnenberger  * check_luser checks to see if user "luser" has been banned
50695cc27f0SJoerg Sonnenberger  * from using us by virtue of having an file of the same name
50795cc27f0SJoerg Sonnenberger  * in the "luserdir" directory.
50895cc27f0SJoerg Sonnenberger  *
50995cc27f0SJoerg Sonnenberger  * If the user has been banned, we copy the contents of the file
51095cc27f0SJoerg Sonnenberger  * to the user's screen. (useful for telling the user what to
51195cc27f0SJoerg Sonnenberger  * do to get un-banned, or just to tell them they aren't
51295cc27f0SJoerg Sonnenberger  * going to be un-banned.)
51395cc27f0SJoerg Sonnenberger  */
51495cc27f0SJoerg Sonnenberger static int
check_luser(const char * userdir,const char * user)51595cc27f0SJoerg Sonnenberger check_luser(const char *userdir, const char *user)
51695cc27f0SJoerg Sonnenberger {
51795cc27f0SJoerg Sonnenberger 	FILE	*f;
51895cc27f0SJoerg Sonnenberger 	int	 n;
51995cc27f0SJoerg Sonnenberger 	char	 tmp[MAXPATHLEN];
52095cc27f0SJoerg Sonnenberger 
52195cc27f0SJoerg Sonnenberger 	n = snprintf(tmp, sizeof(tmp), "%s/%s", userdir, user);
52295cc27f0SJoerg Sonnenberger 	if (n < 0 || (u_int)n >= sizeof(tmp)) {
52395cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "provided banned directory line too long (%s)",
52495cc27f0SJoerg Sonnenberger 		    userdir);
52595cc27f0SJoerg Sonnenberger 		return (0);
52695cc27f0SJoerg Sonnenberger 	}
52795cc27f0SJoerg Sonnenberger 	if ((f = fopen(tmp, "r")) == NULL) {
52895cc27f0SJoerg Sonnenberger 		if (errno == ENOENT) {
52995cc27f0SJoerg Sonnenberger 			/*
53095cc27f0SJoerg Sonnenberger 			 * file or dir doesn't exist, so therefore
53195cc27f0SJoerg Sonnenberger 			 * this luser isn't banned..  all is well
53295cc27f0SJoerg Sonnenberger 			 */
53395cc27f0SJoerg Sonnenberger 			return (1);
53495cc27f0SJoerg Sonnenberger 		} else {
53595cc27f0SJoerg Sonnenberger 			/*
53695cc27f0SJoerg Sonnenberger 			 * user may in fact be banned, but we can't open the
53795cc27f0SJoerg Sonnenberger 			 * file even though it's there. probably a config
53895cc27f0SJoerg Sonnenberger 			 * problem.
53995cc27f0SJoerg Sonnenberger 			 */
54095cc27f0SJoerg Sonnenberger 			syslog(LOG_ERR, "cannot open banned file %s (%s)",
54195cc27f0SJoerg Sonnenberger 			    tmp, strerror(errno));
54295cc27f0SJoerg Sonnenberger 			return (0);
54395cc27f0SJoerg Sonnenberger 		}
54495cc27f0SJoerg Sonnenberger 	} else {
54595cc27f0SJoerg Sonnenberger 		/*
54695cc27f0SJoerg Sonnenberger 		 * user is banned - spit the file at them to
54795cc27f0SJoerg Sonnenberger 		 * tell what they can do and where they can go.
54895cc27f0SJoerg Sonnenberger 		 */
54995cc27f0SJoerg Sonnenberger 		syslog(LOG_INFO, "denied access to %s: %s exists",
55095cc27f0SJoerg Sonnenberger 		    luser, tmp);
55195cc27f0SJoerg Sonnenberger 
55295cc27f0SJoerg Sonnenberger 		/* reuse tmp */
55395cc27f0SJoerg Sonnenberger 		strlcpy(tmp, "\n\n-**- Sorry, you have been banned! -**-\n\n",
55495cc27f0SJoerg Sonnenberger 		    sizeof(tmp));
55595cc27f0SJoerg Sonnenberger 		while (fputs(tmp, stdout) != EOF && !feof(f)) {
55695cc27f0SJoerg Sonnenberger 			if (fgets(tmp, sizeof(tmp), f) == NULL) {
55795cc27f0SJoerg Sonnenberger 				fflush(stdout);
55870224baaSJan Lentfer 				fclose(f);
55995cc27f0SJoerg Sonnenberger 				return (0);
56095cc27f0SJoerg Sonnenberger 			}
56195cc27f0SJoerg Sonnenberger 		}
56270224baaSJan Lentfer 		fclose(f);
56395cc27f0SJoerg Sonnenberger 	}
56495cc27f0SJoerg Sonnenberger 	fflush(stdout);
56595cc27f0SJoerg Sonnenberger 	return (0);
56695cc27f0SJoerg Sonnenberger }
56795cc27f0SJoerg Sonnenberger 
56895cc27f0SJoerg Sonnenberger /*
56995cc27f0SJoerg Sonnenberger  * Search for rulesets left by other authpf processes (either because they
57095cc27f0SJoerg Sonnenberger  * died ungracefully or were terminated) and remove them.
57195cc27f0SJoerg Sonnenberger  */
57295cc27f0SJoerg Sonnenberger static int
remove_stale_rulesets(void)57395cc27f0SJoerg Sonnenberger remove_stale_rulesets(void)
57495cc27f0SJoerg Sonnenberger {
57595cc27f0SJoerg Sonnenberger 	struct pfioc_ruleset	 prs;
57695cc27f0SJoerg Sonnenberger 	u_int32_t		 nr, mnr;
57795cc27f0SJoerg Sonnenberger 
57895cc27f0SJoerg Sonnenberger 	memset(&prs, 0, sizeof(prs));
57970224baaSJan Lentfer 	strlcpy(prs.path, anchorname, sizeof(prs.path));
58095cc27f0SJoerg Sonnenberger 	if (ioctl(dev_fd, DIOCGETRULESETS, &prs)) {
58195cc27f0SJoerg Sonnenberger 		if (errno == EINVAL)
58295cc27f0SJoerg Sonnenberger 			return (0);
58395cc27f0SJoerg Sonnenberger 		else
58495cc27f0SJoerg Sonnenberger 			return (1);
58595cc27f0SJoerg Sonnenberger 	}
58695cc27f0SJoerg Sonnenberger 
58795cc27f0SJoerg Sonnenberger 	mnr = prs.nr;
58895cc27f0SJoerg Sonnenberger 	nr = 0;
58995cc27f0SJoerg Sonnenberger 	while (nr < mnr) {
59095cc27f0SJoerg Sonnenberger 		char	*s, *t;
59195cc27f0SJoerg Sonnenberger 		pid_t	 pid;
59295cc27f0SJoerg Sonnenberger 
59395cc27f0SJoerg Sonnenberger 		prs.nr = nr;
59495cc27f0SJoerg Sonnenberger 		if (ioctl(dev_fd, DIOCGETRULESET, &prs))
59595cc27f0SJoerg Sonnenberger 			return (1);
59695cc27f0SJoerg Sonnenberger 		errno = 0;
59795cc27f0SJoerg Sonnenberger 		if ((t = strchr(prs.name, '(')) == NULL)
59895cc27f0SJoerg Sonnenberger 			t = prs.name;
59995cc27f0SJoerg Sonnenberger 		else
60095cc27f0SJoerg Sonnenberger 			t++;
60195cc27f0SJoerg Sonnenberger 		pid = strtoul(t, &s, 10);
60295cc27f0SJoerg Sonnenberger 		if (!prs.name[0] || errno ||
60395cc27f0SJoerg Sonnenberger 		    (*s && (t == prs.name || *s != ')')))
60495cc27f0SJoerg Sonnenberger 			return (1);
60595cc27f0SJoerg Sonnenberger 		if (kill(pid, 0) && errno != EPERM) {
60695cc27f0SJoerg Sonnenberger 			int			i;
60770224baaSJan Lentfer 			struct pfioc_trans_e	t_e[PF_RULESET_MAX+1];
60870224baaSJan Lentfer 			struct pfioc_trans	t_local;
60995cc27f0SJoerg Sonnenberger 
6108f14f35fSzrj 			bzero(&t_local, sizeof(t_local));
61170224baaSJan Lentfer 			bzero(t_e, sizeof(t_e));
61270224baaSJan Lentfer 			t_local.size = PF_RULESET_MAX+1;
61370224baaSJan Lentfer 			t_local.esize = sizeof(t_e[0]);
61470224baaSJan Lentfer 			t_local.array = t_e;
61570224baaSJan Lentfer 			for (i = 0; i < PF_RULESET_MAX+1; ++i) {
61670224baaSJan Lentfer 				t_e[i].rs_num = i;
61770224baaSJan Lentfer 				snprintf(t_e[i].anchor, sizeof(t_e[i].anchor),
61870224baaSJan Lentfer 				    "%s/%s", anchorname, prs.name);
61995cc27f0SJoerg Sonnenberger 			}
62095cc27f0SJoerg Sonnenberger 			mnr--;
62195cc27f0SJoerg Sonnenberger 		} else
62295cc27f0SJoerg Sonnenberger 			nr++;
62395cc27f0SJoerg Sonnenberger 	}
62495cc27f0SJoerg Sonnenberger 	return (0);
62595cc27f0SJoerg Sonnenberger }
62695cc27f0SJoerg Sonnenberger 
62795cc27f0SJoerg Sonnenberger /*
62895cc27f0SJoerg Sonnenberger  * Add/remove filter entries for user "luser" from ip "ipsrc"
62995cc27f0SJoerg Sonnenberger  */
63095cc27f0SJoerg Sonnenberger static int
change_filter(int add,const char * user,const char * his_ipsrc)63195cc27f0SJoerg Sonnenberger change_filter(int add, const char *user, const char *his_ipsrc)
63295cc27f0SJoerg Sonnenberger {
63370224baaSJan Lentfer 	const char *pargv[13] = {
63470224baaSJan Lentfer 		"pfctl", "-p", "/dev/pf", "-q", "-a", "anchor/ruleset",
63570224baaSJan Lentfer 		"-D", "user_ip=X", "-D", "user_id=X", "-f",
63670224baaSJan Lentfer 		"file", NULL
63770224baaSJan Lentfer 	};
63870224baaSJan Lentfer 	char	*fdpath = NULL, *userstr = NULL, *ipstr = NULL;
63970224baaSJan Lentfer 	char	*rsn = NULL, *fn = NULL;
64070224baaSJan Lentfer 	pid_t	pid;
64170224baaSJan Lentfer 	gid_t   gid;
64270224baaSJan Lentfer 	int	s;
64395cc27f0SJoerg Sonnenberger 
64495cc27f0SJoerg Sonnenberger 	if (user == NULL || !user[0] || his_ipsrc == NULL || !his_ipsrc[0]) {
64570224baaSJan Lentfer 		syslog(LOG_ERR, "invalid user/ipsrc");
64695cc27f0SJoerg Sonnenberger 		goto error;
64795cc27f0SJoerg Sonnenberger 	}
64895cc27f0SJoerg Sonnenberger 
64970224baaSJan Lentfer 	if (asprintf(&rsn, "%s/%s", anchorname, rulesetname) == -1)
65070224baaSJan Lentfer 		goto no_mem;
65170224baaSJan Lentfer 	if (asprintf(&fdpath, "/dev/fd/%d", dev_fd) == -1)
65270224baaSJan Lentfer 		goto no_mem;
65370224baaSJan Lentfer 	if (asprintf(&ipstr, "user_ip=%s", his_ipsrc) == -1)
65470224baaSJan Lentfer 		goto no_mem;
65570224baaSJan Lentfer 	if (asprintf(&userstr, "user_id=%s", user) == -1)
65670224baaSJan Lentfer 		goto no_mem;
65770224baaSJan Lentfer 
65895cc27f0SJoerg Sonnenberger 	if (add) {
65970224baaSJan Lentfer 		struct stat sb;
66070224baaSJan Lentfer 
66170224baaSJan Lentfer 		if (asprintf(&fn, "%s/%s/authpf.rules", PATH_USER_DIR, user)
66270224baaSJan Lentfer 		    == -1)
66370224baaSJan Lentfer 			goto no_mem;
66470224baaSJan Lentfer 		if (stat(fn, &sb) == -1) {
66570224baaSJan Lentfer 			free(fn);
66670224baaSJan Lentfer 			if ((fn = strdup(PATH_PFRULES)) == NULL)
66770224baaSJan Lentfer 				goto no_mem;
66870224baaSJan Lentfer 		}
66970224baaSJan Lentfer 	}
67070224baaSJan Lentfer 	pargv[2] = fdpath;
67170224baaSJan Lentfer 	pargv[5] = rsn;
67270224baaSJan Lentfer 	pargv[7] = userstr;
67370224baaSJan Lentfer 	pargv[9] = ipstr;
67470224baaSJan Lentfer 	if (!add)
67570224baaSJan Lentfer 		pargv[11] = "/dev/null";
67670224baaSJan Lentfer 	else
67770224baaSJan Lentfer 		pargv[11] = fn;
67870224baaSJan Lentfer 
67970224baaSJan Lentfer 	switch (pid = fork()) {
68070224baaSJan Lentfer 	case -1:
68170224baaSJan Lentfer 		syslog(LOG_ERR, "fork failed");
68295cc27f0SJoerg Sonnenberger 		goto error;
68370224baaSJan Lentfer 	case 0:
68470224baaSJan Lentfer 		/* revoke group privs before exec */
68570224baaSJan Lentfer 		gid = getgid();
68670224baaSJan Lentfer 		if (setregid(gid, gid) == -1) {
68770224baaSJan Lentfer 			err(1, "setregid");
68895cc27f0SJoerg Sonnenberger 		}
68970224baaSJan Lentfer 		execvp(PATH_PFCTL, __DECONST(char *const *, pargv));
69070224baaSJan Lentfer 		warn("exec of %s failed", PATH_PFCTL);
69170224baaSJan Lentfer 		_exit(1);
69295cc27f0SJoerg Sonnenberger 	}
69395cc27f0SJoerg Sonnenberger 
69470224baaSJan Lentfer 	/* parent */
69570224baaSJan Lentfer 	waitpid(pid, &s, 0);
69670224baaSJan Lentfer 	if (s != 0) {
69770224baaSJan Lentfer 		syslog(LOG_ERR, "pfctl exited abnormally");
69895cc27f0SJoerg Sonnenberger 		goto error;
69995cc27f0SJoerg Sonnenberger 	}
70095cc27f0SJoerg Sonnenberger 
70195cc27f0SJoerg Sonnenberger 	if (add) {
70295cc27f0SJoerg Sonnenberger 		gettimeofday(&Tstart, NULL);
70395cc27f0SJoerg Sonnenberger 		syslog(LOG_INFO, "allowing %s, user %s", his_ipsrc, user);
70495cc27f0SJoerg Sonnenberger 	} else {
70595cc27f0SJoerg Sonnenberger 		gettimeofday(&Tend, NULL);
70695cc27f0SJoerg Sonnenberger 		syslog(LOG_INFO, "removed %s, user %s - duration %ld seconds",
70795cc27f0SJoerg Sonnenberger 		    his_ipsrc, user, Tend.tv_sec - Tstart.tv_sec);
70895cc27f0SJoerg Sonnenberger 	}
70995cc27f0SJoerg Sonnenberger 	return (0);
71070224baaSJan Lentfer no_mem:
71170224baaSJan Lentfer 	syslog(LOG_ERR, "malloc failed");
71295cc27f0SJoerg Sonnenberger error:
71370224baaSJan Lentfer 	free(fdpath);
71470224baaSJan Lentfer 	free(rsn);
71570224baaSJan Lentfer 	free(userstr);
71670224baaSJan Lentfer 	free(ipstr);
71770224baaSJan Lentfer 	free(fn);
71895cc27f0SJoerg Sonnenberger 	return (-1);
71995cc27f0SJoerg Sonnenberger }
72095cc27f0SJoerg Sonnenberger 
72195cc27f0SJoerg Sonnenberger /*
72270224baaSJan Lentfer  * Add/remove this IP from the "authpf_users" table.
72370224baaSJan Lentfer  */
72470224baaSJan Lentfer static int
change_table(int add,const char * his_ipsrc)72570224baaSJan Lentfer change_table(int add, const char *his_ipsrc)
72670224baaSJan Lentfer {
72770224baaSJan Lentfer 	struct pfioc_table	io;
72870224baaSJan Lentfer 	struct pfr_addr		addr;
72970224baaSJan Lentfer 
73070224baaSJan Lentfer 	bzero(&io, sizeof(io));
73170224baaSJan Lentfer 	strlcpy(io.pfrio_table.pfrt_name, tablename,
73270224baaSJan Lentfer 	    sizeof(io.pfrio_table.pfrt_name));
73370224baaSJan Lentfer 	io.pfrio_buffer = &addr;
73470224baaSJan Lentfer 	io.pfrio_esize = sizeof(addr);
73570224baaSJan Lentfer 	io.pfrio_size = 1;
73670224baaSJan Lentfer 
73770224baaSJan Lentfer 	bzero(&addr, sizeof(addr));
73870224baaSJan Lentfer 	if (his_ipsrc == NULL || !his_ipsrc[0])
73970224baaSJan Lentfer 		return (-1);
74070224baaSJan Lentfer 	if (inet_pton(AF_INET, his_ipsrc, &addr.pfra_ip4addr) == 1) {
74170224baaSJan Lentfer 		addr.pfra_af = AF_INET;
74270224baaSJan Lentfer 		addr.pfra_net = 32;
74370224baaSJan Lentfer 	} else if (inet_pton(AF_INET6, his_ipsrc, &addr.pfra_ip6addr) == 1) {
74470224baaSJan Lentfer 		addr.pfra_af = AF_INET6;
74570224baaSJan Lentfer 		addr.pfra_net = 128;
74670224baaSJan Lentfer 	} else {
74770224baaSJan Lentfer 		syslog(LOG_ERR, "invalid his_ipsrc");
74870224baaSJan Lentfer 		return (-1);
74970224baaSJan Lentfer 	}
75070224baaSJan Lentfer 
75170224baaSJan Lentfer 	if (ioctl(dev_fd, add ? DIOCRADDADDRS : DIOCRDELADDRS, &io) &&
75270224baaSJan Lentfer 	    errno != ESRCH) {
75370224baaSJan Lentfer 		syslog(LOG_ERR, "cannot %s %s from table %s: %s",
75470224baaSJan Lentfer 		    add ? "add" : "remove", his_ipsrc, tablename,
75570224baaSJan Lentfer 		    strerror(errno));
75670224baaSJan Lentfer 		return (-1);
75770224baaSJan Lentfer 	}
75870224baaSJan Lentfer 	return (0);
75970224baaSJan Lentfer }
76070224baaSJan Lentfer 
76170224baaSJan Lentfer /*
76295cc27f0SJoerg Sonnenberger  * This is to kill off states that would otherwise be left behind stateful
76395cc27f0SJoerg Sonnenberger  * rules. This means we don't need to allow in more traffic than we really
76495cc27f0SJoerg Sonnenberger  * want to, since we don't have to worry about any luser sessions lasting
76595cc27f0SJoerg Sonnenberger  * longer than their ssh session. This function is based on
76695cc27f0SJoerg Sonnenberger  * pfctl_kill_states from pfctl.
76795cc27f0SJoerg Sonnenberger  */
76895cc27f0SJoerg Sonnenberger static void
authpf_kill_states(void)76995cc27f0SJoerg Sonnenberger authpf_kill_states(void)
77095cc27f0SJoerg Sonnenberger {
77195cc27f0SJoerg Sonnenberger 	struct pfioc_state_kill	psk;
77270224baaSJan Lentfer 	struct pf_addr target;
77395cc27f0SJoerg Sonnenberger 
77495cc27f0SJoerg Sonnenberger 	memset(&psk, 0, sizeof(psk));
77570224baaSJan Lentfer 	memset(&target, 0, sizeof(target));
77695cc27f0SJoerg Sonnenberger 
77770224baaSJan Lentfer 	if (inet_pton(AF_INET, ipsrc, &target.v4) == 1)
77870224baaSJan Lentfer 		psk.psk_af = AF_INET;
77970224baaSJan Lentfer 	else if (inet_pton(AF_INET6, ipsrc, &target.v6) == 1)
78070224baaSJan Lentfer 		psk.psk_af = AF_INET6;
78170224baaSJan Lentfer 	else {
78270224baaSJan Lentfer 		syslog(LOG_ERR, "inet_pton(%s) failed", ipsrc);
78370224baaSJan Lentfer 		return;
78470224baaSJan Lentfer 	}
78595cc27f0SJoerg Sonnenberger 
78695cc27f0SJoerg Sonnenberger 	/* Kill all states from ipsrc */
78770224baaSJan Lentfer 	memcpy(&psk.psk_src.addr.v.a.addr, &target,
78870224baaSJan Lentfer 	    sizeof(psk.psk_src.addr.v.a.addr));
78995cc27f0SJoerg Sonnenberger 	memset(&psk.psk_src.addr.v.a.mask, 0xff,
79095cc27f0SJoerg Sonnenberger 	    sizeof(psk.psk_src.addr.v.a.mask));
79195cc27f0SJoerg Sonnenberger 	if (ioctl(dev_fd, DIOCKILLSTATES, &psk))
79295cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
79395cc27f0SJoerg Sonnenberger 
79495cc27f0SJoerg Sonnenberger 	/* Kill all states to ipsrc */
79595cc27f0SJoerg Sonnenberger 	memset(&psk.psk_src, 0, sizeof(psk.psk_src));
79670224baaSJan Lentfer 	memcpy(&psk.psk_dst.addr.v.a.addr, &target,
79770224baaSJan Lentfer 	    sizeof(psk.psk_dst.addr.v.a.addr));
79895cc27f0SJoerg Sonnenberger 	memset(&psk.psk_dst.addr.v.a.mask, 0xff,
79995cc27f0SJoerg Sonnenberger 	    sizeof(psk.psk_dst.addr.v.a.mask));
80095cc27f0SJoerg Sonnenberger 	if (ioctl(dev_fd, DIOCKILLSTATES, &psk))
80195cc27f0SJoerg Sonnenberger 		syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
80295cc27f0SJoerg Sonnenberger }
80395cc27f0SJoerg Sonnenberger 
80495cc27f0SJoerg Sonnenberger /* signal handler that makes us go away properly */
80595cc27f0SJoerg Sonnenberger static void
need_death(int signo __unused)80695cc27f0SJoerg Sonnenberger need_death(int signo __unused)
80795cc27f0SJoerg Sonnenberger {
80895cc27f0SJoerg Sonnenberger 	want_death = 1;
80995cc27f0SJoerg Sonnenberger }
81095cc27f0SJoerg Sonnenberger 
81195cc27f0SJoerg Sonnenberger /*
81295cc27f0SJoerg Sonnenberger  * function that removes our stuff when we go away.
81395cc27f0SJoerg Sonnenberger  */
81495cc27f0SJoerg Sonnenberger static __dead void
do_death(int active)81595cc27f0SJoerg Sonnenberger do_death(int active)
81695cc27f0SJoerg Sonnenberger {
81795cc27f0SJoerg Sonnenberger 	int	ret = 0;
81895cc27f0SJoerg Sonnenberger 
81995cc27f0SJoerg Sonnenberger 	if (active) {
82095cc27f0SJoerg Sonnenberger 		change_filter(0, luser, ipsrc);
82170224baaSJan Lentfer 		change_table(0, ipsrc);
82295cc27f0SJoerg Sonnenberger 		authpf_kill_states();
82395cc27f0SJoerg Sonnenberger 		remove_stale_rulesets();
82495cc27f0SJoerg Sonnenberger 	}
82570224baaSJan Lentfer 	if (pidfile[0] && (pidfp != NULL))
82695cc27f0SJoerg Sonnenberger 		if (unlink(pidfile) == -1)
82795cc27f0SJoerg Sonnenberger 			syslog(LOG_ERR, "cannot unlink %s (%m)", pidfile);
82895cc27f0SJoerg Sonnenberger 	exit(ret);
82995cc27f0SJoerg Sonnenberger }
830