summaryrefslogtreecommitdiff
path: root/usr.sbin/tftpd
diff options
context:
space:
mode:
authorJeremie Courreges-Anglas <jca@cvs.openbsd.org>2017-11-07 14:15:39 +0000
committerJeremie Courreges-Anglas <jca@cvs.openbsd.org>2017-11-07 14:15:39 +0000
commit41650afcf69a59f547cbedc53fc02a84ae5ef1ee (patch)
tree10374f9490ec9518fdc0603e02eacc2a6937e419 /usr.sbin/tftpd
parent511399762efb24545117d332c506fa55cc60269a (diff)
Add support for client-specific directories (named after the client address)
tftpd -i will look up the requested path the directory named after the client's IP address. For read requests, if the file is not found, there's a fall back to its root directory. From Jan Klemkow with input and tweaks from at least jmc@, bluhm@, deraadt@, sthen@, semarie@ and myself. ok bluhm@
Diffstat (limited to 'usr.sbin/tftpd')
-rw-r--r--usr.sbin/tftpd/tftpd.817
-rw-r--r--usr.sbin/tftpd/tftpd.c49
2 files changed, 55 insertions, 11 deletions
diff --git a/usr.sbin/tftpd/tftpd.8 b/usr.sbin/tftpd/tftpd.8
index 2e8f89cb5cf..561d112ee94 100644
--- a/usr.sbin/tftpd/tftpd.8
+++ b/usr.sbin/tftpd/tftpd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tftpd.8,v 1.5 2015/07/18 05:32:56 mcbride Exp $
+.\" $OpenBSD: tftpd.8,v 1.6 2017/11/07 14:15:38 jca Exp $
.\"
.\" Copyright (c) 1983, 1991 The Regents of the University of California.
.\" All rights reserved.
@@ -29,7 +29,7 @@
.\"
.\" from: @(#)tftpd.8 6.7 (Berkeley) 5/13/91
.\"
-.Dd $Mdocdate: July 18 2015 $
+.Dd $Mdocdate: November 7 2017 $
.Dt TFTPD 8
.Os
.Sh NAME
@@ -37,7 +37,7 @@
.Nd DARPA Trivial File Transfer Protocol daemon
.Sh SYNOPSIS
.Nm tftpd
-.Op Fl 46cdv
+.Op Fl 46cdiv
.Op Fl l Ar address
.Op Fl p Ar port
.Op Fl r Ar socket
@@ -100,6 +100,15 @@ If this option is specified,
.Nm
will run in the foreground and log
the client IP, type of request, and filename to stderr.
+.It Fl i
+Look up the requested path in the subdirectory named after the
+client's IP address.
+For read requests, if the file is not found,
+.Nm
+falls back on the requested path.
+Note that no attempt is made to limit the client to its subdirectory.
+This option cannot be combined with
+.Fl r .
.It Fl l Ar address
Listen on the specified address.
By default
@@ -126,6 +135,8 @@ before the TFTP request will continue.
By default
.Nm
does not use filename rewriting.
+This option cannot be combined with
+.Fl i .
.It Fl v
Log the client IP, type of request, and filename.
.It Ar directory
diff --git a/usr.sbin/tftpd/tftpd.c b/usr.sbin/tftpd/tftpd.c
index de12d2ac3b1..8536cf59048 100644
--- a/usr.sbin/tftpd/tftpd.c
+++ b/usr.sbin/tftpd/tftpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tftpd.c,v 1.39 2017/05/26 17:38:46 florian Exp $ */
+/* $OpenBSD: tftpd.c,v 1.40 2017/11/07 14:15:38 jca Exp $ */
/*
* Copyright (c) 2012 David Gwynne <dlg@uq.edu.au>
@@ -282,7 +282,7 @@ __dead void
usage(void)
{
extern char *__progname;
- fprintf(stderr, "usage: %s [-46cdv] [-l address] [-p port] [-r socket]"
+ fprintf(stderr, "usage: %s [-46cdiv] [-l address] [-p port] [-r socket]"
" directory\n", __progname);
exit(1);
}
@@ -290,6 +290,7 @@ usage(void)
int cancreate = 0;
int verbose = 0;
int debug = 0;
+int iflag = 0;
int
main(int argc, char *argv[])
@@ -307,7 +308,7 @@ main(int argc, char *argv[])
int family = AF_UNSPEC;
int devnull = -1;
- while ((c = getopt(argc, argv, "46cdl:p:r:v")) != -1) {
+ while ((c = getopt(argc, argv, "46cdil:p:r:v")) != -1) {
switch (c) {
case '4':
family = AF_INET;
@@ -321,6 +322,11 @@ main(int argc, char *argv[])
case 'd':
verbose = debug = 1;
break;
+ case 'i':
+ if (rewrite != NULL)
+ errx(1, "options -i and -r are incompatible");
+ iflag = 1;
+ break;
case 'l':
addr = optarg;
break;
@@ -328,6 +334,8 @@ main(int argc, char *argv[])
port = optarg;
break;
case 'r':
+ if (iflag == 1)
+ errx(1, "options -i and -r are incompatible");
rewrite = optarg;
break;
case 'v':
@@ -949,15 +957,16 @@ error:
* given as we have no login directory.
*/
int
-validate_access(struct tftp_client *client, const char *filename)
+validate_access(struct tftp_client *client, const char *requested)
{
int mode = client->opcode;
struct opt_client *options = client->options;
struct stat stbuf;
int fd, wmode;
- const char *errstr;
+ const char *errstr, *filename;
+ char rewritten[PATH_MAX];
- if (strcmp(filename, SEEDPATH) == 0) {
+ if (strcmp(requested, SEEDPATH) == 0) {
char *buf;
if (mode != RRQ)
return (EACCESS);
@@ -971,15 +980,39 @@ validate_access(struct tftp_client *client, const char *filename)
return (0);
}
+ if (iflag) {
+ int ret;
+
+ /*
+ * In -i mode, look in the directory named after the
+ * client address.
+ */
+ ret = snprintf(rewritten, sizeof(rewritten), "%s/%s",
+ getip(&client->ss), requested);
+ if (ret == -1 || ret >= sizeof(rewritten))
+ return (ENAMETOOLONG + 100);
+ filename = rewritten;
+ } else {
+retryread:
+ filename = requested;
+ }
+
/*
* We use a different permissions scheme if `cancreate' is
* set.
*/
wmode = O_TRUNC;
if (stat(filename, &stbuf) < 0) {
- if (!cancreate)
+ if (!cancreate) {
+ /*
+ * In -i mode, retry failed read requests from
+ * the root directory.
+ */
+ if (mode == RRQ && errno == ENOENT &&
+ filename == rewritten)
+ goto retryread;
return (errno == ENOENT ? ENOTFOUND : EACCESS);
- else {
+ } else {
if ((errno == ENOENT) && (mode != RRQ))
wmode |= O_CREAT;
else