Add two new options for lpd: -c will log all connection-errors to syslog,

while -w allows connection from non-reserved ports.  Also improves the
helpfulness of various connection-error messages.

The changes for IPv6 added back in the reserved-port check which was mistakenly
dropped from lpd in 1997 (copying a change from openbsd).  It is best to have
that check in place, but the check breaks lpr's from some implementations of
lpr/lpd for Windows.  The -w option is for those admins who need to accept
jobs from non-reserved ports, the -c option is for admins who would like a
print-server machine to log all failed connection-attempts to syslog.

Reviewed by:	freebsd-audit@FreeBSD.org  freebsd-print@bostonradio.org
MFC after:	2 weeks
This commit is contained in:
Garance A Drosehn 2001-06-25 01:45:25 +00:00
parent 2b47b55f69
commit 6ddb63cad6
2 changed files with 168 additions and 32 deletions

View File

@ -32,7 +32,7 @@
.\" @(#)lpd.8 8.3 (Berkeley) 4/19/94
.\" $FreeBSD$
.\"
.Dd April 19, 1994
.Dd June 06, 2001
.Dt LPD 8
.Os BSD 4.2
.Sh NAME
@ -40,7 +40,7 @@
.Nd line printer spooler daemon
.Sh SYNOPSIS
.Nm
.Op Fl dlp46
.Op Fl cdlpw46
.Op Ar port#
.Sh DESCRIPTION
.Nm Lpd
@ -62,6 +62,19 @@ the request so the parent can continue to listen for more requests.
.Pp
Available options:
.Bl -tag -width Ds
.It Fl c
By default, if some remote host has a connection error while trying to
send a print request to
.Nm
on a local host,
.Nm
will only send error message to that remote host.
The
.Fl c
flag causes
.Nm
to also log all of those connection errors via
.Xr syslog 3 .
.It Fl d
Turn on
.Dv SO_DEBUG
@ -81,6 +94,23 @@ The
flag causes
.Nm
not to open an Internet listening socket.
This means that
.Nm
will not accept any connections from any remote
hosts, although it will still accept print requests
from all local users.
.It Fl w
By default, the
.Nm
daemon will only accept connections which originate
from a reserved-port (<1024) on the remote host.
The
.Fl w
flag causes
.Nm
to accept connections coming from any port.
This is can be useful when you want to accept print jobs
from certain implementations of lpr written for Windows.
.It Fl 4
Inet only.
.It Fl 6

View File

@ -112,8 +112,10 @@ static void reapchild(int _signo);
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 @@ extern int __ivaliduser_sa __P((FILE *, struct sockaddr *, socklen_t,
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 @@ main(int argc, char **argv)
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 @@ main(int argc, char **argv)
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 @@ main(int argc, char **argv)
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 @@ main(int argc, char **argv)
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 @@ ckqueue(struct printer *pp)
#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 @@ chkhost(struct sockaddr *f)
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 @@ chkhost(struct sockaddr *f)
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,121 @@ chkhost(struct sockaddr *f)
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, sip=%s",
"Print-services are not available to your host (%s).", from_host,
from_ip);
/*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 <stdarg.h>
/*
* 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 +883,9 @@ static void
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);
}