bin/100921: libexec/tftpd: `-w' non-traditional access control

Auster lrou at presto.telepluscom.net
Thu Jul 27 16:01:21 UTC 2006


>Number:         100921
>Category:       bin
>Synopsis:       libexec/tftpd: `-w' non-traditional access control
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Jul 27 16:00:36 GMT 2006
>Closed-Date:
>Last-Modified:
>Originator:     Auster
>Release:        FreeBSD 6.1-RELEASE-p2 i386
>Organization:
>Environment:
System: FreeBSD presto.telepluscom.net 6.1-RELEASE-p2 FreeBSD 6.1-RELEASE-p2 #0: Thu Jun 15 20:30:57 CEST 2006 yx at presto.telepluscom.net:/usr/obj/usr/src/sys/presto i386


>Description:

Partially non-traditional(2) tftp write access control:
  tftpd(8):
  (1) files may be written only if they already exist and are publicly writable.

  (2)	-w	Allow writes requests to create new files.  By default tftpd
		requires that the file specified in a write request exist.


Condition: `-w' allow writes requests to create new files.

Summary, creation new files:
absolute filenames - incorrect
relative filenames - correct


>How-To-Repeat:

~# grep '^tftp' /etc/inetd.conf
tftp	dgram	udp	wait	root	/usr/libexec/tftpd	tftpd -l -w -s /spool/tftp

~# touch a
~# mkdir /spool/tftp/1
~# chown nobody:nogroup /spool/tftp/1
~# chmod 755 /spool/tftp/1
~# rm -f /spool/tftp/1/a
~# tftp localhost
tftp| put a /1/a
Error code 1: File not found
  ! error - incorrect (for `tftpd -w')
tftp| put a 1/a
  ! no error - correct (for `tftpd -w')


>Fix:

for example: variants (1), (2), and (3).


variant (1):
  tftpd(8) manual correction only:

	-w	Allow writes requests to create new files.  By default tftpd
		requires that the file specified in a write request exist.
+		File creation are allowed for relative file names only.




variant (2):
  small code correction (create request w/ absolute file names):

diff -up libexec/tftpd/tftpd.c.orig libexec/tftpd/tftpd.c
--- libexec/tftpd/tftpd.c.orig	Thu Jul 27 12:02:59 2006
+++ libexec/tftpd/tftpd.c	Thu Jul 27 16:08:33 2006
@@ -527,7 +527,7 @@ int
 validate_access(char **filep, int mode)
 {
 	struct stat stbuf;
-	int	fd;
+	int	fd, crq;
 	struct dirlist *dirp;
 	static char pathname[MAXPATHLEN];
 	char *filename = *filep;
@@ -554,16 +554,26 @@ validate_access(char **filep, int mode)
 		/* If directory list is empty, allow access to any file */
 		if (dirp->name == NULL && dirp != dirs)
 			return (EACCESS);
-		if (stat(filename, &stbuf) < 0)
-			return (errno == ENOENT ? ENOTFOUND : EACCESS);
-		if ((stbuf.st_mode & S_IFMT) != S_IFREG)
-			return (ENOTFOUND);
+
+		crq = 0;
+		if (stat(filename, &stbuf) < 0) {
+			if (create_new && (mode == WRQ) && (errno == ENOENT))
+				crq = 1;
+			if (!crq)
+				return (errno == ENOENT ? ENOTFOUND : EACCESS);
+		}
+		if (!crq) {
+			if ((stbuf.st_mode & S_IFMT) != S_IFREG)
+				return (ENOTFOUND);
+		}
 		if (mode == RRQ) {
 			if ((stbuf.st_mode & S_IROTH) == 0)
 				return (EACCESS);
 		} else {
-			if ((stbuf.st_mode & S_IWOTH) == 0)
-				return (EACCESS);
+			if (!crq) {
+				if ((stbuf.st_mode & S_IWOTH) == 0)
+					return (EACCESS);
+			}
 		}
 	} else {
 		int err;










variant (3):
  non-traditional read/write access control:

tftpd(8):
-	-w	Allow writes requests to create new files.  By default tftpd
-		requires that the file specified in a write request exist.
+	-w	Offers a non-traditional (for tftp) access control, which
+		will allows to read, write, and create files, with credentials
+		to tftpd ``user'' (default ``nobody'') only.

diff -up libexec/tftpd/tftpd.c.orig libexec/tftpd/tftpd.c
--- libexec/tftpd/tftpd.c.orig	Thu Jul 27 12:02:59 2006
+++ libexec/tftpd/tftpd.c	Thu Jul 27 16:53:54 2006
@@ -109,7 +109,7 @@ static struct dirlist {
 static int	suppress_naks;
 static int	logging;
 static int	ipchroot;
-static int	create_new = 0;
+static int	traditional_mode = 1;
 static mode_t	mask = S_IWGRP|S_IWOTH;
 
 static const char *errtomsg(int);
@@ -158,7 +158,7 @@ main(int argc, char *argv[])
 			mask = strtol(optarg, NULL, 0);
 			break;
 		case 'w':
-			create_new = 1;
+			traditional_mode = 0;
 			break;
 		default:
 			syslog(LOG_WARNING, "ignoring unknown option -%c", ch);
@@ -527,7 +527,7 @@ int
 validate_access(char **filep, int mode)
 {
 	struct stat stbuf;
-	int	fd;
+	int	fd, crq;
 	struct dirlist *dirp;
 	static char pathname[MAXPATHLEN];
 	char *filename = *filep;
@@ -554,16 +554,26 @@ validate_access(char **filep, int mode)
 		/* If directory list is empty, allow access to any file */
 		if (dirp->name == NULL && dirp != dirs)
 			return (EACCESS);
-		if (stat(filename, &stbuf) < 0)
-			return (errno == ENOENT ? ENOTFOUND : EACCESS);
-		if ((stbuf.st_mode & S_IFMT) != S_IFREG)
-			return (ENOTFOUND);
-		if (mode == RRQ) {
-			if ((stbuf.st_mode & S_IROTH) == 0)
-				return (EACCESS);
-		} else {
-			if ((stbuf.st_mode & S_IWOTH) == 0)
-				return (EACCESS);
+
+		crq = 0;
+		if (stat(filename, &stbuf) < 0) {
+			if (!traditional_mode && (mode == WRQ) && (errno == ENOENT))
+				crq = 1;
+			if (!crq)
+				return (errno == ENOENT ? ENOTFOUND : EACCESS);
+		}
+		if (!crq) {
+			if ((stbuf.st_mode & S_IFMT) != S_IFREG)
+				return (ENOTFOUND);
+		}
+		if (traditional_mode) {
+			if (mode == RRQ) {
+				if ((stbuf.st_mode & S_IROTH) == 0)
+					return (EACCESS);
+			} else {
+				if ((stbuf.st_mode & S_IWOTH) == 0)
+					return (EACCESS);
+			}
 		}
 	} else {
 		int err;
@@ -588,16 +598,30 @@ validate_access(char **filep, int mode)
 				dirp->name, filename);
 			if (stat(pathname, &stbuf) == 0 &&
 			    (stbuf.st_mode & S_IFMT) == S_IFREG) {
-				if ((stbuf.st_mode & S_IROTH) != 0) {
+				if (!traditional_mode)
 					break;
+				if (mode == RRQ) {
+					if ((stbuf.st_mode & S_IROTH) != 0) {
+						break;
+					}
+				} else {
+					if ((stbuf.st_mode & S_IWOTH) != 0) {
+						break;
+					}
 				}
 				err = EACCESS;
 			}
 		}
 		if (dirp->name != NULL)
 			*filep = filename = pathname;
-		else if (mode == RRQ)
-			return (err);
+		else {
+			if (mode == RRQ) {
+				return (err);
+			} else {
+				if (traditional_mode)
+					return (err);
+			}
+		}
 	}
 	if (options[OPT_TSIZE].o_request) {
 		if (mode == RRQ) 
@@ -610,10 +634,10 @@ validate_access(char **filep, int mode)
 	if (mode == RRQ)
 		fd = open(filename, O_RDONLY);
 	else {
-		if (create_new)
-			fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
-		else
+		if (traditional_mode)
 			fd = open(filename, O_WRONLY|O_TRUNC);
+		else
+			fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
 	}
 	if (fd < 0)
 		return (errno + 100);

>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list