1223637Sbz/*	$OpenBSD: authpf.c,v 1.112 2009/01/10 19:08:53 miod Exp $	*/
2126353Smlaier
3126353Smlaier/*
4171172Smlaier * Copyright (C) 1998 - 2007 Bob Beck (beck@openbsd.org).
5126353Smlaier *
6171172Smlaier * Permission to use, copy, modify, and distribute this software for any
7171172Smlaier * purpose with or without fee is hereby granted, provided that the above
8171172Smlaier * copyright notice and this permission notice appear in all copies.
9126353Smlaier *
10171172Smlaier * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11171172Smlaier * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12171172Smlaier * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13171172Smlaier * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14171172Smlaier * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15171172Smlaier * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16171172Smlaier * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17126353Smlaier */
18126353Smlaier
19127082Sobrien#include <sys/cdefs.h>
20127082Sobrien__FBSDID("$FreeBSD$");
21127082Sobrien
22223637Sbz#include <sys/types.h>
23126353Smlaier#include <sys/file.h>
24126353Smlaier#include <sys/ioctl.h>
25126353Smlaier#include <sys/socket.h>
26145840Smlaier#include <sys/stat.h>
27126353Smlaier#include <sys/time.h>
28145840Smlaier#include <sys/wait.h>
29126353Smlaier
30126353Smlaier#include <net/if.h>
31126353Smlaier#include <net/pfvar.h>
32126353Smlaier#include <arpa/inet.h>
33126353Smlaier
34126353Smlaier#include <err.h>
35126353Smlaier#include <errno.h>
36153722Smlaier#ifdef __FreeBSD__
37153722Smlaier#include <inttypes.h>
38153722Smlaier#endif
39145840Smlaier#include <login_cap.h>
40126353Smlaier#include <pwd.h>
41223637Sbz#include <grp.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 "pathnames.h"
50126353Smlaier
51126353Smlaierstatic int	read_config(FILE *);
52223637Sbzstatic void	print_message(const char *);
53223637Sbzstatic int	allowed_luser(struct passwd *);
54223637Sbzstatic int	check_luser(const char *, char *);
55126353Smlaierstatic int	remove_stale_rulesets(void);
56223637Sbzstatic int	recursive_ruleset_purge(char *, char *);
57126353Smlaierstatic int	change_filter(int, const char *, const char *);
58171172Smlaierstatic int	change_table(int, const char *);
59126353Smlaierstatic void	authpf_kill_states(void);
60126353Smlaier
61126353Smlaierint	dev;			/* pf device */
62126353Smlaierchar	anchorname[PF_ANCHOR_NAME_SIZE] = "authpf";
63145840Smlaierchar	rulesetname[MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 2];
64145840Smlaierchar	tablename[PF_TABLE_NAME_SIZE] = "authpf_users";
65223637Sbzint	user_ip = 1;	/* controls whether $user_ip is set */
66126353Smlaier
67126353SmlaierFILE	*pidfp;
68223637Sbzint	pidfd = -1;
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
82223637Sbzextern char *__progname;	/* program name */
83126353Smlaier
84126353Smlaier/*
85126353Smlaier * User shell for authenticating gateways. Sole purpose is to allow
86126353Smlaier * a user to ssh to a gateway, and have the gateway modify packet
87126353Smlaier * filters to allow access, then remove access when the user finishes
88126353Smlaier * up. Meant to be used only from ssh(1) connections.
89126353Smlaier */
90126353Smlaierint
91223637Sbzmain(void)
92126353Smlaier{
93223637Sbz	int		 lockcnt = 0, n;
94126353Smlaier	FILE		*config;
95145840Smlaier	struct in6_addr	 ina;
96126353Smlaier	struct passwd	*pw;
97126353Smlaier	char		*cp;
98171172Smlaier	gid_t		 gid;
99126353Smlaier	uid_t		 uid;
100223637Sbz	const char	*shell;
101145840Smlaier	login_cap_t	*lc;
102126353Smlaier
103223637Sbz	if (strcmp(__progname, "-authpf-noip") == 0)
104223637Sbz                user_ip = 0;
105223637Sbz
106126353Smlaier	config = fopen(PATH_CONFFILE, "r");
107171172Smlaier	if (config == NULL) {
108223637Sbz		syslog(LOG_ERR, "cannot open %s (%m)", PATH_CONFFILE);
109171172Smlaier		exit(1);
110171172Smlaier	}
111126353Smlaier
112126353Smlaier	if ((cp = getenv("SSH_TTY")) == NULL) {
113126353Smlaier		syslog(LOG_ERR, "non-interactive session connection for authpf");
114126353Smlaier		exit(1);
115126353Smlaier	}
116126353Smlaier
117126353Smlaier	if ((cp = getenv("SSH_CLIENT")) == NULL) {
118126353Smlaier		syslog(LOG_ERR, "cannot determine connection source");
119126353Smlaier		exit(1);
120126353Smlaier	}
121126353Smlaier
122126353Smlaier	if (strlcpy(ipsrc, cp, sizeof(ipsrc)) >= sizeof(ipsrc)) {
123126353Smlaier		syslog(LOG_ERR, "SSH_CLIENT variable too long");
124126353Smlaier		exit(1);
125126353Smlaier	}
126126353Smlaier	cp = strchr(ipsrc, ' ');
127126353Smlaier	if (!cp) {
128126353Smlaier		syslog(LOG_ERR, "corrupt SSH_CLIENT variable %s", ipsrc);
129126353Smlaier		exit(1);
130126353Smlaier	}
131126353Smlaier	*cp = '\0';
132145840Smlaier	if (inet_pton(AF_INET, ipsrc, &ina) != 1 &&
133145840Smlaier	    inet_pton(AF_INET6, ipsrc, &ina) != 1) {
134126353Smlaier		syslog(LOG_ERR,
135126353Smlaier		    "cannot determine IP from SSH_CLIENT %s", ipsrc);
136126353Smlaier		exit(1);
137126353Smlaier	}
138126353Smlaier	/* open the pf device */
139126353Smlaier	dev = open(PATH_DEVFILE, O_RDWR);
140126353Smlaier	if (dev == -1) {
141126353Smlaier		syslog(LOG_ERR, "cannot open packet filter device (%m)");
142126353Smlaier		goto die;
143126353Smlaier	}
144126353Smlaier
145126353Smlaier	uid = getuid();
146126353Smlaier	pw = getpwuid(uid);
147126353Smlaier	if (pw == NULL) {
148126353Smlaier		syslog(LOG_ERR, "cannot find user for uid %u", uid);
149126353Smlaier		goto die;
150126353Smlaier	}
151145840Smlaier
152145840Smlaier	if ((lc = login_getclass(pw->pw_class)) != NULL)
153223637Sbz		shell = login_getcapstr(lc, "shell", pw->pw_shell,
154145840Smlaier		    pw->pw_shell);
155145840Smlaier	else
156145840Smlaier		shell = pw->pw_shell;
157145840Smlaier
158223637Sbz#ifndef __FreeBSD__
159145840Smlaier	login_close(lc);
160223637Sbz#endif
161145840Smlaier
162223637Sbz	if (strcmp(shell, PATH_AUTHPF_SHELL) &&
163223637Sbz	    strcmp(shell, PATH_AUTHPF_SHELL_NOIP)) {
164126353Smlaier		syslog(LOG_ERR, "wrong shell for user %s, uid %u",
165126353Smlaier		    pw->pw_name, pw->pw_uid);
166223637Sbz#ifdef __FreeBSD__
167223637Sbz	login_close(lc);
168223637Sbz#else
169145840Smlaier		if (shell != pw->pw_shell)
170145840Smlaier			free(shell);
171223637Sbz#endif
172126353Smlaier		goto die;
173126353Smlaier	}
174126353Smlaier
175223637Sbz#ifdef __FreeBSD__
176223637Sbz	login_close(lc);
177223637Sbz#else
178145840Smlaier	if (shell != pw->pw_shell)
179145840Smlaier		free(shell);
180223637Sbz#endif
181145840Smlaier
182126353Smlaier	/*
183126353Smlaier	 * Paranoia, but this data _does_ come from outside authpf, and
184126353Smlaier	 * truncation would be bad.
185126353Smlaier	 */
186126353Smlaier	if (strlcpy(luser, pw->pw_name, sizeof(luser)) >= sizeof(luser)) {
187126353Smlaier		syslog(LOG_ERR, "username too long: %s", pw->pw_name);
188126353Smlaier		goto die;
189126353Smlaier	}
190126353Smlaier
191130617Smlaier	if ((n = snprintf(rulesetname, sizeof(rulesetname), "%s(%ld)",
192145840Smlaier	    luser, (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) {
193130617Smlaier		syslog(LOG_INFO, "%s(%ld) too large, ruleset name will be %ld",
194130617Smlaier		    luser, (long)getpid(), (long)getpid());
195130617Smlaier		if ((n = snprintf(rulesetname, sizeof(rulesetname), "%ld",
196145840Smlaier		    (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) {
197130617Smlaier			syslog(LOG_ERR, "pid too large for ruleset name");
198130617Smlaier			goto die;
199130617Smlaier		}
200130617Smlaier	}
201130617Smlaier
202130617Smlaier
203223637Sbz	/* Make our entry in /var/authpf as ipaddr or username */
204223637Sbz	n = snprintf(pidfile, sizeof(pidfile), "%s/%s",
205223637Sbz	    PATH_PIDFILE, user_ip ? ipsrc : luser);
206126353Smlaier	if (n < 0 || (u_int)n >= sizeof(pidfile)) {
207126353Smlaier		syslog(LOG_ERR, "path to pidfile too long");
208126353Smlaier		goto die;
209126353Smlaier	}
210126353Smlaier
211223637Sbz	signal(SIGTERM, need_death);
212223637Sbz	signal(SIGINT, need_death);
213223637Sbz	signal(SIGALRM, need_death);
214223637Sbz	signal(SIGPIPE, need_death);
215223637Sbz	signal(SIGHUP, need_death);
216223637Sbz	signal(SIGQUIT, need_death);
217223637Sbz	signal(SIGTSTP, need_death);
218223637Sbz
219126353Smlaier	/*
220126353Smlaier	 * If someone else is already using this ip, then this person
221126353Smlaier	 * wants to switch users - so kill the old process and exit
222126353Smlaier	 * as well.
223126353Smlaier	 *
224126353Smlaier	 * Note, we could print a message and tell them to log out, but the
225126353Smlaier	 * usual case of this is that someone has left themselves logged in,
226126353Smlaier	 * with the authenticated connection iconized and someone else walks
227126353Smlaier	 * up to use and automatically logs in before using. If this just
228126353Smlaier	 * gets rid of the old one silently, the new user never knows they
229126353Smlaier	 * could have used someone else's old authentication. If we
230126353Smlaier	 * tell them to log out before switching users it is an invitation
231126353Smlaier	 * for abuse.
232126353Smlaier	 */
233126353Smlaier
234126353Smlaier	do {
235126353Smlaier		int	save_errno, otherpid = -1;
236126353Smlaier		char	otherluser[MAXLOGNAME];
237126353Smlaier
238126353Smlaier		if ((pidfd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1 ||
239126353Smlaier		    (pidfp = fdopen(pidfd, "r+")) == NULL) {
240126353Smlaier			if (pidfd != -1)
241126353Smlaier				close(pidfd);
242126353Smlaier			syslog(LOG_ERR, "cannot open or create %s: %s", pidfile,
243126353Smlaier			    strerror(errno));
244126353Smlaier			goto die;
245126353Smlaier		}
246126353Smlaier
247126353Smlaier		if (flock(fileno(pidfp), LOCK_EX|LOCK_NB) == 0)
248126353Smlaier			break;
249126353Smlaier		save_errno = errno;
250126353Smlaier
251126353Smlaier		/* Mark our pid, and username to our file. */
252126353Smlaier
253126353Smlaier		rewind(pidfp);
254126353Smlaier		/* 31 == MAXLOGNAME - 1 */
255126353Smlaier		if (fscanf(pidfp, "%d\n%31s\n", &otherpid, otherluser) != 2)
256126353Smlaier			otherpid = -1;
257126353Smlaier		syslog(LOG_DEBUG, "tried to lock %s, in use by pid %d: %s",
258126353Smlaier		    pidfile, otherpid, strerror(save_errno));
259126353Smlaier
260126353Smlaier		if (otherpid > 0) {
261126353Smlaier			syslog(LOG_INFO,
262126353Smlaier			    "killing prior auth (pid %d) of %s by user %s",
263126353Smlaier			    otherpid, ipsrc, otherluser);
264126353Smlaier			if (kill((pid_t) otherpid, SIGTERM) == -1) {
265126353Smlaier				syslog(LOG_INFO,
266126353Smlaier				    "could not kill process %d: (%m)",
267126353Smlaier				    otherpid);
268126353Smlaier			}
269126353Smlaier		}
270126353Smlaier
271126353Smlaier		/*
272223637Sbz		 * We try to kill the previous process and acquire the lock
273126353Smlaier		 * for 10 seconds, trying once a second. if we can't after
274223637Sbz		 * 10 attempts we log an error and give up.
275126353Smlaier		 */
276223637Sbz		if (want_death || ++lockcnt > 10) {
277223637Sbz			if (!want_death)
278223637Sbz				syslog(LOG_ERR, "cannot kill previous authpf (pid %d)",
279223637Sbz				    otherpid);
280171172Smlaier			fclose(pidfp);
281171172Smlaier			pidfp = NULL;
282223637Sbz			pidfd = -1;
283126353Smlaier			goto dogdeath;
284126353Smlaier		}
285126353Smlaier		sleep(1);
286126353Smlaier
287126353Smlaier		/* re-open, and try again. The previous authpf process
288126353Smlaier		 * we killed above should unlink the file and release
289126353Smlaier		 * it's lock, giving us a chance to get it now
290126353Smlaier		 */
291126353Smlaier		fclose(pidfp);
292171172Smlaier		pidfp = NULL;
293223637Sbz		pidfd = -1;
294126353Smlaier	} while (1);
295171172Smlaier
296171172Smlaier	/* whack the group list */
297171172Smlaier	gid = getegid();
298171172Smlaier	if (setgroups(1, &gid) == -1) {
299171172Smlaier		syslog(LOG_INFO, "setgroups: %s", strerror(errno));
300171172Smlaier		do_death(0);
301171172Smlaier	}
302126353Smlaier
303126353Smlaier	/* revoke privs */
304171172Smlaier	uid = getuid();
305171172Smlaier	if (setresuid(uid, uid, uid) == -1) {
306171172Smlaier		syslog(LOG_INFO, "setresuid: %s", strerror(errno));
307171172Smlaier		do_death(0);
308171172Smlaier	}
309130617Smlaier	openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON);
310130617Smlaier
311223637Sbz	if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(pw)) {
312130617Smlaier		syslog(LOG_INFO, "user %s prohibited", luser);
313126353Smlaier		do_death(0);
314130617Smlaier	}
315126353Smlaier
316171172Smlaier	if (read_config(config)) {
317171172Smlaier		syslog(LOG_ERR, "invalid config file %s", PATH_CONFFILE);
318126353Smlaier		do_death(0);
319130617Smlaier	}
320126353Smlaier
321130617Smlaier	if (remove_stale_rulesets()) {
322130617Smlaier		syslog(LOG_INFO, "error removing stale rulesets");
323126353Smlaier		do_death(0);
324130617Smlaier	}
325126353Smlaier
326126353Smlaier	/* We appear to be making headway, so actually mark our pid */
327126353Smlaier	rewind(pidfp);
328126353Smlaier	fprintf(pidfp, "%ld\n%s\n", (long)getpid(), luser);
329126353Smlaier	fflush(pidfp);
330145840Smlaier	(void) ftruncate(fileno(pidfp), ftello(pidfp));
331126353Smlaier
332126353Smlaier	if (change_filter(1, luser, ipsrc) == -1) {
333126353Smlaier		printf("Unable to modify filters\r\n");
334130617Smlaier		do_death(0);
335126353Smlaier	}
336223637Sbz	if (user_ip && change_table(1, ipsrc) == -1) {
337145840Smlaier		printf("Unable to modify table\r\n");
338145840Smlaier		change_filter(0, luser, ipsrc);
339145840Smlaier		do_death(0);
340145840Smlaier	}
341126353Smlaier
342126353Smlaier	while (1) {
343145840Smlaier		printf("\r\nHello %s. ", luser);
344126353Smlaier		printf("You are authenticated from host \"%s\"\r\n", ipsrc);
345126353Smlaier		setproctitle("%s@%s", luser, ipsrc);
346126353Smlaier		print_message(PATH_MESSAGE);
347126353Smlaier		while (1) {
348126353Smlaier			sleep(10);
349126353Smlaier			if (want_death)
350126353Smlaier				do_death(1);
351126353Smlaier		}
352126353Smlaier	}
353126353Smlaier
354126353Smlaier	/* NOTREACHED */
355126353Smlaierdogdeath:
356126353Smlaier	printf("\r\n\r\nSorry, this service is currently unavailable due to ");
357126353Smlaier	printf("technical difficulties\r\n\r\n");
358126353Smlaier	print_message(PATH_PROBLEM);
359126353Smlaier	printf("\r\nYour authentication process (pid %ld) was unable to run\n",
360126353Smlaier	    (long)getpid());
361126353Smlaier	sleep(180); /* them lusers read reaaaaal slow */
362126353Smlaierdie:
363126353Smlaier	do_death(0);
364126353Smlaier}
365126353Smlaier
366126353Smlaier/*
367126353Smlaier * reads config file in PATH_CONFFILE to set optional behaviours up
368126353Smlaier */
369126353Smlaierstatic int
370126353Smlaierread_config(FILE *f)
371126353Smlaier{
372126353Smlaier	char	buf[1024];
373126353Smlaier	int	i = 0;
374126353Smlaier
375126353Smlaier	do {
376126353Smlaier		char	**ap;
377126353Smlaier		char	 *pair[4], *cp, *tp;
378126353Smlaier		int	  len;
379126353Smlaier
380126353Smlaier		if (fgets(buf, sizeof(buf), f) == NULL) {
381126353Smlaier			fclose(f);
382126353Smlaier			return (0);
383126353Smlaier		}
384126353Smlaier		i++;
385126353Smlaier		len = strlen(buf);
386223637Sbz		if (len == 0)
387223637Sbz			continue;
388126353Smlaier		if (buf[len - 1] != '\n' && !feof(f)) {
389126353Smlaier			syslog(LOG_ERR, "line %d too long in %s", i,
390126353Smlaier			    PATH_CONFFILE);
391126353Smlaier			return (1);
392126353Smlaier		}
393126353Smlaier		buf[len - 1] = '\0';
394126353Smlaier
395126353Smlaier		for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
396126353Smlaier			; /* nothing */
397126353Smlaier
398126353Smlaier		if (!*cp || *cp == '#' || *cp == '\n')
399126353Smlaier			continue;
400126353Smlaier
401126353Smlaier		for (ap = pair; ap < &pair[3] &&
402126353Smlaier		    (*ap = strsep(&cp, "=")) != NULL; ) {
403126353Smlaier			if (**ap != '\0')
404126353Smlaier				ap++;
405126353Smlaier		}
406126353Smlaier		if (ap != &pair[2])
407126353Smlaier			goto parse_error;
408126353Smlaier
409126353Smlaier		tp = pair[1] + strlen(pair[1]);
410126353Smlaier		while ((*tp == ' ' || *tp == '\t') && tp >= pair[1])
411126353Smlaier			*tp-- = '\0';
412126353Smlaier
413126353Smlaier		if (strcasecmp(pair[0], "anchor") == 0) {
414126353Smlaier			if (!pair[1][0] || strlcpy(anchorname, pair[1],
415126353Smlaier			    sizeof(anchorname)) >= sizeof(anchorname))
416126353Smlaier				goto parse_error;
417126353Smlaier		}
418145840Smlaier		if (strcasecmp(pair[0], "table") == 0) {
419145840Smlaier			if (!pair[1][0] || strlcpy(tablename, pair[1],
420145840Smlaier			    sizeof(tablename)) >= sizeof(tablename))
421145840Smlaier				goto parse_error;
422145840Smlaier		}
423126353Smlaier	} while (!feof(f) && !ferror(f));
424126353Smlaier	fclose(f);
425126353Smlaier	return (0);
426126353Smlaier
427126353Smlaierparse_error:
428126353Smlaier	fclose(f);
429126353Smlaier	syslog(LOG_ERR, "parse error, line %d of %s", i, PATH_CONFFILE);
430126353Smlaier	return (1);
431126353Smlaier}
432126353Smlaier
433126353Smlaier
434126353Smlaier/*
435126353Smlaier * splatter a file to stdout - max line length of 1024,
436126353Smlaier * used for spitting message files at users to tell them
437126353Smlaier * they've been bad or we're unavailable.
438126353Smlaier */
439126353Smlaierstatic void
440223637Sbzprint_message(const char *filename)
441126353Smlaier{
442126353Smlaier	char	 buf[1024];
443126353Smlaier	FILE	*f;
444126353Smlaier
445126353Smlaier	if ((f = fopen(filename, "r")) == NULL)
446126353Smlaier		return; /* fail silently, we don't care if it isn't there */
447126353Smlaier
448126353Smlaier	do {
449126353Smlaier		if (fgets(buf, sizeof(buf), f) == NULL) {
450126353Smlaier			fflush(stdout);
451126353Smlaier			fclose(f);
452126353Smlaier			return;
453126353Smlaier		}
454126353Smlaier	} while (fputs(buf, stdout) != EOF && !feof(f));
455126353Smlaier	fflush(stdout);
456126353Smlaier	fclose(f);
457126353Smlaier}
458126353Smlaier
459126353Smlaier/*
460126353Smlaier * allowed_luser checks to see if user "luser" is allowed to
461126353Smlaier * use this gateway by virtue of being listed in an allowed
462126353Smlaier * users file, namely /etc/authpf/authpf.allow .
463223637Sbz * Users may be listed by <username>, %<group>, or @<login_class>.
464126353Smlaier *
465126353Smlaier * If /etc/authpf/authpf.allow does not exist, then we assume that
466126353Smlaier * all users who are allowed in by sshd(8) are permitted to
467126353Smlaier * use this gateway. If /etc/authpf/authpf.allow does exist, then a
468126353Smlaier * user must be listed if the connection is to continue, else
469126353Smlaier * the session terminates in the same manner as being banned.
470126353Smlaier */
471126353Smlaierstatic int
472223637Sbzallowed_luser(struct passwd *pw)
473126353Smlaier{
474223637Sbz	char *buf,*lbuf;
475126353Smlaier	int	 matched;
476126353Smlaier	size_t	 len;
477126353Smlaier	FILE	*f;
478126353Smlaier
479126353Smlaier	if ((f = fopen(PATH_ALLOWFILE, "r")) == NULL) {
480126353Smlaier		if (errno == ENOENT) {
481126353Smlaier			/*
482126353Smlaier			 * allowfile doesn't exist, thus this gateway
483126353Smlaier			 * isn't restricted to certain users...
484126353Smlaier			 */
485126353Smlaier			return (1);
486126353Smlaier		}
487126353Smlaier
488126353Smlaier		/*
489126353Smlaier		 * luser may in fact be allowed, but we can't open
490126353Smlaier		 * the file even though it's there. probably a config
491126353Smlaier		 * problem.
492126353Smlaier		 */
493126353Smlaier		syslog(LOG_ERR, "cannot open allowed users file %s (%s)",
494126353Smlaier		    PATH_ALLOWFILE, strerror(errno));
495126353Smlaier		return (0);
496126353Smlaier	} else {
497126353Smlaier		/*
498126353Smlaier		 * /etc/authpf/authpf.allow exists, thus we do a linear
499126353Smlaier		 * search to see if they are allowed.
500126353Smlaier		 * also, if username "*" exists, then this is a
501126353Smlaier		 * "public" gateway, such as it is, so let
502126353Smlaier		 * everyone use it.
503126353Smlaier		 */
504223637Sbz		int gl_init = 0, ngroups = NGROUPS + 1;
505223637Sbz		gid_t groups[NGROUPS + 1];
506223637Sbz
507126353Smlaier		lbuf = NULL;
508223637Sbz		matched = 0;
509223637Sbz
510126353Smlaier		while ((buf = fgetln(f, &len))) {
511223637Sbz
512126353Smlaier			if (buf[len - 1] == '\n')
513126353Smlaier				buf[len - 1] = '\0';
514126353Smlaier			else {
515126353Smlaier				if ((lbuf = (char *)malloc(len + 1)) == NULL)
516126353Smlaier					err(1, NULL);
517126353Smlaier				memcpy(lbuf, buf, len);
518126353Smlaier				lbuf[len] = '\0';
519126353Smlaier				buf = lbuf;
520126353Smlaier			}
521126353Smlaier
522223637Sbz			if (buf[0] == '@') {
523223637Sbz				/* check login class */
524223637Sbz				if (strcmp(pw->pw_class, buf + 1) == 0)
525223637Sbz					matched++;
526223637Sbz			} else if (buf[0] == '%') {
527223637Sbz				/* check group membership */
528223637Sbz				int cnt;
529223637Sbz				struct group *group;
530126353Smlaier
531223637Sbz				if ((group = getgrnam(buf + 1)) == NULL) {
532223637Sbz					syslog(LOG_ERR,
533223637Sbz					    "invalid group '%s' in %s (%s)",
534223637Sbz					    buf + 1, PATH_ALLOWFILE,
535223637Sbz				 	    strerror(errno));
536223637Sbz					return (0);
537223637Sbz				}
538223637Sbz
539223637Sbz				if (!gl_init) {
540223637Sbz					(void) getgrouplist(pw->pw_name,
541223637Sbz					    pw->pw_gid, groups, &ngroups);
542223637Sbz					gl_init++;
543223637Sbz				}
544223637Sbz
545223637Sbz				for ( cnt = 0; cnt < ngroups; cnt++) {
546223637Sbz					if (group->gr_gid == groups[cnt]) {
547223637Sbz						matched++;
548223637Sbz						break;
549223637Sbz					}
550223637Sbz				}
551223637Sbz			} else {
552223637Sbz				/* check username and wildcard */
553223637Sbz				matched = strcmp(pw->pw_name, buf) == 0 ||
554223637Sbz				    strcmp("*", buf) == 0;
555223637Sbz			}
556223637Sbz
557126353Smlaier			if (lbuf != NULL) {
558126353Smlaier				free(lbuf);
559126353Smlaier				lbuf = NULL;
560126353Smlaier			}
561126353Smlaier
562126353Smlaier			if (matched)
563223637Sbz				return (1); /* matched an allowed user/group */
564126353Smlaier		}
565126353Smlaier		syslog(LOG_INFO, "denied access to %s: not listed in %s",
566223637Sbz		    pw->pw_name, PATH_ALLOWFILE);
567126353Smlaier
568126353Smlaier		/* reuse buf */
569223637Sbz		sprintf(buf, "%s", "\n\nSorry, you are not allowed to use this facility!\n");
570126353Smlaier		fputs(buf, stdout);
571126353Smlaier	}
572126353Smlaier	fflush(stdout);
573126353Smlaier	return (0);
574126353Smlaier}
575126353Smlaier
576126353Smlaier/*
577126353Smlaier * check_luser checks to see if user "luser" has been banned
578126353Smlaier * from using us by virtue of having an file of the same name
579126353Smlaier * in the "luserdir" directory.
580126353Smlaier *
581126353Smlaier * If the user has been banned, we copy the contents of the file
582126353Smlaier * to the user's screen. (useful for telling the user what to
583126353Smlaier * do to get un-banned, or just to tell them they aren't
584126353Smlaier * going to be un-banned.)
585126353Smlaier */
586126353Smlaierstatic int
587223637Sbzcheck_luser(const char *luserdir, char *l_user)
588126353Smlaier{
589126353Smlaier	FILE	*f;
590126353Smlaier	int	 n;
591126353Smlaier	char	 tmp[MAXPATHLEN];
592126353Smlaier
593223637Sbz	n = snprintf(tmp, sizeof(tmp), "%s/%s", luserdir, l_user);
594126353Smlaier	if (n < 0 || (u_int)n >= sizeof(tmp)) {
595126353Smlaier		syslog(LOG_ERR, "provided banned directory line too long (%s)",
596126353Smlaier		    luserdir);
597126353Smlaier		return (0);
598126353Smlaier	}
599126353Smlaier	if ((f = fopen(tmp, "r")) == NULL) {
600126353Smlaier		if (errno == ENOENT) {
601126353Smlaier			/*
602126353Smlaier			 * file or dir doesn't exist, so therefore
603126353Smlaier			 * this luser isn't banned..  all is well
604126353Smlaier			 */
605126353Smlaier			return (1);
606126353Smlaier		} else {
607126353Smlaier			/*
608126353Smlaier			 * luser may in fact be banned, but we can't open the
609126353Smlaier			 * file even though it's there. probably a config
610126353Smlaier			 * problem.
611126353Smlaier			 */
612126353Smlaier			syslog(LOG_ERR, "cannot open banned file %s (%s)",
613126353Smlaier			    tmp, strerror(errno));
614126353Smlaier			return (0);
615126353Smlaier		}
616126353Smlaier	} else {
617126353Smlaier		/*
618126353Smlaier		 * luser is banned - spit the file at them to
619126353Smlaier		 * tell what they can do and where they can go.
620126353Smlaier		 */
621126353Smlaier		syslog(LOG_INFO, "denied access to %s: %s exists",
622223637Sbz		    l_user, tmp);
623126353Smlaier
624126353Smlaier		/* reuse tmp */
625126353Smlaier		strlcpy(tmp, "\n\n-**- Sorry, you have been banned! -**-\n\n",
626126353Smlaier		    sizeof(tmp));
627126353Smlaier		while (fputs(tmp, stdout) != EOF && !feof(f)) {
628126353Smlaier			if (fgets(tmp, sizeof(tmp), f) == NULL) {
629126353Smlaier				fflush(stdout);
630171172Smlaier				fclose(f);
631126353Smlaier				return (0);
632126353Smlaier			}
633126353Smlaier		}
634171172Smlaier		fclose(f);
635126353Smlaier	}
636126353Smlaier	fflush(stdout);
637126353Smlaier	return (0);
638126353Smlaier}
639126353Smlaier
640126353Smlaier/*
641126353Smlaier * Search for rulesets left by other authpf processes (either because they
642126353Smlaier * died ungracefully or were terminated) and remove them.
643126353Smlaier */
644126353Smlaierstatic int
645126353Smlaierremove_stale_rulesets(void)
646126353Smlaier{
647126353Smlaier	struct pfioc_ruleset	 prs;
648223637Sbz	u_int32_t		 nr;
649126353Smlaier
650126353Smlaier	memset(&prs, 0, sizeof(prs));
651145840Smlaier	strlcpy(prs.path, anchorname, sizeof(prs.path));
652126353Smlaier	if (ioctl(dev, DIOCGETRULESETS, &prs)) {
653126353Smlaier		if (errno == EINVAL)
654126353Smlaier			return (0);
655126353Smlaier		else
656126353Smlaier			return (1);
657126353Smlaier	}
658126353Smlaier
659223637Sbz	nr = prs.nr;
660223637Sbz	while (nr) {
661130617Smlaier		char	*s, *t;
662126353Smlaier		pid_t	 pid;
663126353Smlaier
664223637Sbz		prs.nr = nr - 1;
665126353Smlaier		if (ioctl(dev, DIOCGETRULESET, &prs))
666126353Smlaier			return (1);
667126353Smlaier		errno = 0;
668130617Smlaier		if ((t = strchr(prs.name, '(')) == NULL)
669130617Smlaier			t = prs.name;
670130617Smlaier		else
671130617Smlaier			t++;
672130617Smlaier		pid = strtoul(t, &s, 10);
673130617Smlaier		if (!prs.name[0] || errno ||
674130617Smlaier		    (*s && (t == prs.name || *s != ')')))
675126353Smlaier			return (1);
676223637Sbz		if ((kill(pid, 0) && errno != EPERM) || pid == getpid()) {
677223637Sbz			if (recursive_ruleset_purge(anchorname, prs.name))
678145840Smlaier				return (1);
679223637Sbz		}
680223637Sbz		nr--;
681126353Smlaier	}
682126353Smlaier	return (0);
683126353Smlaier}
684126353Smlaier
685223637Sbzstatic int
686223637Sbzrecursive_ruleset_purge(char *an, char *rs)
687223637Sbz{
688223637Sbz	struct pfioc_trans_e     *t_e = NULL;
689223637Sbz	struct pfioc_trans	 *t = NULL;
690223637Sbz	struct pfioc_ruleset	 *prs = NULL;
691223637Sbz	int			  i;
692223637Sbz
693223637Sbz
694223637Sbz	/* purge rules */
695223637Sbz	errno = 0;
696223637Sbz	if ((t = calloc(1, sizeof(struct pfioc_trans))) == NULL)
697223637Sbz		goto no_mem;
698223637Sbz	if ((t_e = calloc(PF_RULESET_MAX+1,
699223637Sbz	    sizeof(struct pfioc_trans_e))) == NULL)
700223637Sbz		goto no_mem;
701223637Sbz	t->size = PF_RULESET_MAX+1;
702223637Sbz	t->esize = sizeof(struct pfioc_trans_e);
703223637Sbz	t->array = t_e;
704223637Sbz	for (i = 0; i < PF_RULESET_MAX+1; ++i) {
705223637Sbz		t_e[i].rs_num = i;
706223637Sbz		snprintf(t_e[i].anchor, sizeof(t_e[i].anchor), "%s/%s", an, rs);
707223637Sbz	}
708223637Sbz	t_e[PF_RULESET_MAX].rs_num = PF_RULESET_TABLE;
709223637Sbz	if ((ioctl(dev, DIOCXBEGIN, t) ||
710223637Sbz	    ioctl(dev, DIOCXCOMMIT, t)) &&
711223637Sbz	    errno != EINVAL)
712223637Sbz		goto cleanup;
713223637Sbz
714223637Sbz	/* purge any children */
715223637Sbz	if ((prs = calloc(1, sizeof(struct pfioc_ruleset))) == NULL)
716223637Sbz		goto no_mem;
717223637Sbz	snprintf(prs->path, sizeof(prs->path), "%s/%s", an, rs);
718223637Sbz	if (ioctl(dev, DIOCGETRULESETS, prs)) {
719223637Sbz		if (errno != EINVAL)
720223637Sbz			goto cleanup;
721223637Sbz		errno = 0;
722223637Sbz	} else {
723223637Sbz		int nr = prs->nr;
724223637Sbz
725223637Sbz		while (nr) {
726223637Sbz			prs->nr = 0;
727223637Sbz			if (ioctl(dev, DIOCGETRULESET, prs))
728223637Sbz				goto cleanup;
729223637Sbz
730223637Sbz			if (recursive_ruleset_purge(prs->path, prs->name))
731223637Sbz				goto cleanup;
732223637Sbz			nr--;
733223637Sbz		}
734223637Sbz	}
735223637Sbz
736223637Sbzno_mem:
737223637Sbz	if (errno == ENOMEM)
738223637Sbz		syslog(LOG_ERR, "calloc failed");
739223637Sbz
740223637Sbzcleanup:
741223637Sbz	free(t);
742223637Sbz	free(t_e);
743223637Sbz	free(prs);
744223637Sbz	return (errno);
745223637Sbz}
746223637Sbz
747126353Smlaier/*
748126353Smlaier * Add/remove filter entries for user "luser" from ip "ipsrc"
749126353Smlaier */
750126353Smlaierstatic int
751223637Sbzchange_filter(int add, const char *l_user, const char *ip_src)
752126353Smlaier{
753145840Smlaier	char	*fdpath = NULL, *userstr = NULL, *ipstr = NULL;
754145840Smlaier	char	*rsn = NULL, *fn = NULL;
755145840Smlaier	pid_t	pid;
756171172Smlaier	gid_t   gid;
757145840Smlaier	int	s;
758126353Smlaier
759126353Smlaier	if (add) {
760145840Smlaier		struct stat sb;
761223637Sbz		char *pargv[13] = {
762223637Sbz			"pfctl", "-p", "/dev/pf", "-q", "-a", "anchor/ruleset",
763223637Sbz			"-D", "user_id=X", "-D", "user_ip=X", "-f", "file", NULL
764223637Sbz		};
765145840Smlaier
766223637Sbz		if (l_user == NULL || !l_user[0] || ip_src == NULL || !ip_src[0]) {
767223637Sbz			syslog(LOG_ERR, "invalid luser/ipsrc");
768223637Sbz			goto error;
769223637Sbz		}
770223637Sbz
771223637Sbz		if (asprintf(&rsn, "%s/%s", anchorname, rulesetname) == -1)
772145840Smlaier			goto no_mem;
773223637Sbz		if (asprintf(&fdpath, "/dev/fd/%d", dev) == -1)
774223637Sbz			goto no_mem;
775223637Sbz		if (asprintf(&ipstr, "user_ip=%s", ip_src) == -1)
776223637Sbz			goto no_mem;
777223637Sbz		if (asprintf(&userstr, "user_id=%s", l_user) == -1)
778223637Sbz			goto no_mem;
779223637Sbz		if (asprintf(&fn, "%s/%s/authpf.rules",
780223637Sbz		    PATH_USER_DIR, l_user) == -1)
781223637Sbz			goto no_mem;
782145840Smlaier		if (stat(fn, &sb) == -1) {
783145840Smlaier			free(fn);
784145840Smlaier			if ((fn = strdup(PATH_PFRULES)) == NULL)
785145840Smlaier				goto no_mem;
786126353Smlaier		}
787223637Sbz		pargv[2] = fdpath;
788223637Sbz		pargv[5] = rsn;
789223637Sbz		pargv[7] = userstr;
790223637Sbz		if (user_ip) {
791223637Sbz			pargv[9] = ipstr;
792223637Sbz			pargv[11] = fn;
793223637Sbz		} else {
794223637Sbz			pargv[8] = "-f";
795223637Sbz			pargv[9] = fn;
796223637Sbz			pargv[10] = NULL;
797223637Sbz		}
798126353Smlaier
799223637Sbz		switch (pid = fork()) {
800223637Sbz		case -1:
801223637Sbz			syslog(LOG_ERR, "fork failed");
802223637Sbz			goto error;
803223637Sbz		case 0:
804223637Sbz			/* revoke group privs before exec */
805223637Sbz			gid = getgid();
806223637Sbz			if (setregid(gid, gid) == -1) {
807223637Sbz				err(1, "setregid");
808223637Sbz			}
809223637Sbz			execvp(PATH_PFCTL, pargv);
810223637Sbz			warn("exec of %s failed", PATH_PFCTL);
811223637Sbz			_exit(1);
812171172Smlaier		}
813126353Smlaier
814223637Sbz		/* parent */
815223637Sbz		waitpid(pid, &s, 0);
816223637Sbz		if (s != 0) {
817223637Sbz			syslog(LOG_ERR, "pfctl exited abnormally");
818223637Sbz			goto error;
819223637Sbz		}
820126353Smlaier
821126353Smlaier		gettimeofday(&Tstart, NULL);
822223637Sbz		syslog(LOG_INFO, "allowing %s, user %s", ip_src, l_user);
823126353Smlaier	} else {
824223637Sbz		remove_stale_rulesets();
825223637Sbz
826126353Smlaier		gettimeofday(&Tend, NULL);
827223637Sbz		syslog(LOG_INFO, "removed %s, user %s - duration %ju seconds",
828223637Sbz		    ip_src, l_user, (uintmax_t)(Tend.tv_sec - Tstart.tv_sec));
829126353Smlaier	}
830126353Smlaier	return (0);
831145840Smlaierno_mem:
832145840Smlaier	syslog(LOG_ERR, "malloc failed");
833126353Smlaiererror:
834145840Smlaier	free(fdpath);
835145840Smlaier	free(rsn);
836145840Smlaier	free(userstr);
837145840Smlaier	free(ipstr);
838145840Smlaier	free(fn);
839126353Smlaier	return (-1);
840126353Smlaier}
841126353Smlaier
842126353Smlaier/*
843145840Smlaier * Add/remove this IP from the "authpf_users" table.
844145840Smlaier */
845145840Smlaierstatic int
846223637Sbzchange_table(int add, const char *ip_src)
847145840Smlaier{
848145840Smlaier	struct pfioc_table	io;
849145840Smlaier	struct pfr_addr		addr;
850145840Smlaier
851145840Smlaier	bzero(&io, sizeof(io));
852171172Smlaier	strlcpy(io.pfrio_table.pfrt_name, tablename,
853171172Smlaier	    sizeof(io.pfrio_table.pfrt_name));
854145840Smlaier	io.pfrio_buffer = &addr;
855145840Smlaier	io.pfrio_esize = sizeof(addr);
856145840Smlaier	io.pfrio_size = 1;
857145840Smlaier
858145840Smlaier	bzero(&addr, sizeof(addr));
859223637Sbz	if (ip_src == NULL || !ip_src[0])
860145840Smlaier		return (-1);
861223637Sbz	if (inet_pton(AF_INET, ip_src, &addr.pfra_ip4addr) == 1) {
862145840Smlaier		addr.pfra_af = AF_INET;
863145840Smlaier		addr.pfra_net = 32;
864223637Sbz	} else if (inet_pton(AF_INET6, ip_src, &addr.pfra_ip6addr) == 1) {
865145840Smlaier		addr.pfra_af = AF_INET6;
866145840Smlaier		addr.pfra_net = 128;
867145840Smlaier	} else {
868145840Smlaier		syslog(LOG_ERR, "invalid ipsrc");
869145840Smlaier		return (-1);
870145840Smlaier	}
871145840Smlaier
872145840Smlaier	if (ioctl(dev, add ? DIOCRADDADDRS : DIOCRDELADDRS, &io) &&
873145840Smlaier	    errno != ESRCH) {
874145840Smlaier		syslog(LOG_ERR, "cannot %s %s from table %s: %s",
875223637Sbz		    add ? "add" : "remove", ip_src, tablename,
876145840Smlaier		    strerror(errno));
877145840Smlaier		return (-1);
878145840Smlaier	}
879145840Smlaier	return (0);
880145840Smlaier}
881145840Smlaier
882145840Smlaier/*
883126353Smlaier * This is to kill off states that would otherwise be left behind stateful
884126353Smlaier * rules. This means we don't need to allow in more traffic than we really
885126353Smlaier * want to, since we don't have to worry about any luser sessions lasting
886126353Smlaier * longer than their ssh session. This function is based on
887126353Smlaier * pfctl_kill_states from pfctl.
888126353Smlaier */
889126353Smlaierstatic void
890126353Smlaierauthpf_kill_states(void)
891126353Smlaier{
892126353Smlaier	struct pfioc_state_kill	psk;
893145840Smlaier	struct pf_addr target;
894126353Smlaier
895126353Smlaier	memset(&psk, 0, sizeof(psk));
896145840Smlaier	memset(&target, 0, sizeof(target));
897126353Smlaier
898145840Smlaier	if (inet_pton(AF_INET, ipsrc, &target.v4) == 1)
899145840Smlaier		psk.psk_af = AF_INET;
900145840Smlaier	else if (inet_pton(AF_INET6, ipsrc, &target.v6) == 1)
901145840Smlaier		psk.psk_af = AF_INET6;
902145840Smlaier	else {
903145840Smlaier		syslog(LOG_ERR, "inet_pton(%s) failed", ipsrc);
904145840Smlaier		return;
905145840Smlaier	}
906126353Smlaier
907126353Smlaier	/* Kill all states from ipsrc */
908145840Smlaier	memcpy(&psk.psk_src.addr.v.a.addr, &target,
909145840Smlaier	    sizeof(psk.psk_src.addr.v.a.addr));
910126353Smlaier	memset(&psk.psk_src.addr.v.a.mask, 0xff,
911126353Smlaier	    sizeof(psk.psk_src.addr.v.a.mask));
912126353Smlaier	if (ioctl(dev, DIOCKILLSTATES, &psk))
913126353Smlaier		syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
914126353Smlaier
915126353Smlaier	/* Kill all states to ipsrc */
916126353Smlaier	memset(&psk.psk_src, 0, sizeof(psk.psk_src));
917145840Smlaier	memcpy(&psk.psk_dst.addr.v.a.addr, &target,
918145840Smlaier	    sizeof(psk.psk_dst.addr.v.a.addr));
919126353Smlaier	memset(&psk.psk_dst.addr.v.a.mask, 0xff,
920126353Smlaier	    sizeof(psk.psk_dst.addr.v.a.mask));
921126353Smlaier	if (ioctl(dev, DIOCKILLSTATES, &psk))
922126353Smlaier		syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
923126353Smlaier}
924126353Smlaier
925126353Smlaier/* signal handler that makes us go away properly */
926126353Smlaierstatic void
927223637Sbzneed_death(int signo __unused)
928126353Smlaier{
929126353Smlaier	want_death = 1;
930126353Smlaier}
931126353Smlaier
932126353Smlaier/*
933126353Smlaier * function that removes our stuff when we go away.
934126353Smlaier */
935127024Smlaier#ifdef __FreeBSD__
936127024Smlaierstatic __dead2 void
937127024Smlaier#else
938126353Smlaierstatic __dead void
939127024Smlaier#endif
940126353Smlaierdo_death(int active)
941126353Smlaier{
942126353Smlaier	int	ret = 0;
943126353Smlaier
944126353Smlaier	if (active) {
945126353Smlaier		change_filter(0, luser, ipsrc);
946223637Sbz		if (user_ip) {
947223637Sbz			change_table(0, ipsrc);
948223637Sbz			authpf_kill_states();
949223637Sbz		}
950126353Smlaier	}
951223637Sbz	if (pidfile[0] && pidfd != -1)
952126353Smlaier		if (unlink(pidfile) == -1)
953126353Smlaier			syslog(LOG_ERR, "cannot unlink %s (%m)", pidfile);
954126353Smlaier	exit(ret);
955126353Smlaier}
956