From owner-freebsd-audit Fri Jun 15 19:18: 9 2001 Delivered-To: freebsd-audit@freebsd.org Received: from mail.rpi.edu (mail.rpi.edu [128.113.22.40]) by hub.freebsd.org (Postfix) with ESMTP id 8DA2837B40A for ; Fri, 15 Jun 2001 19:17:00 -0700 (PDT) (envelope-from drosih@rpi.edu) Received: from [128.113.24.47] (gilead.acs.rpi.edu [128.113.24.47]) by mail.rpi.edu (8.11.3/8.11.3) with ESMTP id f5G2Gup68370; Fri, 15 Jun 2001 22:16:56 -0400 Mime-Version: 1.0 X-Sender: drosih@mail.rpi.edu Message-Id: Date: Fri, 15 Jun 2001 22:16:54 -0400 To: freebsd-audit@freebsd.org, freebsd-print@bostonradio.org From: Garance A Drosihn Subject: Patch: new options for lpd, improved msgs for connect-errs Cc: Morgan Davis Content-Type: text/plain; charset="us-ascii" ; format="flowed" Sender: owner-freebsd-audit@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG Main goals of this update to lpd: 1) add new option '-c', which will cause lpd to log appropriate messages via syslog for all connection-errors. 2) add new option '-w', which will cause lpd to accept connections from other hosts (generally Windows) even if the connection is NOT from a reserved port. 3) generally improve the helpfulness of the various connection error-messages. I realize this doesn't include an update to the man page yet, but I thought I'd first see if people wanted the new options to key off of other letters (instead of -c and -w). Some discussion: Back in July 1997, revision 1.6 (imp) of lpd dropped the check which required that incoming connections be coming from a reserved port. It looks like this was mistakenly copied from openbsd's lpd (I intend to check further). In at least my (RPI) environment, that check really needs to be there. The IPv6 update added that check back in. However, it then turns out that there are some implementations for lpr on Windows which do not bother at all with reserved ports, and those clients broke with the lpd in 4.3-release. The '-w' option is the quick fix so that people who want to accept connections from non-reserved ports can do so. Previous to this update, if a connection from a remote host to a freebsd print server failed, only the remote-host got the error messsage. And in some universes (ahem, windows) that message is not necessarily echoed to the user. The '-c' option causes connection- errors to be logged to syslog. My assumption is that the syslog-ing should not be done by default, because it would provide a remote user an easy way to fill up the syslog-files on your machine. You'll notice my error-writing routine takes TWO 'msg-format' strings, and (obviously) only one variable-list of parameters. This probably seems weird, particularly with the way I handle 'NULL' being sent for the first fmt-string, but other alternatives seemed even messier (IMO). I moved the reserved-port check to be the LAST check done (it had been the first), partially because I wanted to have the name of the remote host for the error messages printed. It's also partially because I was thinking there could be a "third file" that lpd might scan for hostnames it would allow a connection from (and thus a third pass in the current two-pass loop). Say, "/etc/hosts.lpd-extras", and maybe that third file would indicate specific hosts where a connection would be allowed from any port, instead of using '-w' to drop the reserved-port check for ALL hosts. I don't really want to implement this "third file" yet, as I have some other ideas where that "third file" might be useful. Also, there's a minor change in the format of error messages sent to the remote host. Assuming a print server named 'pserver.rpi.edu', the remote host would used to see messages like: pserver.rpi.edu: lpd: Your host does not have line printer access it now sees: lpd [@pserver.rpi.edu]: Your host (blah.rpi.edu) does not have print-service access I intend to do the same thing in the frecverr() routine in recvjob.c (in a later update). (just the 'lpd [@pserver]' part, I am not adding the client's hostname to all the error messages). - - - - - - - So, please look this over, try it out, let me know what you think about it. This shouldn't change any of the actual CHECKS that are being done, it should just change the error messages which result. But, also let me know if any of the checks could be improved (in a security-paranoid sense). Also let me know if the messages to syslog could be more helpful to potentially-harried administrators trying to figure out why some client isn't able to connect... - - - - - - - Index: lpd/lpd.c =================================================================== RCS file: /home/ncvs/src/usr.sbin/lpr/lpd/lpd.c,v retrieving revision 1.25 diff -u -r1.25 lpd.c --- lpd/lpd.c 2001/06/16 00:14:02 1.25 +++ lpd/lpd.c 2001/06/16 01:10:34 @@ -112,8 +112,10 @@ static void mcleanup(int _signo); static void doit(void); static void startup(void); -static void chkhost(struct sockaddr *_f); +static void chkhost(struct sockaddr *_f, int _ch_opts); static int ckqueue(struct printer *_pp); +static void fhosterr(int _dosys, const char *_sysmsg, const char *_usermsg, + ...); static int *socksetup(int _af, int _debuglvl); static void usage(void); @@ -123,10 +125,13 @@ uid_t uid, euid; +#define LPD_NOPORTCHK 0001 /* skip reserved-port check */ +#define LPD_LOGCONNERR 0002 /* (sys)log connection errors */ + int main(int argc, char **argv) { - int errs, f, funix, *finet, fromlen, i, options, socket_debug; + int ch_options, errs, f, funix, *finet, fromlen, i, socket_debug; fd_set defreadfds; struct sockaddr_un un, fromunix; struct sockaddr_storage frominet; @@ -137,6 +142,8 @@ euid = geteuid(); /* these shouldn't be different */ uid = getuid(); + + ch_options = 0; socket_debug = 0; gethostname(local_host, sizeof(local_host)); @@ -146,8 +153,12 @@ errx(EX_NOPERM,"must run as root"); errs = 0; - while ((i = getopt(argc, argv, "dlp46")) != -1) + while ((i = getopt(argc, argv, "cdlpw46")) != -1) switch (i) { + case 'c': + /* log all kinds of connection-errors to syslog */ + ch_options |= LPD_LOGCONNERR; + break; case 'd': socket_debug++; break; @@ -157,6 +168,11 @@ case 'p': pflag++; break; + case 'w': + /* allow connections coming from a non-reserved port */ + /* (done by some lpr-implementations for MS-Windows) */ + ch_options |= LPD_NOPORTCHK; + break; case '4': family = PF_INET; inet_flag++; @@ -366,7 +382,8 @@ if (domain == AF_INET) { /* for both AF_INET and AF_INET6 */ from_remote = 1; - chkhost((struct sockaddr *)&frominet); + chkhost((struct sockaddr *)&frominet, + ch_options); } else from_remote = 0; doit(); @@ -600,36 +617,40 @@ #define DUMMY ":nobody::" /* - * Check to see if the from host has access to the line printer. + * Check to see if the host connecting to this host has access to any + * lpd services on this host. */ static void -chkhost(struct sockaddr *f) +chkhost(struct sockaddr *f, int ch_opts) { struct addrinfo hints, *res, *r; register FILE *hostf; - int first = 1; - int good = 0; char hostbuf[NI_MAXHOST], ip[NI_MAXHOST]; char serv[NI_MAXSERV]; - int error, addrlen; - caddr_t addr; + int error, errsav, fpass, good, wantsl; - error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv), - NI_NUMERICSERV); - if (error || atoi(serv) >= IPPORT_RESERVED) - fatal(0, "Malformed from address"); + wantsl = 0; + if (ch_opts & LPD_LOGCONNERR) + wantsl = 1; /* also syslog the errors */ + from_host = ".na."; + /* Need real hostname for temporary filenames */ error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, NI_NAMEREQD); if (error) { + errsav = error; error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID); if (error) - fatal(0, "Host name for your address unknown"); + fhosterr(wantsl, + "can not determine hostname for remote host (%d)", + "Host name for your address not known", error); else - fatal(0, "Host name for your address (%s) unknown", - hostbuf); + fhosterr(wantsl, + "Host name for remote host (%s) not known (%d)", + "Host name for your address (%s) not known", + hostbuf, errsav); } strlcpy(frombuf, hostbuf, sizeof(frombuf)); @@ -639,7 +660,8 @@ error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID); if (error) - fatal(0, "Cannot print address"); + fhosterr(wantsl, "Cannot print IP address (error %d)", + "Cannot print IP address", error); from_ip = strdup(hostbuf); /* Reject numeric addresses */ @@ -649,7 +671,8 @@ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; if (getaddrinfo(from_host, NULL, &hints, &res) == 0) { freeaddrinfo(res); - fatal(0, "reverse lookup results in non-FQDN %s", from_host); + fhosterr(wantsl, NULL, "reverse lookup results in non-FQDN %s", + from_host); } /* Check for spoof, ala rlogind */ @@ -658,38 +681,120 @@ hints.ai_socktype = SOCK_DGRAM; /*dummy*/ error = getaddrinfo(from_host, NULL, &hints, &res); if (error) { - fatal(0, "hostname for your address (%s) unknown: %s", from_ip, - gai_strerror(error)); + fhosterr(wantsl, "dns lookup for address %s failed: %s", + "hostname for your address (%s) unknown: %s", from_ip, + gai_strerror(error)); } good = 0; for (r = res; good == 0 && r; r = r->ai_next) { error = getnameinfo(r->ai_addr, r->ai_addrlen, ip, sizeof(ip), - NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID); + NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID); if (!error && !strcmp(from_ip, ip)) good = 1; } if (res) freeaddrinfo(res); if (good == 0) - fatal(0, "address for your hostname (%s) not matched", - from_ip); + fhosterr(wantsl, "address for remote host (%s) not matched", + "address for your hostname (%s) not matched", from_ip); + fpass = 1; hostf = fopen(_PATH_HOSTSEQUIV, "r"); again: if (hostf) { if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) { (void) fclose(hostf); - return; + goto foundhost; } (void) fclose(hostf); } - if (first == 1) { - first = 0; + if (fpass == 1) { + fpass = 2; hostf = fopen(_PATH_HOSTSLPD, "r"); goto again; } - fatal(0, "Your host does not have line printer access"); + fhosterr(wantsl, "refused connection from %s", + "Your host (%s) does not have print-service access", from_host); /*NOTREACHED*/ + +foundhost: + if (ch_opts & LPD_NOPORTCHK) + return; /* skip the reserved-port check */ + + error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv), + NI_NUMERICSERV); + if (error) + fhosterr(wantsl, NULL, "malformed from-address (%d)", error); + + if (atoi(serv) >= IPPORT_RESERVED) + fhosterr(wantsl, NULL, "connected from invalid port (%s)", + serv); +} + +#include +/* + * Handle fatal errors in chkhost. The first message will optionally be sent + * to syslog, the second one is sent to the connecting host. If the first + * message is NULL, then the same message is used for both. Note that the + * argument list for both messages are assumed to be the same (or at least + * the initial arguments for one must be EXACTLY the same as the complete + * argument list for the other message). + * + * The idea is that the syslog message is meant for an administrator of a + * print server (the host receiving connections), while the usermsg is meant + * for a remote user who may or may not be clueful, and may or may not be + * doing something nefarious. Some remote users (eg, MS-Windows...) may not + * even see whatever message is sent, which is why there's the option to + * start 'lpd' with the connection-errors also sent to syslog. + * + * Given that hostnames can theoretically be fairly long (well, over 250 + * bytes), it would probably be helpful to have the 'from_host' field at + * the end of any error messages which include that info. + */ +void +fhosterr(int dosys, const char *sysmsg, const char *usermsg, ...) +{ + va_list ap; + char *sbuf, *ubuf; + const char *testone; + + va_start(ap, usermsg); + vasprintf(&ubuf, usermsg, ap); + va_end(ap); + + if (dosys) { + sbuf = ubuf; /* assume sysmsg == NULL */ + if (sysmsg != NULL) { + va_start(ap, usermsg); + vasprintf(&sbuf, sysmsg, ap); + va_end(ap); + } + /* + * If the first variable-parameter is not the 'from_host', + * then first write THAT information as a line to syslog. + */ + va_start(ap, usermsg); + testone = va_arg(ap, const char *); + if (testone != from_host) { + syslog(LOG_WARNING, "for connection from %s:", from_host); + } + va_end(ap); + + /* now write the syslog message */ + syslog(LOG_WARNING, "%s", sbuf); + } + + printf("%s [@%s]: %s\n", progname, local_host, ubuf); + fflush(stdout); + + /* + * Add a minimal delay before exiting (and disconnecting from the + * sending-host). This is just in case that machine responds by + * INSTANTLY retrying (and instantly re-failing...). This may also + * give the other side more time to read the error message. + */ + sleep(2); /* a paranoid throttling measure */ + exit(1); } /* setup server socket for specified address family */ @@ -777,9 +882,9 @@ usage(void) { #ifdef INET6 - fprintf(stderr, "usage: lpd [-dlp46] [port#]\n"); + fprintf(stderr, "usage: lpd [-cdlpw46] [port#]\n"); #else - fprintf(stderr, "usage: lpd [-dlp] [port#]\n"); + fprintf(stderr, "usage: lpd [-cdlpw] [port#]\n"); #endif exit(EX_USAGE); } -- Garance Alistair Drosehn = gad@eclipse.acs.rpi.edu Senior Systems Programmer or gad@freebsd.org Rensselaer Polytechnic Institute or drosih@rpi.edu To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-audit" in the body of the message