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