* implement -u user and -g group - to specify the username and/or

group to run as.
  if -u isn't given, user defaults to "nobody".
  if -g isn't given, group defaults to the primary group of user.
  doesn't try and change uid or gid if they're already set ok.
  should solve PR 4218.
* deprecate register
* deprecate trailing \n in syslog messages
This commit is contained in:
lukem 1998-07-29 11:31:22 +00:00
parent 19e9da1bad
commit 15b3c2bfae
2 changed files with 143 additions and 46 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: tftpd.8,v 1.7 1998/06/08 12:41:43 lukem Exp $ .\" $NetBSD: tftpd.8,v 1.8 1998/07/29 11:31:22 lukem Exp $
.\" .\"
.\" Copyright (c) 1983, 1991, 1993 .\" Copyright (c) 1983, 1991, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -33,7 +33,7 @@
.\" .\"
.\" from: @(#)tftpd.8 8.1 (Berkeley) 6/4/93 .\" from: @(#)tftpd.8 8.1 (Berkeley) 6/4/93
.\" .\"
.Dd June 4, 1993 .Dd July 29, 1998
.Dt TFTPD 8 .Dt TFTPD 8
.Os BSD 4.2 .Os BSD 4.2
.Sh NAME .Sh NAME
@ -43,9 +43,11 @@
Internet Trivial File Transfer Protocol server Internet Trivial File Transfer Protocol server
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl s Ar directory .Op Fl g Ar group
.Op Fl l .Op Fl l
.Op Fl n .Op Fl n
.Op Fl s Ar directory
.Op Fl u Ar user
.Op Ar directory ... .Op Ar directory ...
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
@ -92,17 +94,27 @@ The given directories are also treated as a search path for
relative filename requests. relative filename requests.
.Pp .Pp
The options are: The options are:
.Bl -tag -width Ds .Bl -tag -width "directory"
.It Fl g Ar group
Change gid to that of
.Ar group
on startup.
If this isn't specified, the gid is set to that of the
.Ar user
specified with
.Fl u .
.It Fl l .It Fl l
Logs all requests using Logs all requests using
.Xr syslog 3 . .Xr syslog 3 .
.It Fl n .It Fl n
Suppresses negative acknowledgement of requests for nonexistent Suppresses negative acknowledgement of requests for nonexistent
relative filenames. relative filenames.
.It Fl s .It Fl s Ar directory
.Nm .Nm
will will
.Xr chroot 2 .Xr chroot 2
to
.Ar directory
on startup. on startup.
This is recommended for security reasons (so that files other than This is recommended for security reasons (so that files other than
those in the those in the
@ -116,6 +128,21 @@ to
.Sq \&. .Sq \&.
under under
.Pa /tftpboot . .Pa /tftpboot .
.It Fl u Ar user
Change uid to that of
.Ar user
on startup.
If
.Fl u
isn't given,
.Ar user
defaults to
.Dq nobody .
If
.Fl g
isn't also given, change the gid to that of
.Ar user
as well.
.El .El
.Pp .Pp
.Sh SEE ALSO .Sh SEE ALSO
@ -131,6 +158,13 @@ The
.Fl s .Fl s
flag appeared in flag appeared in
.Nx 1.0 . .Nx 1.0 .
.Pp
The
.Fl g
and
.Fl u
flags appeared in
.Nx 1.4 .
.Sh SECURITY CONSIDERATIONS .Sh SECURITY CONSIDERATIONS
You are You are
.Em strongly .Em strongly

View File

@ -1,4 +1,4 @@
/* $NetBSD: tftpd.c,v 1.13 1998/07/26 15:02:27 mycroft Exp $ */ /* $NetBSD: tftpd.c,v 1.14 1998/07/29 11:31:22 lukem Exp $ */
/* /*
* Copyright (c) 1983, 1993 * Copyright (c) 1983, 1993
@ -40,7 +40,7 @@ __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
#if 0 #if 0
static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93";
#else #else
__RCSID("$NetBSD: tftpd.c,v 1.13 1998/07/26 15:02:27 mycroft Exp $"); __RCSID("$NetBSD: tftpd.c,v 1.14 1998/07/29 11:31:22 lukem Exp $");
#endif #endif
#endif /* not lint */ #endif /* not lint */
@ -66,7 +66,9 @@ __RCSID("$NetBSD: tftpd.c,v 1.13 1998/07/26 15:02:27 mycroft Exp $");
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <grp.h>
#include <netdb.h> #include <netdb.h>
#include <pwd.h>
#include <setjmp.h> #include <setjmp.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
@ -77,9 +79,7 @@ __RCSID("$NetBSD: tftpd.c,v 1.13 1998/07/26 15:02:27 mycroft Exp $");
#include "tftpsubs.h" #include "tftpsubs.h"
/* XXX svr4 defines UID_NOBODY and GID_NOBODY constants in <sys/param.h> */ #define DEFAULTUSER "nobody"
#define UID_NOBODY 32767
#define GID_NOBODY 32766
#define TIMEOUT 5 #define TIMEOUT 5
@ -140,7 +140,9 @@ struct formats {
static void static void
usage() usage()
{ {
syslog(LOG_ERR, "Usage: %s [-s] [directory ...]\n", __progname); syslog(LOG_ERR,
"Usage: %s [-ln] [-u user] [-g group] [-s directory] [directory ...]",
__progname);
exit(1); exit(1);
} }
@ -149,16 +151,31 @@ main(argc, argv)
int argc; int argc;
char **argv; char **argv;
{ {
register struct tftphdr *tp; struct passwd *pwent;
register int n = 0; struct group *grent;
struct tftphdr *tp;
int n = 0;
int ch, on; int ch, on;
int fd = 0; int fd = 0;
struct sockaddr_in sin; struct sockaddr_in sin;
char *tgtuser, *tgtgroup, *ep;
uid_t curuid, tgtuid;
gid_t curgid, tgtgid;
long nid;
openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON); openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
tgtuser = DEFAULTUSER;
tgtgroup = NULL;
curuid = getuid();
curgid = getgid();
while ((ch = getopt(argc, argv, "lns:")) != -1) while ((ch = getopt(argc, argv, "g:lns:u:")) != -1)
switch (ch) { switch (ch) {
case 'g':
tgtgroup = optarg;
break;
case 'l': case 'l':
logging = 1; logging = 1;
break; break;
@ -172,6 +189,10 @@ main(argc, argv)
securedir = optarg; securedir = optarg;
break; break;
case 'u':
tgtuser = optarg;
break;
default: default:
usage(); usage();
break; break;
@ -191,42 +212,84 @@ main(argc, argv)
} }
} }
if (*tgtuser == '\0' || (tgtgroup != NULL && *tgtgroup == '\0'))
usage();
nid = (strtol(tgtuser, &ep, 10));
if (*ep == '\0') {
if (nid > UID_MAX) {
syslog(LOG_ERR, "uid %ld is too large", nid);
exit(1);
}
pwent = getpwuid((uid_t)nid);
} else
pwent = getpwnam(tgtuser);
if (pwent == NULL) {
syslog(LOG_ERR, "unknown user `%s'", tgtuser);
exit(1);
}
tgtuid = pwent->pw_uid;
tgtgid = pwent->pw_gid;
if (tgtgroup != NULL) {
nid = (strtol(tgtgroup, &ep, 10));
if (*ep == '\0') {
if (nid > GID_MAX) {
syslog(LOG_ERR, "gid %ld is too large", nid);
exit(1);
}
grent = getgrgid((gid_t)nid);
} else
grent = getgrnam(tgtgroup);
if (grent != NULL)
tgtgid = grent->gr_gid;
else {
syslog(LOG_ERR, "unknown group `%s'", tgtgroup);
exit(1);
}
}
if (secure) { if (secure) {
if (chdir(securedir) < 0) { if (chdir(securedir) < 0) {
syslog(LOG_ERR, "chdir %s: %m", securedir); syslog(LOG_ERR, "chdir %s: %m", securedir);
exit(1); exit(1);
} }
if (chroot(".")) { if (chroot(".")) {
syslog(LOG_ERR, "chroot: %m\n"); syslog(LOG_ERR, "chroot: %m");
exit(1); exit(1);
} }
} }
if (setgid(GID_NOBODY)) { syslog(LOG_DEBUG, "running as user `%s' (%d), group `%s' (%d)",
syslog(LOG_ERR, "setgid: %m"); tgtuser, tgtuid, tgtgroup ? tgtgroup : "(unspecified)" , tgtgid);
exit(1); if (curgid != tgtgid) {
if (setgid(tgtgid)) {
syslog(LOG_ERR, "setgid to %d: %m", (int)tgtgid);
exit(1);
}
if (setgroups(0, NULL)) {
syslog(LOG_ERR, "setgroups: %m");
exit(1);
}
} }
if (setgroups(0, NULL)) { if (curuid != tgtuid) {
syslog(LOG_ERR, "setgroups: %m"); if (setuid(tgtuid)) {
exit(1); syslog(LOG_ERR, "setuid to %d: %m", (int)tgtuid);
} exit(1);
}
if (setuid(UID_NOBODY)) {
syslog(LOG_ERR, "setuid: %m");
exit(1);
} }
on = 1; on = 1;
if (ioctl(fd, FIONBIO, &on) < 0) { if (ioctl(fd, FIONBIO, &on) < 0) {
syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); syslog(LOG_ERR, "ioctl(FIONBIO): %m");
exit(1); exit(1);
} }
fromlen = sizeof (from); fromlen = sizeof (from);
n = recvfrom(fd, buf, sizeof (buf), 0, n = recvfrom(fd, buf, sizeof (buf), 0,
(struct sockaddr *)&from, &fromlen); (struct sockaddr *)&from, &fromlen);
if (n < 0) { if (n < 0) {
syslog(LOG_ERR, "recvfrom: %m\n"); syslog(LOG_ERR, "recvfrom: %m");
exit(1); exit(1);
} }
/* /*
@ -273,7 +336,7 @@ main(argc, argv)
} }
} }
if (pid < 0) { if (pid < 0) {
syslog(LOG_ERR, "fork: %m\n"); syslog(LOG_ERR, "fork: %m");
exit(1); exit(1);
} else if (pid != 0) { } else if (pid != 0) {
exit(0); exit(0);
@ -286,17 +349,17 @@ main(argc, argv)
close(1); close(1);
peer = socket(AF_INET, SOCK_DGRAM, 0); peer = socket(AF_INET, SOCK_DGRAM, 0);
if (peer < 0) { if (peer < 0) {
syslog(LOG_ERR, "socket: %m\n"); syslog(LOG_ERR, "socket: %m");
exit(1); exit(1);
} }
memset(&sin, 0, sizeof(sin)); memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
syslog(LOG_ERR, "bind: %m\n"); syslog(LOG_ERR, "bind: %m");
exit(1); exit(1);
} }
if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
syslog(LOG_ERR, "connect: %m\n"); syslog(LOG_ERR, "connect: %m");
exit(1); exit(1);
} }
tp = (struct tftphdr *)buf; tp = (struct tftphdr *)buf;
@ -314,9 +377,9 @@ tftp(tp, size)
struct tftphdr *tp; struct tftphdr *tp;
int size; int size;
{ {
register char *cp; char *cp;
int first = 1, ecode; int first = 1, ecode;
register struct formats *pf; struct formats *pf;
char *filename, *mode = NULL; /* XXX gcc */ char *filename, *mode = NULL; /* XXX gcc */
filename = cp = tp->th_stuff; filename = cp = tp->th_stuff;
@ -494,8 +557,8 @@ sendfile(pf)
struct formats *pf; struct formats *pf;
{ {
struct tftphdr *dp; struct tftphdr *dp;
register struct tftphdr *ap; /* ack packet */ struct tftphdr *ap; /* ack packet */
register int size, n; int size, n;
volatile int block; volatile int block;
signal(SIGALRM, timer); signal(SIGALRM, timer);
@ -515,7 +578,7 @@ sendfile(pf)
send_data: send_data:
if (send(peer, dp, size + 4, 0) != size + 4) { if (send(peer, dp, size + 4, 0) != size + 4) {
syslog(LOG_ERR, "tftpd: write: %m\n"); syslog(LOG_ERR, "tftpd: write: %m");
goto abort; goto abort;
} }
read_ahead(file, pf->f_convert); read_ahead(file, pf->f_convert);
@ -524,7 +587,7 @@ send_data:
n = recv(peer, ackbuf, sizeof (ackbuf), 0); n = recv(peer, ackbuf, sizeof (ackbuf), 0);
alarm(0); alarm(0);
if (n < 0) { if (n < 0) {
syslog(LOG_ERR, "tftpd: read: %m\n"); syslog(LOG_ERR, "tftpd: read: %m");
goto abort; goto abort;
} }
ap->th_opcode = ntohs((u_short)ap->th_opcode); ap->th_opcode = ntohs((u_short)ap->th_opcode);
@ -564,8 +627,8 @@ recvfile(pf)
struct formats *pf; struct formats *pf;
{ {
struct tftphdr *dp; struct tftphdr *dp;
register struct tftphdr *ap; /* ack buffer */ struct tftphdr *ap; /* ack buffer */
register int n, size; int n, size;
volatile int block; volatile int block;
signal(SIGALRM, timer); signal(SIGALRM, timer);
@ -580,7 +643,7 @@ recvfile(pf)
(void) setjmp(timeoutbuf); (void) setjmp(timeoutbuf);
send_ack: send_ack:
if (send(peer, ackbuf, 4, 0) != 4) { if (send(peer, ackbuf, 4, 0) != 4) {
syslog(LOG_ERR, "tftpd: write: %m\n"); syslog(LOG_ERR, "tftpd: write: %m");
goto abort; goto abort;
} }
write_behind(file, pf->f_convert); write_behind(file, pf->f_convert);
@ -589,7 +652,7 @@ send_ack:
n = recv(peer, dp, PKTSIZE, 0); n = recv(peer, dp, PKTSIZE, 0);
alarm(0); alarm(0);
if (n < 0) { /* really? */ if (n < 0) { /* really? */
syslog(LOG_ERR, "tftpd: read: %m\n"); syslog(LOG_ERR, "tftpd: read: %m");
goto abort; goto abort;
} }
dp->th_opcode = ntohs((u_short)dp->th_opcode); dp->th_opcode = ntohs((u_short)dp->th_opcode);
@ -654,7 +717,7 @@ errtomsg(error)
int error; int error;
{ {
static char buf[20]; static char buf[20];
register const struct errmsg *pe; const struct errmsg *pe;
if (error == 0) if (error == 0)
return "success"; return "success";
@ -675,9 +738,9 @@ static void
nak(error) nak(error)
int error; int error;
{ {
register struct tftphdr *tp; struct tftphdr *tp;
int length; int length;
register const struct errmsg *pe; const struct errmsg *pe;
tp = (struct tftphdr *)buf; tp = (struct tftphdr *)buf;
tp->th_opcode = htons((u_short)ERROR); tp->th_opcode = htons((u_short)ERROR);
@ -695,7 +758,7 @@ nak(error)
tp->th_msg[length] = '\0'; tp->th_msg[length] = '\0';
length += 5; length += 5;
if (send(peer, buf, length, 0) != length) if (send(peer, buf, length, 0) != length)
syslog(LOG_ERR, "nak: %m\n"); syslog(LOG_ERR, "nak: %m");
} }
static char * static char *