From 737b89c60586b61a6fd0fda5f0de05bc6ba3bf6a Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 7 Feb 1999 17:03:07 +0000 Subject: FreeBSD support. svn path=/trunk/; revision=2376 --- NEWS | 3 + conf.c | 8 ++- configure.in | 7 ++ fetchmail-features.html | 6 +- fetchmail.c | 42 ++++++++++-- fetchmail.h | 2 +- fetchmail.man | 38 +++++++---- fetchmailconf | 10 +-- interface.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++- options.c | 10 +-- rcfile_y.y | 14 ++-- 11 files changed, 265 insertions(+), 46 deletions(-) diff --git a/NEWS b/NEWS index 5f29565d..cb216c12 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ Release Notes: +fetchmail-4.7.8 (): +* FreeBSD support for interface and monitor options by Andy Doran . + fetchmail-4.7.7 (Tue Feb 2 18:57:04 EST 1999): * Fixed off-by-one error in batchlimit logic (thanks to Brian Warner). * Added MD5 checksums to web page. diff --git a/conf.c b/conf.c index b12c8d76..e7f9f9c9 100644 --- a/conf.c +++ b/conf.c @@ -132,8 +132,10 @@ void dump_config(struct runctl *runp, struct query *querylist) * We need this in order to know whether `interface' and `monitor' * are valid options or not. */ -#ifdef linux +#if defined(linux) fputs("os_type = 'linux'\n", stdout); +#elif defined(__FreeBSD__) + fputs("os_type = 'freebsd'\n", stdout); #else fputs("os_type = 'generic'\n", stdout); #endif @@ -275,10 +277,10 @@ void dump_config(struct runctl *runp, struct query *querylist) listdump("aka", ctl->server.akalist); listdump("localdomains", ctl->server.localdomains); -#ifdef linux +#if defined(linux) || defined(__FreeBSD__) stringdump("interface", ctl->server.interface); stringdump("monitor", ctl->server.monitor); -#endif /* linux */ +#endif /* linux || __FreeBSD__ */ stringdump("plugin", ctl->server.plugin); stringdump("plugout", ctl->server.plugout); diff --git a/configure.in b/configure.in index 56098371..32ae87e1 100644 --- a/configure.in +++ b/configure.in @@ -57,6 +57,13 @@ then fi fi +# Check for FreeBSD special case: -lkvm needed +if test `uname` = "FreeBSD" +then + echo "Adding -lkvm to standard libraries" + LIBS="$LIBS -lkvm" +fi + # i18n # Arnaldo Carvalho de Melo # Sat Nov 7 15:39:03 EDT 1998 diff --git a/fetchmail-features.html b/fetchmail-features.html index 191197dc..a243bfea 100644 --- a/fetchmail-features.html +++ b/fetchmail-features.html @@ -10,7 +10,7 @@
Back to Fetchmail Home Page To Site Map -$Date: 1999/01/10 00:10:42 $ +$Date: 1999/02/07 17:03:04 $

@@ -18,6 +18,8 @@

Since 4.0:

    +
  • The interface and monitor options now work with freeBSD. +
  • Fetchmail now sends RFC1894-conformant bouncemail on SMTP and LMTP errors.
  • Full support for LMTP according to RFC2033. @@ -166,7 +168,7 @@ get-mail, gwpop, pimp-1.0, pop-perl5-1.2, popc, popmail-1.6 and upop.

    Back to Fetchmail Home Page To Site Map -$Date: 1999/01/10 00:10:42 $ +$Date: 1999/02/07 17:03:04 $

    Eric S. Raymond <esr@snark.thyrsus.com>
    diff --git a/fetchmail.c b/fetchmail.c index 116adafa..47133a93 100644 --- a/fetchmail.c +++ b/fetchmail.c @@ -19,6 +19,9 @@ #include #endif #include +#ifdef __FreeBSD__ +#include +#endif #include #include #include @@ -137,6 +140,27 @@ void itimerthread(void* dummy) { } #endif +#ifdef __FreeBSD__ +/* drop SGID kmem privileage until we need it */ +static void dropprivs(void) +{ + struct group *gr; + gid_t egid; + gid_t rgid; + + egid = getegid(); + rgid = getgid(); + gr = getgrgid(egid); + + if (gr && !strcmp(gr->gr_name, "kmem")) + { + extern void interface_set_gids(gid_t egid, gid_t rgid); + interface_set_gids(egid, rgid); + setegid(rgid); + } +} +#endif + int main (int argc, char **argv) { int st, bkgd = FALSE; @@ -147,6 +171,10 @@ int main (int argc, char **argv) char *netrc_file, *tmpbuf; pid_t pid; +#ifdef __FreeBSD__ + dropprivs(); +#endif + envquery(argc, argv); #ifdef ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); @@ -548,11 +576,11 @@ int main (int argc, char **argv) } } -#if defined(linux) && !INET6 +#if (defined(linux) && !INET6) || defined(__FreeBSD__) /* interface_approve() does its own error logging */ if (!interface_approve(&ctl->server)) continue; -#endif /* defined(linux) && !INET6 */ +#endif /* (defined(linux) && !INET6) || defined(__FreeBSD__) */ querystatus = query_host(ctl); @@ -577,7 +605,7 @@ int main (int argc, char **argv) ((querystatus!=PS_NOMAIL) || (outlevel==O_DEBUG))) report(stdout, _("Query status=%d\n"), querystatus); -#if defined(linux) && !INET6 +#if (defined(linux) && !INET6) || defined (__FreeBSD__) if (ctl->server.monitor) { /* @@ -588,7 +616,7 @@ int main (int argc, char **argv) sleep(3); interface_note_activity(&ctl->server); } -#endif /* defined(linux) && !INET6 */ +#endif /* (defined(linux) && !INET6) || defined(__FreeBSD__) */ } } @@ -803,11 +831,11 @@ static void optmerge(struct query *h2, struct query *h1, int force) FLAG_MERGE(server.checkalias); FLAG_MERGE(server.uidl); -#ifdef linux +#if defined(linux) || defined(__FreeBSD__) FLAG_MERGE(server.interface); FLAG_MERGE(server.monitor); FLAG_MERGE(server.interface_pair); -#endif /* linux */ +#endif /* linux || defined(__FreeBSD__) */ FLAG_MERGE(server.plugin); FLAG_MERGE(server.plugout); @@ -1584,7 +1612,7 @@ static void dump_params (struct runctl *runp, } } } -#ifdef linux +#if defined(linux) || defined(__FreeBSD__) if (ctl->server.interface) printf(_(" Connection must be through interface %s.\n"), ctl->server.interface); else if (outlevel >= O_VERBOSE) diff --git a/fetchmail.h b/fetchmail.h index fa8a0e9e..e0e53933 100644 --- a/fetchmail.h +++ b/fetchmail.h @@ -195,7 +195,7 @@ struct hostdata /* shared among all user connections to given server */ flag checkalias; /* resolve aliases by comparing IPs? */ -#ifdef linux +#if defined(linux) || defined(__FreeBSD__) char *interface; char *monitor; int monitor_io; diff --git a/fetchmail.man b/fetchmail.man index 6eb9a289..0cb7f688 100644 --- a/fetchmail.man +++ b/fetchmail.man @@ -413,7 +413,9 @@ etc.). The field before the second slash is the acceptable IP address. The field after the second slash is a mask which specifies a range of IP addresses to accept. If no mask is present 255.255.255.255 is assumed (i.e. an exact match). This option is currently only supported -under Linux. +under Linux and FreeBSD. Please see the +.B monitor +section for below for FreeBSD specific information. .TP .B \-M interface, --monitor interface (Keyword: monitor) @@ -422,7 +424,16 @@ after a period of inactivity (e.g. PPP links) to remain up indefinitely. This option identifies a system TCP/IP interface to be monitored for activity. After each poll interval, if the link is up but no other activity has occurred on the link, then the poll will be -skipped. This option is currently only supported under Linux. +skipped. This option is currently only supported under Linux and FreeBSD. +For the +.B monitor +and +.B interface +options to work for non root users under FreeBSD, the fetchmail binary +must be installed SGID kmem. This would be a security hole, but +fetchmail runs with the effective GID set to that of the kmem group +.I only +when interface data is being collected. .TP .B \-A, --auth (Keyword: auth[enticate]) @@ -1702,7 +1713,8 @@ successfully retrieved mail. Otherwise the returned error status is that of the last host queried. .SH AUTHOR -Eric S. Raymond . +Eric S. Raymond . Too many other people to +name here have contributed code and patches. This program is descended from and replaces .IR popclient , by Carl Harris ; the internals have become quite different, @@ -1767,16 +1779,16 @@ mailserver-side filter that consolidates the contents of all envelope headers into a single one (procmail, mailagent, or maildrop can be orogrammed to do this fairly easily). .PP -Use of any of the supported protocols other than POP3 with OTP or RPA, APOP, -KPOP, IMAP-K4, IMAP-GSS, or ETRN requires that the program send unencrypted -passwords over the TCP/IP connection to the mailserver. This creates -a risk that name/password pairs might be snaffled with a packet -sniffer or more sophisticated monitoring software. Under Linux, the ---interface option can be used to restrict polling to availability of -a specific interface device with a specific local IP address, but -snooping is still possible if (a) either host has a network device -that can be opened in promiscuous mode, or (b) the intervening network -link can be tapped. +Use of any of the supported protocols other than POP3 with OTP or RPA, +APOP, KPOP, IMAP-K4, IMAP-GSS, or ETRN requires that the program send +unencrypted passwords over the TCP/IP connection to the mailserver. +This creates a risk that name/password pairs might be snaffled with a +packet sniffer or more sophisticated monitoring software. Under Linux +and FreeBSD, the --interface option can be used to restrict polling to +availability of a specific interface device with a specific local IP +address, but snooping is still possible if (a) either host has a +network device that can be opened in promiscuous mode, or (b) the +intervening network link can be tapped. .PP Use of the %F or %T escapes in an mda option could open a security hole, because they pass text manipulable by an attacker to a shell diff --git a/fetchmailconf b/fetchmailconf index 846ed1d7..576774f9 100755 --- a/fetchmailconf +++ b/fetchmailconf @@ -832,8 +832,8 @@ fetchmail from triggering an expensive dial-out if the interface is not already active. The `interface' and `monitor' options are available -only for Linux systems. See the fetchmail manual page -for details on these. +only for Linux and freeBSD systems. See the fetchmail +manual page for details on these. The `netsec' option will be configurable only if fetchmail was compiled with IPV6 support. If you need to use it, @@ -1006,16 +1006,16 @@ class ServerEdit(Frame, MyWidget): self.server.localdomains, None, None, mdropwin, multihelp) mdropwin.pack(fill=X) - if os_type == 'linux' or 'netsec' in feature_options: + if os_type == 'linux' or os_type == 'freebsd' or 'netsec' in feature_options: secwin = Frame(rightwin, relief=RAISED, bd=5) Label(secwin, text="Security").pack(side=TOP) # Don't actually let users set this. KPOP sets it implicitly # ButtonBar(secwin, 'Authorization mode:', # self.auth, authlist, 1, None).pack(side=TOP) - if os_type == 'linux' or 'interface' in dictmembers: + if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers: LabeledEntry(secwin, 'IP range to check before poll:', self.interface, leftwidth).pack(side=TOP, fill=X) - if os_type == 'linux' or 'monitor' in dictmembers: + if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers: LabeledEntry(secwin, 'Interface to monitor:', self.monitor, leftwidth).pack(side=TOP, fill=X) if 'netsec' in feature_options or 'netsec' in dictmembers: diff --git a/interface.c b/interface.c index fafc32d4..75d975f9 100644 --- a/interface.c +++ b/interface.c @@ -4,14 +4,19 @@ * This module was implemented by George M. Sipe * or and is: * - * Copyright (c) 1996,1997 by George M. Sipe - ALL RIGHTS RESERVED + * Copyright (c) 1996,1997 by George M. Sipe + * + * FreeBSD specific portions written by and Copyright (c) 1999 + * Andy Doran . * * This is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; version 2, or (at your option) any later version. */ +#include +#include -#if defined(linux) && !defined(INET6) +#if (defined(linux) && !defined(INET6)) || defined(__FreeBSD__) #include "config.h" #include @@ -27,6 +32,14 @@ #include #include #include +#if defined(__FreeBSD__) +#if __FreeBSD_version >= 300001 +#include +#endif +#include +#include +#include +#endif #include "config.h" #include "fetchmail.h" #include "i18n.h" @@ -66,6 +79,9 @@ void interface_init(void) } } + +#if defined(linux) + static int _get_ifinfo_(int socket_fd, FILE *stats_file, const char *ifname, ifinfo_t *ifinfo) /* get active network interface information - return non-zero upon success */ @@ -156,6 +172,155 @@ static int get_ifinfo(const char *ifname, ifinfo_t *ifinfo) return(result); } +#elif defined __FreeBSD__ + +static kvm_t *kvmfd; +static struct nlist symbols[] = +{ + {"_ifnet"}, + {NULL} +}; +static u_long ifnet_savedaddr; +static gid_t if_rgid; +static gid_t if_egid; + +void +interface_set_gids(gid_t egid, gid_t rgid) +{ + if_rgid = rgid; + if_egid = egid; +} + +static int +openkvm(void) +{ + if ((kvmfd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL) + return FALSE; + + if (kvm_nlist(kvmfd, symbols) < 0) + return FALSE; + + if (kvm_read(kvmfd, (unsigned long) symbols[0].n_value, &ifnet_savedaddr, sizeof(unsigned long)) == -1) + return FALSE; + + return TRUE; +} + +static int +get_ifinfo(const char *ifname, ifinfo_t *ifinfo) +{ + char tname[16]; + char iname[16]; + struct ifnet ifnet; + unsigned long ifnet_addr = ifnet_savedaddr; +#if __FreeBSD_version >= 300001 + struct ifnethead ifnethead; + struct ifaddrhead ifaddrhead; +#endif + struct ifaddr ifaddr; + unsigned long ifaddr_addr; + struct sockaddr sa; + unsigned long sa_addr; + uint i; + + if (if_egid) + setegid(if_egid); + + for (i = 0; ifname[i] && ifname[i] != '/'; i++) + iname[i] = ifname[i]; + + iname[i] = '\0'; + + if (!kvmfd) + { + if (!openkvm()) + { + report(stderr, 0, _("Unable to open kvm interface. Make sure fetchmail is SGID kmem.")); + if (if_egid) + setegid(if_rgid); + exit(1); + } + } + +#if __FreeBSD_version >= 300001 + kvm_read(kvmfd, ifnet_savedaddr, (char *) &ifnethead, sizeof ifnethead); + ifnet_addr = (u_long) ifnethead.tqh_first; +#else + ifnet_addr = ifnet_savedaddr; +#endif + + while (ifnet_addr) + { + kvm_read(kvmfd, ifnet_addr, &ifnet, sizeof(ifnet)); + kvm_read(kvmfd, (unsigned long) ifnet.if_name, tname, sizeof tname); + snprintf(tname, sizeof tname - 1, "%s%d", tname, ifnet.if_unit); + + if (!strcmp(tname, iname)) + { + if (!(ifnet.if_flags & IFF_UP)) + { + if (if_egid) + setegid(if_rgid); + return 0; + } + + ifinfo->rx_packets = ifnet.if_ipackets; + ifinfo->tx_packets = ifnet.if_opackets; + +#if __FreeBSD_version >= 300001 + ifaddr_addr = (u_long) ifnet.if_addrhead.tqh_first; +#else + ifaddr_addr = (u_long) ifnet.if_addrlist; +#endif + + while(ifaddr_addr) + { + kvm_read(kvmfd, ifaddr_addr, &ifaddr, sizeof(ifaddr)); + kvm_read(kvmfd, (u_long)ifaddr.ifa_addr, &sa, sizeof(sa)); + + if (sa.sa_family != AF_INET) + { +#if __FreeBSD_version >= 300001 + ifaddr_addr = (u_long) ifaddr.ifa_link.tqe_next; +#else + ifaddr_addr = (u_long) ifaddr.ifa_next; +#endif + continue; + } + + ifinfo->addr.s_addr = *(u_long *)(sa.sa_data + 2); + kvm_read(kvmfd, (u_long)ifaddr.ifa_dstaddr, &sa, sizeof(sa)); + ifinfo->dstaddr.s_addr = *(u_long *)(sa.sa_data + 2); + kvm_read(kvmfd, (u_long)ifaddr.ifa_netmask, &sa, sizeof(sa)); + ifinfo->netmask.s_addr = *(u_long *)(sa.sa_data + 2); + + if (if_egid) + setegid(if_rgid); + + return 1; + } + + if (if_egid) + setegid(if_rgid); + + return 0; + } + +#if __FreeBSD_version >= 300001 + ifnet_addr = (u_long) ifnet.if_link.tqe_next; +#else + ifnet_addr = (unsigned long) ifnet.if_next; +#endif + } + + if (if_egid) + setegid(if_rgid); + + return 0; +} +#endif /* defined __FreeBSD__ */ + + #ifndef HAVE_INET_ATON /* * Note: This is not a true replacement for inet_aton(), as it won't @@ -313,4 +478,4 @@ int interface_approve(struct hostdata *hp) return(TRUE); } -#endif /* defined(linux) && !defined(INET6) */ +#endif /* (defined(linux) && !defined(INET6)) || defined(__FreeBSD__) */ diff --git a/options.c b/options.c index 31150162..0832b892 100644 --- a/options.c +++ b/options.c @@ -128,10 +128,10 @@ static const struct option longoptions[] = { {"netsec", required_argument, (int *) 0, LA_NETSEC }, #endif /* INET6 */ -#if defined(linux) && !INET6 +#if (defined(linux) && !INET6) || defined(__FreeBSD__) {"interface", required_argument, (int *) 0, LA_INTERFACE }, {"monitor", required_argument, (int *) 0, LA_MONITOR }, -#endif /* defined(linux) && !INET6 */ +#endif /* (defined(linux) && !INET6) || defined(__FreeBSD__) */ {"plugin", required_argument, (int *) 0, LA_PLUGIN }, {"plugout", required_argument, (int *) 0, LA_PLUGOUT }, @@ -498,7 +498,7 @@ struct query *ctl; /* option record to be initialized */ #endif /* NET_SECURITY */ break; -#if defined(linux) && !INET6 +#if (defined(linux) && !INET6) || defined(__FreeBSD__) case 'I': case LA_INTERFACE: interface_parse(optarg, &ctl->server); @@ -507,7 +507,7 @@ struct query *ctl; /* option record to be initialized */ case LA_MONITOR: ctl->server.monitor = xstrdup(optarg); break; -#endif /* defined(linux) && !INET6 */ +#endif /* (defined(linux) && !INET6) || defined(__FreeBSD__) */ case LA_PLUGIN: ctl->server.plugin = xstrdup(optarg); break; @@ -565,7 +565,7 @@ struct query *ctl; /* option record to be initialized */ P(_(" -f, --fetchmailrc specify alternate run control file\n")); P(_(" -i, --idfile specify alternate UIDs file\n")); P(_(" --postmaster specify recipient of last resort\n")); -#if defined(linux) && !INET6 +#if (defined(linux) && !INET6) || defined(__FreeBSD__) P(_(" -I, --interface interface required specification\n")); P(_(" -M, --monitor monitor interface for activity\n")); #endif diff --git a/rcfile_y.y b/rcfile_y.y index d688198c..19e788d5 100644 --- a/rcfile_y.y +++ b/rcfile_y.y @@ -207,18 +207,18 @@ serv_option : AKA alias_list #endif /* NET_SECURITY */ } | INTERFACE STRING { -#if defined(linux) && !defined(INET6) +#if (defined(linux) && !defined(INET6)) || defined(__FreeBSD__) interface_parse($2, ¤t.server); -#else /* defined(linux) && !defined(INET6) */ - fprintf(stderr, "fetchmail: interface option is only supported under Linux\n"); -#endif /* defined(linux) && !defined(INET6) */ +#else /* (defined(linux) && !defined(INET6)) || defined(__FreeBSD__) */ + fprintf(stderr, "fetchmail: interface option is only supported under Linux and FreeBSD\n"); +#endif /* (defined(linux) && !defined(INET6)) || defined(__FreeBSD__) */ } | MONITOR STRING { -#if defined(linux) && !defined(INET6) +#if (defined(linux) && !defined(INET6)) || defined(__FreeBSD__) current.server.monitor = xstrdup($2); -#else /* defined(linux) && !defined(INET6) */ +#else /* (defined(linux) && !defined(INET6)) || defined(__FreeBSD__) */ fprintf(stderr, "fetchmail: monitor option is only supported under Linux\n"); -#endif /* defined(linux) && !defined(INET6) */ +#endif /* (defined(linux) && !defined(INET6) || defined(__FreeBSD__)) */ } | PLUGIN STRING { current.server.plugin = xstrdup($2); } | PLUGOUT STRING { current.server.plugout = xstrdup($2); } -- cgit v1.2.3