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