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