summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgad <gad@FreeBSD.org>2001-06-25 01:45:25 +0000
committergad <gad@FreeBSD.org>2001-06-25 01:45:25 +0000
commite066fac19f8dd58b76967b86465ce5d78339107b (patch)
tree6d8ba5049bb3635f96110917d79874a0cd5a4fad
parent22109f70bf012e62a4502107cc02983cb702377f (diff)
downloadFreeBSD-src-e066fac19f8dd58b76967b86465ce5d78339107b.zip
FreeBSD-src-e066fac19f8dd58b76967b86465ce5d78339107b.tar.gz
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
-rw-r--r--usr.sbin/lpr/lpd/lpd.834
-rw-r--r--usr.sbin/lpr/lpd/lpd.c166
2 files changed, 168 insertions, 32 deletions
diff --git a/usr.sbin/lpr/lpd/lpd.8 b/usr.sbin/lpr/lpd/lpd.8
index aeb569e..ace150b 100644
--- a/usr.sbin/lpr/lpd/lpd.8
+++ b/usr.sbin/lpr/lpd/lpd.8
@@ -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
diff --git a/usr.sbin/lpr/lpd/lpd.c b/usr.sbin/lpr/lpd/lpd.c
index 9c1fdaa..16e8796 100644
--- a/usr.sbin/lpr/lpd/lpd.c
+++ b/usr.sbin/lpr/lpd/lpd.c
@@ -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);
}
OpenPOWER on IntegriCloud