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