1 /* $NetBSD: authpf.c,v 1.6 2008/06/18 09:06:26 yamt Exp $ */
2 /* $OpenBSD: authpf.c,v 1.104 2007/02/24 17:35:08 beck Exp $ */
5 * Copyright (C) 1998 - 2007 Bob Beck (beck@openbsd.org).
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #include <sys/socket.h>
29 #include <net/pfvar.h>
30 #include <arpa/inet.h>
34 #include <login_cap.h>
43 #include "pathnames.h"
45 static int read_config(FILE *);
46 static void print_message(char *);
47 static int allowed_luser(char *);
48 static int check_luser(char *, char *);
49 static int remove_stale_rulesets(void);
50 static int change_filter(int, const char *, const char *);
51 static int change_table(int, const char *);
52 static void authpf_kill_states(void);
54 int dev
; /* pf device */
55 char anchorname
[PF_ANCHOR_NAME_SIZE
] = "authpf";
56 char rulesetname
[MAXPATHLEN
- PF_ANCHOR_NAME_SIZE
- 2];
57 char tablename
[PF_TABLE_NAME_SIZE
] = "authpf_users";
60 char luser
[MAXLOGNAME
]; /* username */
61 char ipsrc
[256]; /* ip as a string */
62 char pidfile
[MAXPATHLEN
]; /* we save pid in this file. */
64 struct timeval Tstart
, Tend
; /* start and end times of session */
66 volatile sig_atomic_t want_death
;
67 static void need_death(int signo
);
68 static __dead
void do_death(int);
71 * User shell for authenticating gateways. Sole purpose is to allow
72 * a user to ssh to a gateway, and have the gateway modify packet
73 * filters to allow access, then remove access when the user finishes
74 * up. Meant to be used only from ssh(1) connections.
77 main(int argc
, char *argv
[])
79 int lockcnt
= 0, n
, pidfd
;
89 config
= fopen(PATH_CONFFILE
, "r");
91 syslog(LOG_ERR
, "can not open %s (%m)", PATH_CONFFILE
);
95 if ((cp
= getenv("SSH_TTY")) == NULL
) {
96 syslog(LOG_ERR
, "non-interactive session connection for authpf");
100 if ((cp
= getenv("SSH_CLIENT")) == NULL
) {
101 syslog(LOG_ERR
, "cannot determine connection source");
105 if (strlcpy(ipsrc
, cp
, sizeof(ipsrc
)) >= sizeof(ipsrc
)) {
106 syslog(LOG_ERR
, "SSH_CLIENT variable too long");
109 cp
= strchr(ipsrc
, ' ');
111 syslog(LOG_ERR
, "corrupt SSH_CLIENT variable %s", ipsrc
);
115 if (inet_pton(AF_INET
, ipsrc
, &ina
) != 1 &&
116 inet_pton(AF_INET6
, ipsrc
, &ina
) != 1) {
118 "cannot determine IP from SSH_CLIENT %s", ipsrc
);
121 /* open the pf device */
122 dev
= open(PATH_DEVFILE
, O_RDWR
);
124 syslog(LOG_ERR
, "cannot open packet filter device (%m)");
131 syslog(LOG_ERR
, "cannot find user for uid %u", uid
);
135 if ((lc
= login_getclass(pw
->pw_class
)) != NULL
)
136 shell
= login_getcapstr(lc
, "shell", pw
->pw_shell
,
139 shell
= pw
->pw_shell
;
143 if (strcmp(shell
, PATH_AUTHPF_SHELL
)) {
144 syslog(LOG_ERR
, "wrong shell for user %s, uid %u",
145 pw
->pw_name
, pw
->pw_uid
);
146 if (shell
!= pw
->pw_shell
)
151 if (shell
!= pw
->pw_shell
)
155 * Paranoia, but this data _does_ come from outside authpf, and
156 * truncation would be bad.
158 if (strlcpy(luser
, pw
->pw_name
, sizeof(luser
)) >= sizeof(luser
)) {
159 syslog(LOG_ERR
, "username too long: %s", pw
->pw_name
);
163 if ((n
= snprintf(rulesetname
, sizeof(rulesetname
), "%s(%ld)",
164 luser
, (long)getpid())) < 0 || (u_int
)n
>= sizeof(rulesetname
)) {
165 syslog(LOG_INFO
, "%s(%ld) too large, ruleset name will be %ld",
166 luser
, (long)getpid(), (long)getpid());
167 if ((n
= snprintf(rulesetname
, sizeof(rulesetname
), "%ld",
168 (long)getpid())) < 0 || (u_int
)n
>= sizeof(rulesetname
)) {
169 syslog(LOG_ERR
, "pid too large for ruleset name");
175 /* Make our entry in /var/authpf as /var/authpf/ipaddr */
176 n
= snprintf(pidfile
, sizeof(pidfile
), "%s/%s", PATH_PIDFILE
, ipsrc
);
177 if (n
< 0 || (u_int
)n
>= sizeof(pidfile
)) {
178 syslog(LOG_ERR
, "path to pidfile too long");
183 * If someone else is already using this ip, then this person
184 * wants to switch users - so kill the old process and exit
187 * Note, we could print a message and tell them to log out, but the
188 * usual case of this is that someone has left themselves logged in,
189 * with the authenticated connection iconized and someone else walks
190 * up to use and automatically logs in before using. If this just
191 * gets rid of the old one silently, the new user never knows they
192 * could have used someone else's old authentication. If we
193 * tell them to log out before switching users it is an invitation
198 int save_errno
, otherpid
= -1;
199 char otherluser
[MAXLOGNAME
];
201 if ((pidfd
= open(pidfile
, O_RDWR
|O_CREAT
, 0644)) == -1 ||
202 (pidfp
= fdopen(pidfd
, "r+")) == NULL
) {
205 syslog(LOG_ERR
, "cannot open or create %s: %s", pidfile
,
210 if (flock(fileno(pidfp
), LOCK_EX
|LOCK_NB
) == 0)
214 /* Mark our pid, and username to our file. */
217 /* 31 == MAXLOGNAME - 1 */
218 if (fscanf(pidfp
, "%d\n%31s\n", &otherpid
, otherluser
) != 2)
220 syslog(LOG_DEBUG
, "tried to lock %s, in use by pid %d: %s",
221 pidfile
, otherpid
, strerror(save_errno
));
225 "killing prior auth (pid %d) of %s by user %s",
226 otherpid
, ipsrc
, otherluser
);
227 if (kill((pid_t
) otherpid
, SIGTERM
) == -1) {
229 "could not kill process %d: (%m)",
235 * we try to kill the previous process and acquire the lock
236 * for 10 seconds, trying once a second. if we can't after
237 * 10 attempts we log an error and give up
239 if (++lockcnt
> 10) {
240 syslog(LOG_ERR
, "cannot kill previous authpf (pid %d)",
248 /* re-open, and try again. The previous authpf process
249 * we killed above should unlink the file and release
250 * it's lock, giving us a chance to get it now
256 /* whack the group list */
258 if (setgroups(1, &gid
) == -1) {
259 syslog(LOG_INFO
, "setgroups: %s", strerror(errno
));
265 #if defined(__OpenBSD__)
266 if (setresuid(uid
, uid
, uid
) == -1) {
267 syslog(LOG_INFO
, "setresuid: %s", strerror(errno
));
270 #else /* defined(__OpenBSD__) */
272 if (setuid(uid
) == -1) {
273 syslog(LOG_INFO
, "setresuid: %s", strerror(errno
));
276 #endif /* defined(__OpenBSD__) */
277 openlog("authpf", LOG_PID
| LOG_NDELAY
, LOG_DAEMON
);
279 if (!check_luser(PATH_BAN_DIR
, luser
) || !allowed_luser(luser
)) {
280 syslog(LOG_INFO
, "user %s prohibited", luser
);
284 if (read_config(config
)) {
285 syslog(LOG_ERR
, "invalid config file %s", PATH_CONFFILE
);
289 if (remove_stale_rulesets()) {
290 syslog(LOG_INFO
, "error removing stale rulesets");
294 /* We appear to be making headway, so actually mark our pid */
296 fprintf(pidfp
, "%ld\n%s\n", (long)getpid(), luser
);
298 (void) ftruncate(fileno(pidfp
), ftello(pidfp
));
300 if (change_filter(1, luser
, ipsrc
) == -1) {
301 printf("Unable to modify filters\r\n");
304 if (change_table(1, ipsrc
) == -1) {
305 printf("Unable to modify table\r\n");
306 change_filter(0, luser
, ipsrc
);
310 signal(SIGTERM
, need_death
);
311 signal(SIGINT
, need_death
);
312 signal(SIGALRM
, need_death
);
313 signal(SIGPIPE
, need_death
);
314 signal(SIGHUP
, need_death
);
315 signal(SIGQUIT
, need_death
);
316 signal(SIGTSTP
, need_death
);
318 printf("\r\nHello %s. ", luser
);
319 printf("You are authenticated from host \"%s\"\r\n", ipsrc
);
320 setproctitle("%s@%s", luser
, ipsrc
);
321 print_message(PATH_MESSAGE
);
331 printf("\r\n\r\nSorry, this service is currently unavailable due to ");
332 printf("technical difficulties\r\n\r\n");
333 print_message(PATH_PROBLEM
);
334 printf("\r\nYour authentication process (pid %ld) was unable to run\n",
336 sleep(180); /* them lusers read reaaaaal slow */
343 * reads config file in PATH_CONFFILE to set optional behaviours up
353 char *pair
[4], *cp
, *tp
;
356 if (fgets(buf
, sizeof(buf
), f
) == NULL
) {
362 if (buf
[len
- 1] != '\n' && !feof(f
)) {
363 syslog(LOG_ERR
, "line %d too long in %s", i
,
369 for (cp
= buf
; *cp
== ' ' || *cp
== '\t'; cp
++)
372 if (!*cp
|| *cp
== '#' || *cp
== '\n')
375 for (ap
= pair
; ap
< &pair
[3] &&
376 (*ap
= strsep(&cp
, "=")) != NULL
; ) {
383 tp
= pair
[1] + strlen(pair
[1]);
384 while ((*tp
== ' ' || *tp
== '\t') && tp
>= pair
[1])
387 if (strcasecmp(pair
[0], "anchor") == 0) {
388 if (!pair
[1][0] || strlcpy(anchorname
, pair
[1],
389 sizeof(anchorname
)) >= sizeof(anchorname
))
392 if (strcasecmp(pair
[0], "table") == 0) {
393 if (!pair
[1][0] || strlcpy(tablename
, pair
[1],
394 sizeof(tablename
)) >= sizeof(tablename
))
397 } while (!feof(f
) && !ferror(f
));
403 syslog(LOG_ERR
, "parse error, line %d of %s", i
, PATH_CONFFILE
);
409 * splatter a file to stdout - max line length of 1024,
410 * used for spitting message files at users to tell them
411 * they've been bad or we're unavailable.
414 print_message(char *filename
)
419 if ((f
= fopen(filename
, "r")) == NULL
)
420 return; /* fail silently, we don't care if it isn't there */
423 if (fgets(buf
, sizeof(buf
), f
) == NULL
) {
428 } while (fputs(buf
, stdout
) != EOF
&& !feof(f
));
434 * allowed_luser checks to see if user "luser" is allowed to
435 * use this gateway by virtue of being listed in an allowed
436 * users file, namely /etc/authpf/authpf.allow .
438 * If /etc/authpf/authpf.allow does not exist, then we assume that
439 * all users who are allowed in by sshd(8) are permitted to
440 * use this gateway. If /etc/authpf/authpf.allow does exist, then a
441 * user must be listed if the connection is to continue, else
442 * the session terminates in the same manner as being banned.
445 allowed_luser(char *luser
)
452 if ((f
= fopen(PATH_ALLOWFILE
, "r")) == NULL
) {
453 if (errno
== ENOENT
) {
455 * allowfile doesn't exist, thus this gateway
456 * isn't restricted to certain users...
462 * luser may in fact be allowed, but we can't open
463 * the file even though it's there. probably a config
466 syslog(LOG_ERR
, "cannot open allowed users file %s (%s)",
467 PATH_ALLOWFILE
, strerror(errno
));
471 * /etc/authpf/authpf.allow exists, thus we do a linear
472 * search to see if they are allowed.
473 * also, if username "*" exists, then this is a
474 * "public" gateway, such as it is, so let
478 while ((buf
= fgetln(f
, &len
))) {
479 if (buf
[len
- 1] == '\n')
482 if ((lbuf
= (char *)malloc(len
+ 1)) == NULL
)
484 memcpy(lbuf
, buf
, len
);
489 matched
= strcmp(luser
, buf
) == 0 || strcmp("*", buf
) == 0;
497 return (1); /* matched an allowed username */
499 syslog(LOG_INFO
, "denied access to %s: not listed in %s",
500 luser
, PATH_ALLOWFILE
);
503 buf
= "\n\nSorry, you are not allowed to use this facility!\n";
511 * check_luser checks to see if user "luser" has been banned
512 * from using us by virtue of having an file of the same name
513 * in the "luserdir" directory.
515 * If the user has been banned, we copy the contents of the file
516 * to the user's screen. (useful for telling the user what to
517 * do to get un-banned, or just to tell them they aren't
518 * going to be un-banned.)
521 check_luser(char *luserdir
, char *luser
)
525 char tmp
[MAXPATHLEN
];
527 n
= snprintf(tmp
, sizeof(tmp
), "%s/%s", luserdir
, luser
);
528 if (n
< 0 || (u_int
)n
>= sizeof(tmp
)) {
529 syslog(LOG_ERR
, "provided banned directory line too long (%s)",
533 if ((f
= fopen(tmp
, "r")) == NULL
) {
534 if (errno
== ENOENT
) {
536 * file or dir doesn't exist, so therefore
537 * this luser isn't banned.. all is well
542 * luser may in fact be banned, but we can't open the
543 * file even though it's there. probably a config
546 syslog(LOG_ERR
, "cannot open banned file %s (%s)",
547 tmp
, strerror(errno
));
552 * luser is banned - spit the file at them to
553 * tell what they can do and where they can go.
555 syslog(LOG_INFO
, "denied access to %s: %s exists",
559 strlcpy(tmp
, "\n\n-**- Sorry, you have been banned! -**-\n\n",
561 while (fputs(tmp
, stdout
) != EOF
&& !feof(f
)) {
562 if (fgets(tmp
, sizeof(tmp
), f
) == NULL
) {
575 * Search for rulesets left by other authpf processes (either because they
576 * died ungracefully or were terminated) and remove them.
579 remove_stale_rulesets(void)
581 struct pfioc_ruleset prs
;
584 memset(&prs
, 0, sizeof(prs
));
585 strlcpy(prs
.path
, anchorname
, sizeof(prs
.path
));
586 if (ioctl(dev
, DIOCGETRULESETS
, &prs
)) {
600 if (ioctl(dev
, DIOCGETRULESET
, &prs
))
603 if ((t
= strchr(prs
.name
, '(')) == NULL
)
607 pid
= strtoul(t
, &s
, 10);
608 if (!prs
.name
[0] || errno
||
609 (*s
&& (t
== prs
.name
|| *s
!= ')')))
611 if (kill(pid
, 0) && errno
!= EPERM
) {
613 struct pfioc_trans_e t_e
[PF_RULESET_MAX
+1];
614 struct pfioc_trans t
;
616 bzero(&t
, sizeof(t
));
617 bzero(t_e
, sizeof(t_e
));
618 t
.size
= PF_RULESET_MAX
+1;
619 t
.esize
= sizeof(t_e
[0]);
621 for (i
= 0; i
< PF_RULESET_MAX
+1; ++i
) {
623 snprintf(t_e
[i
].anchor
, sizeof(t_e
[i
].anchor
),
624 "%s/%s", anchorname
, prs
.name
);
626 t_e
[PF_RULESET_MAX
].rs_num
= PF_RULESET_TABLE
;
627 if ((ioctl(dev
, DIOCXBEGIN
, &t
) ||
628 ioctl(dev
, DIOCXCOMMIT
, &t
)) &&
639 * Add/remove filter entries for user "luser" from ip "ipsrc"
642 change_filter(int add
, const char *luser
, const char *ipsrc
)
645 "pfctl", "-p", "/dev/pf", "-q", "-a", "anchor/ruleset",
646 "-D", "user_ip=X", "-D", "user_id=X", "-f",
649 char *fdpath
= NULL
, *userstr
= NULL
, *ipstr
= NULL
;
650 char *rsn
= NULL
, *fn
= NULL
;
655 if (luser
== NULL
|| !luser
[0] || ipsrc
== NULL
|| !ipsrc
[0]) {
656 syslog(LOG_ERR
, "invalid luser/ipsrc");
660 if (asprintf(&rsn
, "%s/%s", anchorname
, rulesetname
) == -1)
662 if (asprintf(&fdpath
, "/dev/fd/%d", dev
) == -1)
664 if (asprintf(&ipstr
, "user_ip=%s", ipsrc
) == -1)
666 if (asprintf(&userstr
, "user_id=%s", luser
) == -1)
672 if (asprintf(&fn
, "%s/%s/authpf.rules", PATH_USER_DIR
, luser
)
675 if (stat(fn
, &sb
) == -1) {
677 if ((fn
= strdup(PATH_PFRULES
)) == NULL
)
686 pargv
[11] = "/dev/null";
690 switch (pid
= fork()) {
692 syslog(LOG_ERR
, "fork failed");
695 /* revoke group privs before exec */
697 if (setregid(gid
, gid
) == -1) {
700 execvp(PATH_PFCTL
, pargv
);
701 warn("exec of %s failed", PATH_PFCTL
);
708 syslog(LOG_ERR
, "pfctl exited abnormally");
713 gettimeofday(&Tstart
, NULL
);
714 syslog(LOG_INFO
, "allowing %s, user %s", ipsrc
, luser
);
716 gettimeofday(&Tend
, NULL
);
717 syslog(LOG_INFO
, "removed %s, user %s - duration %lld seconds",
718 ipsrc
, luser
, (long long)(Tend
.tv_sec
- Tstart
.tv_sec
));
722 syslog(LOG_ERR
, "malloc failed");
733 * Add/remove this IP from the "authpf_users" table.
736 change_table(int add
, const char *ipsrc
)
738 struct pfioc_table io
;
739 struct pfr_addr addr
;
741 bzero(&io
, sizeof(io
));
742 strlcpy(io
.pfrio_table
.pfrt_name
, tablename
,
743 sizeof(io
.pfrio_table
.pfrt_name
));
744 io
.pfrio_buffer
= &addr
;
745 io
.pfrio_esize
= sizeof(addr
);
748 bzero(&addr
, sizeof(addr
));
749 if (ipsrc
== NULL
|| !ipsrc
[0])
751 if (inet_pton(AF_INET
, ipsrc
, &addr
.pfra_ip4addr
) == 1) {
752 addr
.pfra_af
= AF_INET
;
754 } else if (inet_pton(AF_INET6
, ipsrc
, &addr
.pfra_ip6addr
) == 1) {
755 addr
.pfra_af
= AF_INET6
;
758 syslog(LOG_ERR
, "invalid ipsrc");
762 if (ioctl(dev
, add
? DIOCRADDADDRS
: DIOCRDELADDRS
, &io
) &&
764 syslog(LOG_ERR
, "cannot %s %s from table %s: %s",
765 add
? "add" : "remove", ipsrc
, tablename
,
773 * This is to kill off states that would otherwise be left behind stateful
774 * rules. This means we don't need to allow in more traffic than we really
775 * want to, since we don't have to worry about any luser sessions lasting
776 * longer than their ssh session. This function is based on
777 * pfctl_kill_states from pfctl.
780 authpf_kill_states(void)
782 struct pfioc_state_kill psk
;
783 struct pf_addr target
;
785 memset(&psk
, 0, sizeof(psk
));
786 memset(&target
, 0, sizeof(target
));
788 if (inet_pton(AF_INET
, ipsrc
, &target
.v4
) == 1)
789 psk
.psk_af
= AF_INET
;
790 else if (inet_pton(AF_INET6
, ipsrc
, &target
.v6
) == 1)
791 psk
.psk_af
= AF_INET6
;
793 syslog(LOG_ERR
, "inet_pton(%s) failed", ipsrc
);
797 /* Kill all states from ipsrc */
798 memcpy(&psk
.psk_src
.addr
.v
.a
.addr
, &target
,
799 sizeof(psk
.psk_src
.addr
.v
.a
.addr
));
800 memset(&psk
.psk_src
.addr
.v
.a
.mask
, 0xff,
801 sizeof(psk
.psk_src
.addr
.v
.a
.mask
));
802 if (ioctl(dev
, DIOCKILLSTATES
, &psk
))
803 syslog(LOG_ERR
, "DIOCKILLSTATES failed (%m)");
805 /* Kill all states to ipsrc */
806 memset(&psk
.psk_src
, 0, sizeof(psk
.psk_src
));
807 memcpy(&psk
.psk_dst
.addr
.v
.a
.addr
, &target
,
808 sizeof(psk
.psk_dst
.addr
.v
.a
.addr
));
809 memset(&psk
.psk_dst
.addr
.v
.a
.mask
, 0xff,
810 sizeof(psk
.psk_dst
.addr
.v
.a
.mask
));
811 if (ioctl(dev
, DIOCKILLSTATES
, &psk
))
812 syslog(LOG_ERR
, "DIOCKILLSTATES failed (%m)");
815 /* signal handler that makes us go away properly */
817 need_death(int signo
)
823 * function that removes our stuff when we go away.
831 change_filter(0, luser
, ipsrc
);
832 change_table(0, ipsrc
);
833 authpf_kill_states();
834 remove_stale_rulesets();
836 if (pidfile
[0] && (pidfp
!= NULL
))
837 if (unlink(pidfile
) == -1)
838 syslog(LOG_ERR
, "cannot unlink %s (%m)", pidfile
);