GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/authpf/authpf.c Lines: 0 454 0.0 %
Date: 2017-11-13 Branches: 0 347 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: authpf.c,v 1.125 2016/03/29 14:53:27 mestre Exp $	*/
2
3
/*
4
 * Copyright (C) 1998 - 2007 Bob Beck (beck@openbsd.org).
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/file.h>
21
#include <sys/ioctl.h>
22
#include <sys/socket.h>
23
#include <sys/stat.h>
24
#include <sys/time.h>
25
#include <sys/wait.h>
26
27
#include <netinet/in.h>
28
#include <arpa/inet.h>
29
#include <net/if.h>
30
#include <net/pfvar.h>
31
32
#include <err.h>
33
#include <errno.h>
34
#include <login_cap.h>
35
#include <pwd.h>
36
#include <grp.h>
37
#include <signal.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <syslog.h>
42
#include <unistd.h>
43
44
#include "pathnames.h"
45
46
static int	read_config(FILE *);
47
static void	print_message(char *);
48
static int	allowed_luser(struct passwd *);
49
static int	check_luser(char *, char *);
50
static int	remove_stale_rulesets(void);
51
static int	recursive_ruleset_purge(char *, char *);
52
static int	change_filter(int, const char *, const char *);
53
static int	change_table(int, const char *);
54
static void	authpf_kill_states(void);
55
56
int	dev;			/* pf device */
57
char	anchorname[PF_ANCHOR_NAME_SIZE] = "authpf";
58
char	rulesetname[PATH_MAX - PF_ANCHOR_NAME_SIZE - 2];
59
char	tablename[PF_TABLE_NAME_SIZE] = "authpf_users";
60
int	user_ip = 1;	/* controls whether $user_ip is set */
61
62
FILE	*pidfp;
63
int	pidfd = -1;
64
char	 luser[LOGIN_NAME_MAX];	/* username */
65
char	 ipsrc[256];		/* ip as a string */
66
char	 pidfile[PATH_MAX];	/* we save pid in this file. */
67
68
struct timeval	Tstart, Tend;	/* start and end times of session */
69
70
volatile sig_atomic_t	want_death;
71
static void		need_death(int signo);
72
static __dead void	do_death(int);
73
extern char *__progname;	/* program name */
74
75
/*
76
 * User shell for authenticating gateways. Sole purpose is to allow
77
 * a user to ssh to a gateway, and have the gateway modify packet
78
 * filters to allow access, then remove access when the user finishes
79
 * up. Meant to be used only from ssh(1) connections.
80
 */
81
int
82
main(int argc, char *argv[])
83
{
84
	int		 lockcnt = 0, n;
85
	FILE		*config;
86
	struct in6_addr	 ina;
87
	struct passwd	*pw;
88
	char		*cp;
89
	gid_t		 gid;
90
	uid_t		 uid;
91
	char		*shell;
92
	login_cap_t	*lc;
93
94
	if (strcmp(__progname, "-authpf-noip") == 0)
95
                user_ip = 0;
96
97
	config = fopen(PATH_CONFFILE, "r");
98
	if (config == NULL) {
99
		syslog(LOG_ERR, "cannot open %s (%m)", PATH_CONFFILE);
100
		exit(1);
101
	}
102
103
	if ((cp = getenv("SSH_TTY")) == NULL) {
104
		syslog(LOG_ERR, "non-interactive session connection for authpf");
105
		exit(1);
106
	}
107
108
	if ((cp = getenv("SSH_CLIENT")) == NULL) {
109
		syslog(LOG_ERR, "cannot determine connection source");
110
		exit(1);
111
	}
112
113
	if (strlcpy(ipsrc, cp, sizeof(ipsrc)) >= sizeof(ipsrc)) {
114
		syslog(LOG_ERR, "SSH_CLIENT variable too long");
115
		exit(1);
116
	}
117
	cp = strchr(ipsrc, ' ');
118
	if (!cp) {
119
		syslog(LOG_ERR, "corrupt SSH_CLIENT variable %s", ipsrc);
120
		exit(1);
121
	}
122
	*cp = '\0';
123
	if (inet_pton(AF_INET, ipsrc, &ina) != 1 &&
124
	    inet_pton(AF_INET6, ipsrc, &ina) != 1) {
125
		syslog(LOG_ERR,
126
		    "cannot determine IP from SSH_CLIENT %s", ipsrc);
127
		exit(1);
128
	}
129
	/* open the pf device */
130
	dev = open(PATH_DEVFILE, O_RDWR);
131
	if (dev == -1) {
132
		syslog(LOG_ERR, "cannot open packet filter device (%m)");
133
		goto die;
134
	}
135
136
	uid = getuid();
137
	pw = getpwuid(uid);
138
	if (pw == NULL) {
139
		syslog(LOG_ERR, "cannot find user for uid %u", uid);
140
		goto die;
141
	}
142
143
	if ((lc = login_getclass(pw->pw_class)) != NULL)
144
		shell = login_getcapstr(lc, "shell", pw->pw_shell,
145
		    pw->pw_shell);
146
	else
147
		shell = pw->pw_shell;
148
149
	login_close(lc);
150
151
	if (strcmp(shell, PATH_AUTHPF_SHELL) &&
152
	    strcmp(shell, PATH_AUTHPF_SHELL_NOIP)) {
153
		syslog(LOG_ERR, "wrong shell for user %s, uid %u",
154
		    pw->pw_name, pw->pw_uid);
155
		if (shell != pw->pw_shell)
156
			free(shell);
157
		goto die;
158
	}
159
160
	if (shell != pw->pw_shell)
161
		free(shell);
162
163
	/*
164
	 * Paranoia, but this data _does_ come from outside authpf, and
165
	 * truncation would be bad.
166
	 */
167
	if (strlcpy(luser, pw->pw_name, sizeof(luser)) >= sizeof(luser)) {
168
		syslog(LOG_ERR, "username too long: %s", pw->pw_name);
169
		goto die;
170
	}
171
172
	if ((n = snprintf(rulesetname, sizeof(rulesetname), "%s(%ld)",
173
	    luser, (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) {
174
		syslog(LOG_INFO, "%s(%ld) too large, ruleset name will be %ld",
175
		    luser, (long)getpid(), (long)getpid());
176
		if ((n = snprintf(rulesetname, sizeof(rulesetname), "%ld",
177
		    (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) {
178
			syslog(LOG_ERR, "pid too large for ruleset name");
179
			goto die;
180
		}
181
	}
182
183
184
	/* Make our entry in /var/authpf as ipaddr or username */
185
	n = snprintf(pidfile, sizeof(pidfile), "%s/%s",
186
	    PATH_PIDFILE, user_ip ? ipsrc : luser);
187
	if (n < 0 || (u_int)n >= sizeof(pidfile)) {
188
		syslog(LOG_ERR, "path to pidfile too long");
189
		goto die;
190
	}
191
192
	signal(SIGTERM, need_death);
193
	signal(SIGINT, need_death);
194
	signal(SIGALRM, need_death);
195
	signal(SIGPIPE, need_death);
196
	signal(SIGHUP, need_death);
197
	signal(SIGQUIT, need_death);
198
	signal(SIGTSTP, need_death);
199
200
	/*
201
	 * If someone else is already using this ip, then this person
202
	 * wants to switch users - so kill the old process and exit
203
	 * as well.
204
	 *
205
	 * Note, we could print a message and tell them to log out, but the
206
	 * usual case of this is that someone has left themselves logged in,
207
	 * with the authenticated connection iconized and someone else walks
208
	 * up to use and automatically logs in before using. If this just
209
	 * gets rid of the old one silently, the new user never knows they
210
	 * could have used someone else's old authentication. If we
211
	 * tell them to log out before switching users it is an invitation
212
	 * for abuse.
213
	 */
214
215
	do {
216
		int	save_errno, otherpid = -1;
217
		char	otherluser[LOGIN_NAME_MAX];
218
219
		if ((pidfd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1 ||
220
		    (pidfp = fdopen(pidfd, "r+")) == NULL) {
221
			if (pidfd != -1)
222
				close(pidfd);
223
			syslog(LOG_ERR, "cannot open or create %s: %s", pidfile,
224
			    strerror(errno));
225
			goto die;
226
		}
227
228
		if (flock(fileno(pidfp), LOCK_EX|LOCK_NB) == 0)
229
			break;
230
		save_errno = errno;
231
232
		/* Mark our pid, and username to our file. */
233
234
		rewind(pidfp);
235
		/* 31 == MAXLOGNAME - 1 */
236
		if (fscanf(pidfp, "%d\n%31s\n", &otherpid, otherluser) != 2)
237
			otherpid = -1;
238
		syslog(LOG_DEBUG, "tried to lock %s, in use by pid %d: %s",
239
		    pidfile, otherpid, strerror(save_errno));
240
241
		if (otherpid > 0) {
242
			syslog(LOG_INFO,
243
			    "killing prior auth (pid %d) of %s by user %s",
244
			    otherpid, ipsrc, otherluser);
245
			if (kill((pid_t) otherpid, SIGTERM) == -1) {
246
				syslog(LOG_INFO,
247
				    "could not kill process %d: (%m)",
248
				    otherpid);
249
			}
250
		}
251
252
		/*
253
		 * We try to kill the previous process and acquire the lock
254
		 * for 10 seconds, trying once a second. if we can't after
255
		 * 10 attempts we log an error and give up.
256
		 */
257
		if (want_death || ++lockcnt > 10) {
258
			if (!want_death)
259
				syslog(LOG_ERR, "cannot kill previous authpf (pid %d)",
260
				    otherpid);
261
			fclose(pidfp);
262
			pidfp = NULL;
263
			pidfd = -1;
264
			goto dogdeath;
265
		}
266
		sleep(1);
267
268
		/* re-open, and try again. The previous authpf process
269
		 * we killed above should unlink the file and release
270
		 * it's lock, giving us a chance to get it now
271
		 */
272
		fclose(pidfp);
273
		pidfp = NULL;
274
		pidfd = -1;
275
	} while (1);
276
277
	/* whack the group list */
278
	gid = getegid();
279
	if (setgroups(1, &gid) == -1) {
280
		syslog(LOG_INFO, "setgroups: %s", strerror(errno));
281
		do_death(0);
282
	}
283
284
	/* revoke privs */
285
	uid = getuid();
286
	if (setresuid(uid, uid, uid) == -1) {
287
		syslog(LOG_INFO, "setresuid: %s", strerror(errno));
288
		do_death(0);
289
	}
290
	openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON);
291
292
	if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(pw)) {
293
		syslog(LOG_INFO, "user %s prohibited", luser);
294
		do_death(0);
295
	}
296
297
	if (read_config(config)) {
298
		syslog(LOG_ERR, "invalid config file %s", PATH_CONFFILE);
299
		do_death(0);
300
	}
301
302
	if (remove_stale_rulesets()) {
303
		syslog(LOG_INFO, "error removing stale rulesets");
304
		do_death(0);
305
	}
306
307
	/* We appear to be making headway, so actually mark our pid */
308
	rewind(pidfp);
309
	fprintf(pidfp, "%ld\n%s\n", (long)getpid(), luser);
310
	fflush(pidfp);
311
	(void) ftruncate(fileno(pidfp), ftello(pidfp));
312
313
	if (change_filter(1, luser, ipsrc) == -1) {
314
		printf("Unable to modify filters\r\n");
315
		do_death(0);
316
	}
317
	if (user_ip && change_table(1, ipsrc) == -1) {
318
		printf("Unable to modify table\r\n");
319
		change_filter(0, luser, ipsrc);
320
		do_death(0);
321
	}
322
323
	while (1) {
324
		struct stat sb;
325
		char *path_message;
326
		printf("\r\nHello %s. ", luser);
327
		printf("You are authenticated from host \"%s\"\r\n", ipsrc);
328
		setproctitle("%s@%s", luser, ipsrc);
329
		if (asprintf(&path_message, "%s/%s/authpf.message",
330
		    PATH_USER_DIR, luser) == -1)
331
			do_death(1);
332
		if (stat(path_message, &sb) == -1 || ! S_ISREG(sb.st_mode)) {
333
			free(path_message);
334
			if ((path_message = strdup(PATH_MESSAGE)) == NULL)
335
				do_death(1);
336
		}
337
		print_message(path_message);
338
		while (1) {
339
			sleep(10);
340
			if (want_death)
341
				do_death(1);
342
		}
343
	}
344
345
dogdeath:
346
	printf("\r\n\r\nSorry, this service is currently unavailable due to ");
347
	printf("technical difficulties\r\n\r\n");
348
	print_message(PATH_PROBLEM);
349
	printf("\r\nYour authentication process (pid %ld) was unable to run\n",
350
	    (long)getpid());
351
	sleep(180); /* them lusers read reaaaaal slow */
352
die:
353
	do_death(0);
354
}
355
356
/*
357
 * reads config file in PATH_CONFFILE to set optional behaviours up
358
 */
359
static int
360
read_config(FILE *f)
361
{
362
	char	buf[1024];
363
	int	i = 0;
364
365
	do {
366
		char	**ap;
367
		char	 *pair[4], *cp, *tp;
368
		int	  len;
369
370
		if (fgets(buf, sizeof(buf), f) == NULL) {
371
			fclose(f);
372
			return (0);
373
		}
374
		i++;
375
		len = strlen(buf);
376
		if (len == 0)
377
			continue;
378
		if (buf[len - 1] != '\n' && !feof(f)) {
379
			syslog(LOG_ERR, "line %d too long in %s", i,
380
			    PATH_CONFFILE);
381
			return (1);
382
		}
383
		buf[len - 1] = '\0';
384
385
		for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
386
			; /* nothing */
387
388
		if (!*cp || *cp == '#' || *cp == '\n')
389
			continue;
390
391
		for (ap = pair; ap < &pair[3] &&
392
		    (*ap = strsep(&cp, "=")) != NULL; ) {
393
			if (**ap != '\0')
394
				ap++;
395
		}
396
		if (ap != &pair[2])
397
			goto parse_error;
398
399
		tp = pair[1] + strlen(pair[1]);
400
		while ((*tp == ' ' || *tp == '\t') && tp >= pair[1])
401
			*tp-- = '\0';
402
403
		if (strcasecmp(pair[0], "anchor") == 0) {
404
			if (!pair[1][0] || strlcpy(anchorname, pair[1],
405
			    sizeof(anchorname)) >= sizeof(anchorname))
406
				goto parse_error;
407
		}
408
		if (strcasecmp(pair[0], "table") == 0) {
409
			if (!pair[1][0] || strlcpy(tablename, pair[1],
410
			    sizeof(tablename)) >= sizeof(tablename))
411
				goto parse_error;
412
		}
413
	} while (!feof(f) && !ferror(f));
414
	fclose(f);
415
	return (0);
416
417
parse_error:
418
	fclose(f);
419
	syslog(LOG_ERR, "parse error, line %d of %s", i, PATH_CONFFILE);
420
	return (1);
421
}
422
423
424
/*
425
 * splatter a file to stdout - max line length of 1024,
426
 * used for spitting message files at users to tell them
427
 * they've been bad or we're unavailable.
428
 */
429
static void
430
print_message(char *filename)
431
{
432
	char	 buf[1024];
433
	FILE	*f;
434
435
	if ((f = fopen(filename, "r")) == NULL)
436
		return; /* fail silently, we don't care if it isn't there */
437
438
	do {
439
		if (fgets(buf, sizeof(buf), f) == NULL) {
440
			fflush(stdout);
441
			fclose(f);
442
			return;
443
		}
444
	} while (fputs(buf, stdout) != EOF && !feof(f));
445
	fflush(stdout);
446
	fclose(f);
447
}
448
449
/*
450
 * allowed_luser checks to see if user "luser" is allowed to
451
 * use this gateway by virtue of being listed in an allowed
452
 * users file, namely /etc/authpf/authpf.allow .
453
 * Users may be listed by <username>, %<group>, or @<login_class>.
454
 *
455
 * If /etc/authpf/authpf.allow does not exist, then we assume that
456
 * all users who are allowed in by sshd(8) are permitted to
457
 * use this gateway. If /etc/authpf/authpf.allow does exist, then a
458
 * user must be listed if the connection is to continue, else
459
 * the session terminates in the same manner as being banned.
460
 */
461
static int
462
allowed_luser(struct passwd *pw)
463
{
464
	char	*buf, *lbuf;
465
	int	 matched;
466
	size_t	 len;
467
	FILE	*f;
468
469
	if ((f = fopen(PATH_ALLOWFILE, "r")) == NULL) {
470
		if (errno == ENOENT) {
471
			/*
472
			 * allowfile doesn't exist, thus this gateway
473
			 * isn't restricted to certain users...
474
			 */
475
			return (1);
476
		}
477
478
		/*
479
		 * luser may in fact be allowed, but we can't open
480
		 * the file even though it's there. probably a config
481
		 * problem.
482
		 */
483
		syslog(LOG_ERR, "cannot open allowed users file %s (%s)",
484
		    PATH_ALLOWFILE, strerror(errno));
485
		return (0);
486
	} else {
487
		/*
488
		 * /etc/authpf/authpf.allow exists, thus we do a linear
489
		 * search to see if they are allowed.
490
		 * also, if username "*" exists, then this is a
491
		 * "public" gateway, such as it is, so let
492
		 * everyone use it.
493
		 */
494
		int gl_init = 0, ngroups = NGROUPS_MAX + 1;
495
		gid_t groups[NGROUPS_MAX + 1];
496
497
		lbuf = NULL;
498
		matched = 0;
499
500
		while ((buf = fgetln(f, &len))) {
501
502
			if (buf[len - 1] == '\n')
503
				buf[len - 1] = '\0';
504
			else {
505
				if ((lbuf = malloc(len + 1)) == NULL)
506
					err(1, NULL);
507
				memcpy(lbuf, buf, len);
508
				lbuf[len] = '\0';
509
				buf = lbuf;
510
			}
511
512
			if (buf[0] == '@') {
513
				/* check login class */
514
				if (strcmp(pw->pw_class, buf + 1) == 0)
515
					matched++;
516
			} else if (buf[0] == '%') {
517
				/* check group membership */
518
				int cnt;
519
				struct group *group;
520
521
				if ((group = getgrnam(buf + 1)) == NULL) {
522
					syslog(LOG_ERR,
523
					    "invalid group '%s' in %s (%s)",
524
					    buf + 1, PATH_ALLOWFILE,
525
				 	    strerror(errno));
526
					fclose(f);
527
					return (0);
528
				}
529
530
				if (!gl_init) {
531
					(void) getgrouplist(pw->pw_name,
532
					    pw->pw_gid, groups, &ngroups);
533
					gl_init++;
534
				}
535
536
				for ( cnt = 0; cnt < ngroups; cnt++) {
537
					if (group->gr_gid == groups[cnt]) {
538
						matched++;
539
						break;
540
					}
541
				}
542
			} else {
543
				/* check username and wildcard */
544
				matched = strcmp(pw->pw_name, buf) == 0 ||
545
				    strcmp("*", buf) == 0;
546
			}
547
548
			free(lbuf);
549
			lbuf = NULL;
550
551
			if (matched) {
552
				fclose(f);
553
				return (1); /* matched an allowed user/group */
554
			}
555
		}
556
		syslog(LOG_INFO, "denied access to %s: not listed in %s",
557
		    pw->pw_name, PATH_ALLOWFILE);
558
559
		/* reuse buf */
560
		buf = "\n\nSorry, you are not allowed to use this facility!\n";
561
		fputs(buf, stdout);
562
	}
563
	fflush(stdout);
564
	fclose(f);
565
	return (0);
566
}
567
568
/*
569
 * check_luser checks to see if user "luser" has been banned
570
 * from using us by virtue of having an file of the same name
571
 * in the "luserdir" directory.
572
 *
573
 * If the user has been banned, we copy the contents of the file
574
 * to the user's screen. (useful for telling the user what to
575
 * do to get un-banned, or just to tell them they aren't
576
 * going to be un-banned.)
577
 */
578
static int
579
check_luser(char *luserdir, char *luser)
580
{
581
	FILE	*f;
582
	int	 n;
583
	char	 tmp[PATH_MAX];
584
585
	n = snprintf(tmp, sizeof(tmp), "%s/%s", luserdir, luser);
586
	if (n < 0 || (u_int)n >= sizeof(tmp)) {
587
		syslog(LOG_ERR, "provided banned directory line too long (%s)",
588
		    luserdir);
589
		return (0);
590
	}
591
	if ((f = fopen(tmp, "r")) == NULL) {
592
		if (errno == ENOENT) {
593
			/*
594
			 * file or dir doesn't exist, so therefore
595
			 * this luser isn't banned..  all is well
596
			 */
597
			return (1);
598
		} else {
599
			/*
600
			 * luser may in fact be banned, but we can't open the
601
			 * file even though it's there. probably a config
602
			 * problem.
603
			 */
604
			syslog(LOG_ERR, "cannot open banned file %s (%s)",
605
			    tmp, strerror(errno));
606
			return (0);
607
		}
608
	} else {
609
		/*
610
		 * luser is banned - spit the file at them to
611
		 * tell what they can do and where they can go.
612
		 */
613
		syslog(LOG_INFO, "denied access to %s: %s exists",
614
		    luser, tmp);
615
616
		/* reuse tmp */
617
		strlcpy(tmp, "\n\n-**- Sorry, you have been banned! -**-\n\n",
618
		    sizeof(tmp));
619
		while (fputs(tmp, stdout) != EOF && !feof(f)) {
620
			if (fgets(tmp, sizeof(tmp), f) == NULL) {
621
				fflush(stdout);
622
				fclose(f);
623
				return (0);
624
			}
625
		}
626
		fclose(f);
627
	}
628
	fflush(stdout);
629
	return (0);
630
}
631
632
/*
633
 * Search for rulesets left by other authpf processes (either because they
634
 * died ungracefully or were terminated) and remove them.
635
 */
636
static int
637
remove_stale_rulesets(void)
638
{
639
	struct pfioc_ruleset	 prs;
640
	u_int32_t		 nr;
641
642
	memset(&prs, 0, sizeof(prs));
643
	strlcpy(prs.path, anchorname, sizeof(prs.path));
644
	if (ioctl(dev, DIOCGETRULESETS, &prs)) {
645
		if (errno == EINVAL)
646
			return (0);
647
		else
648
			return (1);
649
	}
650
651
	nr = prs.nr;
652
	while (nr) {
653
		char	*s, *t;
654
		pid_t	 pid;
655
656
		prs.nr = nr - 1;
657
		if (ioctl(dev, DIOCGETRULESET, &prs))
658
			return (1);
659
		errno = 0;
660
		if ((t = strchr(prs.name, '(')) == NULL)
661
			t = prs.name;
662
		else
663
			t++;
664
		pid = strtoul(t, &s, 10);
665
		if (!prs.name[0] || errno ||
666
		    (*s && (t == prs.name || *s != ')')))
667
			return (1);
668
		if ((kill(pid, 0) && errno != EPERM) || pid == getpid()) {
669
			if (recursive_ruleset_purge(anchorname, prs.name))
670
				return (1);
671
		}
672
		nr--;
673
	}
674
	return (0);
675
}
676
677
static int
678
recursive_ruleset_purge(char *an, char *rs)
679
{
680
	struct pfioc_trans_e     *t_e = NULL;
681
	struct pfioc_trans	 *t = NULL;
682
	struct pfioc_ruleset	 *prs = NULL;
683
684
	/* purge rules */
685
	errno = 0;
686
	if ((t = calloc(1, sizeof(struct pfioc_trans))) == NULL)
687
		goto no_mem;
688
	if ((t_e = calloc(2, sizeof(struct pfioc_trans_e))) == NULL)
689
		goto no_mem;
690
	t->size = 2;
691
	t->esize = sizeof(struct pfioc_trans_e);
692
	t->array = t_e;
693
	t_e[0].type = PF_TRANS_RULESET;
694
	snprintf(t_e[0].anchor, sizeof(t_e[0].anchor), "%s/%s", an, rs);
695
	t_e[1].type = PF_TRANS_TABLE;
696
697
	if ((ioctl(dev, DIOCXBEGIN, t) ||
698
	    ioctl(dev, DIOCXCOMMIT, t)) &&
699
	    errno != EINVAL)
700
		goto cleanup;
701
702
	/* purge any children */
703
	if ((prs = calloc(1, sizeof(struct pfioc_ruleset))) == NULL)
704
		goto no_mem;
705
	snprintf(prs->path, sizeof(prs->path), "%s/%s", an, rs);
706
	if (ioctl(dev, DIOCGETRULESETS, prs)) {
707
		if (errno != EINVAL)
708
			goto cleanup;
709
		errno = 0;
710
	} else {
711
		int nr = prs->nr;
712
713
		while (nr) {
714
			prs->nr = 0;
715
			if (ioctl(dev, DIOCGETRULESET, prs))
716
				goto cleanup;
717
718
			if (recursive_ruleset_purge(prs->path, prs->name))
719
				goto cleanup;
720
			nr--;
721
		}
722
	}
723
724
no_mem:
725
	if (errno == ENOMEM)
726
		syslog(LOG_ERR, "calloc failed");
727
728
cleanup:
729
	free(t);
730
	free(t_e);
731
	free(prs);
732
	return (errno);
733
}
734
735
/*
736
 * Add/remove filter entries for user "luser" from ip "ipsrc"
737
 */
738
static int
739
change_filter(int add, const char *luser, const char *ipsrc)
740
{
741
	char	*fdpath = NULL, *userstr = NULL, *ipstr = NULL;
742
	char	*rsn = NULL, *fn = NULL;
743
	pid_t	pid;
744
	gid_t   gid;
745
	int	s;
746
747
	if (add) {
748
		struct stat sb;
749
		struct group *grent;
750
		char	*pargv[13] = {
751
			"pfctl", "-p", "/dev/pf", "-q", "-a", "anchor/ruleset",
752
			"-D", "user_id=X", "-D", "user_ip=X", "-f", "file", NULL
753
		};
754
755
		if((grent = getgrgid(getgid())) == NULL) {
756
			syslog(LOG_ERR, "Group not found user %s, gid %d",
757
			    luser, getgid());
758
			goto error;
759
		}
760
761
		if (luser == NULL || !luser[0] || ipsrc == NULL || !ipsrc[0]) {
762
			syslog(LOG_ERR, "invalid luser/ipsrc");
763
			goto error;
764
		}
765
766
		if (asprintf(&rsn, "%s/%s", anchorname, rulesetname) == -1)
767
			goto no_mem;
768
		if (asprintf(&fdpath, "/dev/fd/%d", dev) == -1)
769
			goto no_mem;
770
		if (asprintf(&ipstr, "user_ip=%s", ipsrc) == -1)
771
			goto no_mem;
772
		if (asprintf(&userstr, "user_id=%s", luser) == -1)
773
			goto no_mem;
774
		if (asprintf(&fn, "%s/%s/authpf.rules",
775
		    PATH_USER_DIR, luser) == -1)
776
			goto no_mem;
777
		if (stat(fn, &sb) == -1) {
778
			free(fn);
779
			if(asprintf(&fn, "%s/%s/authpf.rules", PATH_GROUP_DIR,
780
				grent->gr_name) == -1)
781
				goto no_mem;
782
			if(stat(fn, &sb) == -1) {
783
				free(fn);
784
				if ((fn = strdup(PATH_PFRULES)) == NULL)
785
					goto no_mem;
786
			}
787
		}
788
		pargv[2] = fdpath;
789
		pargv[5] = rsn;
790
		pargv[7] = userstr;
791
		if (user_ip) {
792
			pargv[9] = ipstr;
793
			pargv[11] = fn;
794
		} else {
795
			pargv[8] = "-f";
796
			pargv[9] = fn;
797
			pargv[10] = NULL;
798
		}
799
800
		switch (pid = fork()) {
801
		case -1:
802
			syslog(LOG_ERR, "fork failed");
803
			goto error;
804
		case 0:
805
			/* revoke group privs before exec */
806
			gid = getgid();
807
			if (setresgid(gid, gid, gid) == -1) {
808
				err(1, "setregid");
809
			}
810
			execvp(PATH_PFCTL, pargv);
811
			warn("exec of %s failed", PATH_PFCTL);
812
			_exit(1);
813
		}
814
815
		/* parent */
816
		waitpid(pid, &s, 0);
817
		if (s != 0) {
818
			syslog(LOG_ERR, "pfctl exited abnormally");
819
			goto error;
820
		}
821
822
		gettimeofday(&Tstart, NULL);
823
		syslog(LOG_INFO, "allowing %s, user %s", ipsrc, luser);
824
	} else {
825
		remove_stale_rulesets();
826
827
		gettimeofday(&Tend, NULL);
828
		syslog(LOG_INFO, "removed %s, user %s - duration %d seconds",
829
		    ipsrc, luser, (int)(Tend.tv_sec - Tstart.tv_sec));
830
	}
831
	return (0);
832
no_mem:
833
	syslog(LOG_ERR, "malloc failed");
834
error:
835
	free(fdpath);
836
	free(rsn);
837
	free(userstr);
838
	free(ipstr);
839
	free(fn);
840
	return (-1);
841
}
842
843
/*
844
 * Add/remove this IP from the "authpf_users" table.
845
 */
846
static int
847
change_table(int add, const char *ipsrc)
848
{
849
	struct pfioc_table	io;
850
	struct pfr_addr		addr;
851
852
	bzero(&io, sizeof(io));
853
	strlcpy(io.pfrio_table.pfrt_name, tablename,
854
	    sizeof(io.pfrio_table.pfrt_name));
855
	io.pfrio_buffer = &addr;
856
	io.pfrio_esize = sizeof(addr);
857
	io.pfrio_size = 1;
858
859
	bzero(&addr, sizeof(addr));
860
	if (ipsrc == NULL || !ipsrc[0])
861
		return (-1);
862
	if (inet_pton(AF_INET, ipsrc, &addr.pfra_ip4addr) == 1) {
863
		addr.pfra_af = AF_INET;
864
		addr.pfra_net = 32;
865
	} else if (inet_pton(AF_INET6, ipsrc, &addr.pfra_ip6addr) == 1) {
866
		addr.pfra_af = AF_INET6;
867
		addr.pfra_net = 128;
868
	} else {
869
		syslog(LOG_ERR, "invalid ipsrc");
870
		return (-1);
871
	}
872
873
	if (ioctl(dev, add ? DIOCRADDADDRS : DIOCRDELADDRS, &io) &&
874
	    errno != ESRCH) {
875
		syslog(LOG_ERR, "cannot %s %s from table %s: %s",
876
		    add ? "add" : "remove", ipsrc, tablename,
877
		    strerror(errno));
878
		return (-1);
879
	}
880
	return (0);
881
}
882
883
/*
884
 * This is to kill off states that would otherwise be left behind stateful
885
 * rules. This means we don't need to allow in more traffic than we really
886
 * want to, since we don't have to worry about any luser sessions lasting
887
 * longer than their ssh session. This function is based on
888
 * pfctl_kill_states from pfctl.
889
 */
890
static void
891
authpf_kill_states(void)
892
{
893
	struct pfioc_state_kill	psk;
894
	struct pf_addr target;
895
896
	memset(&psk, 0, sizeof(psk));
897
	memset(&target, 0, sizeof(target));
898
899
	if (inet_pton(AF_INET, ipsrc, &target.v4) == 1)
900
		psk.psk_af = AF_INET;
901
	else if (inet_pton(AF_INET6, ipsrc, &target.v6) == 1)
902
		psk.psk_af = AF_INET6;
903
	else {
904
		syslog(LOG_ERR, "inet_pton(%s) failed", ipsrc);
905
		return;
906
	}
907
908
	/* Kill all states from ipsrc */
909
	memcpy(&psk.psk_src.addr.v.a.addr, &target,
910
	    sizeof(psk.psk_src.addr.v.a.addr));
911
	memset(&psk.psk_src.addr.v.a.mask, 0xff,
912
	    sizeof(psk.psk_src.addr.v.a.mask));
913
	if (ioctl(dev, DIOCKILLSTATES, &psk))
914
		syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
915
916
	/* Kill all states to ipsrc */
917
	memset(&psk.psk_src, 0, sizeof(psk.psk_src));
918
	memcpy(&psk.psk_dst.addr.v.a.addr, &target,
919
	    sizeof(psk.psk_dst.addr.v.a.addr));
920
	memset(&psk.psk_dst.addr.v.a.mask, 0xff,
921
	    sizeof(psk.psk_dst.addr.v.a.mask));
922
	if (ioctl(dev, DIOCKILLSTATES, &psk))
923
		syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
924
}
925
926
/* signal handler that makes us go away properly */
927
static void
928
need_death(int signo)
929
{
930
	want_death = 1;
931
}
932
933
/*
934
 * function that removes our stuff when we go away.
935
 */
936
static __dead void
937
do_death(int active)
938
{
939
	int	ret = 0;
940
941
	if (active) {
942
		change_filter(0, luser, ipsrc);
943
		if (user_ip) {
944
			change_table(0, ipsrc);
945
			authpf_kill_states();
946
		}
947
	}
948
	if (pidfile[0] && pidfd != -1)
949
		if (unlink(pidfile) == -1)
950
			syslog(LOG_ERR, "cannot unlink %s (%m)", pidfile);
951
	exit(ret);
952
}